cpp-httplib icon indicating copy to clipboard operation
cpp-httplib copied to clipboard

Request Lost when using SSL

Open nanjo712 opened this issue 1 year ago • 8 comments

I am trying to build an HTTP server with SSL using httplib, and I have generated an SSL certificate using OpenSSL, something like this: httplib::SSLServer server("server.crt", "server.key");. When I send a large number of requests using Postman for testing, two-thirds of the requests are consistently lost. Strangely, this two-thirds ratio is almost stable, meaning that no matter how many threads I use, each thread will lose two-thirds of its requests. What's even more peculiar is that if I start 10 threads at the same time and only send one request per thread, all requests receive responses.

This phenomenon is not limited to local HTTP requests; remote requests have the same issue, exhibiting the exact same behavior. However, once I disable SSL, all instances of request loss disappear.

server.Get("/username",
               [&](const httplib::Request &req, httplib::Response &res)
               {
                   std::string token = req.get_header_value("Authorization");
                   if (token.find("Bearer ") == 0)
                   {
                       token.erase(0, 7);
                   }
                   else
                   {
                       res.status = 400;
                       res.set_content("Invalid token", "text/plain");
                       return;
                   }
                   auto user = user_manager.get_user(token);
                   if (user)
                   {
                       spdlog::info("Username retrieved: {}", user->get_username());
                       res.status = 200;
                       res.set_content(user->get_username(), "text/plain");
                   }
                   else
                   {
                       spdlog::warn("Invalid token: {}", token);
                       res.status = 400;
                       res.set_content("Invalid token", "text/plain");
                   }
               });

nanjo712 avatar Apr 20 '24 17:04 nanjo712

@nanjo712 could you make a unit test that can reproduce the problem in test/test.cc with httplib:: SSLServer and httplib::SSLClient? Then, I'll take a look at it. Thanks!

yhirose avatar Apr 21 '24 23:04 yhirose

I tried unit testing but was unable to reproduce the bug. When I attempted to test with Python and other languages, I did not encounter the same issue either. It seems that this problem only occurs with Postman.

TEST(SSLTest, SSLTest1) {
  SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
  ASSERT_TRUE(svr.is_valid());
  svr.Get("/hi", [](const Request & /*req*/, Response &res) {
    res.set_content("Hello World!", "text/plain");
  });

  std::thread t([&]() { svr.listen(HOST, 8443); });
  auto se = detail::scope_exit([&] {
    svr.stop();
    t.join();
    ASSERT_FALSE(svr.is_running());
  });

  svr.wait_until_ready();

  auto func = []() {
    SSLClient cli("localhost", 8443);
    cli.enable_server_certificate_verification(false);

    for (int i = 0; i < 100; i++) {
      auto res = cli.Get("/hi");
      ASSERT_TRUE(res);
      EXPECT_EQ(StatusCode::OK_200, res->status);
      EXPECT_EQ("Hello World!", res->body);
    }
  };

  std::vector<std::thread> threads;
  for (int i = 0; i < 10; i++) {
    threads.emplace_back(func);
  }
  for (auto &t : threads) {
    t.join();
  }
}

it will pass the test.

Perhaps this library has some compatibility issues with Postman

nanjo712 avatar Apr 30 '24 07:04 nanjo712

GET https://localhost:8080/username
Error: read ECONNRESET
Request Headers
Content-Type: application/json
Accept: */*
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjoiNzRjNzQ4ZGUtMjlkYy00ZjIxLTkzNzUtMTVmMjc3N2JhMjdiIn0sImV4cCI6MTcxNDQ2NzA4NiwiaWF0IjoxNzE0NDYzNDg2LCJpc3MiOiJ3b3NoaXJlbiIsIm5iZiI6MTcxNDQ2MzQ4Nn0.fykvd3AVKYLE0PqGQ0h5owIXD-OYTkllKqqK9TyDs7c
User-Agent: PostmanRuntime/7.37.3
Host: localhost:8080
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

When Postman get an Error, it shows message like this. This looks like the server closed the TCP connection and the client sent a new request before receiving the disconnection message

nanjo712 avatar Apr 30 '24 08:04 nanjo712

If I remove the "Connection: keep-alive" from the header, this problem will disappear. I think that‘s the problem

nanjo712 avatar Apr 30 '24 08:04 nanjo712

I also encountered a similar problem. Removing header Connection: keep-alive can fix the problem. Does that mean removing this header from the postman side?

pikalin1997 avatar Jun 03 '24 07:06 pikalin1997

I also encountered a similar problem. Removing header Connection: keep-alive can fix the problem. Does that mean removing this header from the postman side?

Yes, just remove the header in Postman. That fix the problem.

nanjo712 avatar Jun 24 '24 11:06 nanjo712

@yhirose I try the test TEST(KeepAliveTest, SSLClientReconnection) on my machine. The program did not pass the test

nanjo712 avatar Jun 24 '24 12:06 nanjo712

[ctest] Test project D:/cpp-httplib/build
[ctest]     Start 219: KeepAliveTest.SSLClientReconnection
[ctest] 1/1 Test #219: KeepAliveTest.SSLClientReconnection ...***Failed    2.12 sec
[ctest] Running main() from D:/cpp-httplib/build/_deps/gtest-src/googletest/src/gtest_main.cc
[ctest] Note: Google Test filter = KeepAliveTest.SSLClientReconnection
[ctest] [==========] Running 1 test from 1 test suite.
[ctest] [----------] Global test environment set-up.
[ctest] [----------] 1 test from KeepAliveTest
[ctest] [ RUN      ] KeepAliveTest.SSLClientReconnection
[ctest] D:/cpp-httplib/test/test.cc(4988): error: Value of: result
[ctest]   Actual: false
[ctest] Expected: true
[ctest] 
[ctest] [  FAILED  ] KeepAliveTest.SSLClientReconnection (2050 ms)
[ctest] [----------] 1 test from KeepAliveTest (2050 ms total)
[ctest] 
[ctest] [----------] Global test environment tear-down
[ctest] [==========] 1 test from 1 test suite ran. (2050 ms total)
[ctest] [  PASSED  ] 0 tests.
[ctest] [  FAILED  ] 1 test, listed below:
[ctest] [  FAILED  ] KeepAliveTest.SSLClientReconnection
[ctest] 
[ctest]  1 FAILED TEST
[ctest] 
[ctest] 
[ctest] 0% tests passed, 1 tests failed out of 1
[ctest] 
[ctest] Total Test time (real) =   2.14 sec
[ctest] 
[ctest] The following tests FAILED:
[ctest] 	219 - KeepAliveTest.SSLClientReconnection (Failed)

That's the log. The code in line 4988 is

  result = cli.Get("/hi");
  ASSERT_TRUE(result); // 4988
  EXPECT_EQ(StatusCode::OK_200, result->status);

  result = cli.Get("/hi");
  ASSERT_TRUE(result);
  EXPECT_EQ(StatusCode::OK_200, result->status);

nanjo712 avatar Jun 24 '24 12:06 nanjo712

@yhirose I try the test TEST(KeepAliveTest, SSLClientReconnection) on my machine. The program did not pass the test

I'll close it since you found the solution.

yhirose avatar Aug 31 '24 23:08 yhirose