Request Lost when using SSL
nanjo712 opened this issue · 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 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!
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
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
If I remove the "Connection: keep-alive" from the header, this problem will disappear. I think that‘s the problem
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?
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.
@yhirose I try the test TEST(KeepAliveTest, SSLClientReconnection) on my machine. The program did not pass the test
[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);