diff --git a/httplib.h b/httplib.h index 946a833..8612ecb 100644 --- a/httplib.h +++ b/httplib.h @@ -2337,6 +2337,17 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { #endif } +inline bool is_socket_alive(socket_t sock) { + const auto val = detail::select_read(sock, 0, 0); + if (val == 0) { + return true; + } else if (val < 0 && errno == EBADF) { + return false; + } + char buf[1]; + return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0; +} + class SocketStream : public Stream { public: SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, @@ -5723,13 +5734,14 @@ inline bool ClientImpl::send(Request &req, Response &res, Error &error) { { std::lock_guard guard(socket_mutex_); + // Set this to false immediately - if it ever gets set to true by the end of // the request, we know another thread instructed us to close the socket. socket_should_be_closed_when_request_is_done_ = false; auto is_alive = false; if (socket_.is_open()) { - is_alive = detail::select_write(socket_.sock, 0, 0) > 0; + is_alive = detail::is_socket_alive(socket_.sock); if (!is_alive) { // Attempt to avoid sigpipe by shutting down nongracefully if it seems // like the other side has already closed the connection Also, there diff --git a/test/test.cc b/test/test.cc index b4d1db7..4d1180e 100644 --- a/test/test.cc +++ b/test/test.cc @@ -3756,6 +3756,36 @@ TEST(KeepAliveTest, ReadTimeout) { ASSERT_FALSE(svr.is_running()); } +TEST(KeepAliveTest, Issue1041) { + const auto resourcePath = "/hi"; + + Server svr; + svr.set_keep_alive_timeout(3); + + svr.Get(resourcePath, [](const httplib::Request &, httplib::Response &res) { + res.set_content("Hello World!", "text/plain"); + }); + + auto a2 = std::async(std::launch::async, [&svr]{ svr.listen(HOST, PORT); }); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + Client cli(HOST, PORT); + cli.set_keep_alive(true); + + auto result = cli.Get(resourcePath); + ASSERT_TRUE(result); + EXPECT_EQ(200, result->status); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + + result = cli.Get(resourcePath); + ASSERT_TRUE(result); + EXPECT_EQ(200, result->status); + + svr.stop(); + a2.wait(); +} + TEST(ClientProblemDetectionTest, ContentProvider) { Server svr;