Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request Lost when using SSL #1825

Closed
nanjo712 opened this issue Apr 20, 2024 · 9 comments
Closed

Request Lost when using SSL #1825

nanjo712 opened this issue Apr 20, 2024 · 9 comments

Comments

@nanjo712
Copy link

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");
                   }
               });
@yhirose
Copy link
Owner

yhirose commented Apr 21, 2024

@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!

@nanjo712
Copy link
Author

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
Copy link
Author

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
Copy link
Author

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

@pikalin1997
Copy link

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?

@nanjo712
Copy link
Author

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
Copy link
Author

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

@nanjo712
Copy link
Author

[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);

@yhirose
Copy link
Owner

yhirose commented Aug 31, 2024

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants