From c5c704cb3b7f0e1ba5dbb5135e369847f6df08ac Mon Sep 17 00:00:00 2001 From: yhirose Date: Mon, 4 Dec 2023 21:27:34 -0500 Subject: [PATCH] Fix #1724 --- httplib.h | 7 +++++++ test/test.cc | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/httplib.h b/httplib.h index b880de7..16940be 100644 --- a/httplib.h +++ b/httplib.h @@ -382,6 +382,7 @@ public: DataSink &operator=(DataSink &&) = delete; std::function write; + std::function is_writable; std::function done; std::function done_with_trailer; std::ostream os; @@ -3959,6 +3960,8 @@ inline bool write_content(Stream &strm, const ContentProvider &content_provider, return ok; }; + data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; + while (offset < end_offset && !is_shutting_down()) { if (!strm.is_writable()) { error = Error::Write; @@ -4003,6 +4006,8 @@ write_content_without_length(Stream &strm, return ok; }; + data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; + data_sink.done = [&](void) { data_available = false; }; while (data_available && !is_shutting_down()) { @@ -4053,6 +4058,8 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider, return ok; }; + data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; + auto done_with_trailer = [&](const Headers *trailer) { if (!ok) { return; } diff --git a/test/test.cc b/test/test.cc index b7e238a..4890a3a 100644 --- a/test/test.cc +++ b/test/test.cc @@ -4464,6 +4464,43 @@ TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) { EXPECT_EQ("helloworld", res->body); } +TEST(LongPollingTest, ClientCloseDetection) { + Server svr; + + svr.Get("/events", [&](const Request & /*req*/, Response &res) { + res.set_chunked_content_provider( + "text/plain", [](std::size_t const, DataSink &sink) -> bool { + EXPECT_TRUE(sink.is_writable()); // the socket is alive + sink.os << "hello"; + + auto count = 10; + while (count > 0 && sink.is_writable()) { + this_thread::sleep_for(chrono::milliseconds(10)); + } + EXPECT_FALSE(sink.is_writable()); // the socket is closed + return true; + }); + }); + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.wait_until_ready(); + + Client cli("localhost", PORT); + + auto res = cli.Get("/events", [&](const char *data, size_t data_length) { + EXPECT_EQ("hello", string(data, data_length)); + return false; // close the socket immediately. + }); + + ASSERT_FALSE(res); +} + TEST(GetWithParametersTest, GetWithParameters) { Server svr;