@@ -1660,6 +1660,7 @@ class SocketStream final : public Stream {
16601660 bool is_readable() const override;
16611661 bool wait_readable() const override;
16621662 bool wait_writable() const override;
1663+ bool is_peer_alive() const override;
16631664 ssize_t read(char *ptr, size_t size) override;
16641665 ssize_t write(const char *ptr, size_t size) override;
16651666 void get_remote_ip_and_port(std::string &ip, int &port) const override;
@@ -3313,10 +3314,10 @@ bool write_content_with_progress(Stream &strm,
33133314 return ok;
33143315 };
33153316
3316- data_sink.is_writable = [&]() -> bool { return strm.wait_writable (); };
3317+ data_sink.is_writable = [&]() -> bool { return strm.is_peer_alive (); };
33173318
33183319 while (offset < end_offset && !is_shutting_down()) {
3319- if (!strm.wait_writable()) {
3320+ if (!strm.wait_writable() || !strm.is_peer_alive() ) {
33203321 error = Error::Write;
33213322 return false;
33223323 } else if (!content_provider(offset, end_offset - offset, data_sink)) {
@@ -3328,6 +3329,11 @@ bool write_content_with_progress(Stream &strm,
33283329 }
33293330 }
33303331
3332+ if (offset < end_offset) { // exited due to is_shutting_down(), not completion
3333+ error = Error::Write;
3334+ return false;
3335+ }
3336+
33313337 error = Error::Success;
33323338 return true;
33333339}
@@ -3367,20 +3373,21 @@ write_content_without_length(Stream &strm,
33673373 return ok;
33683374 };
33693375
3370- data_sink.is_writable = [&]() -> bool { return strm.wait_writable (); };
3376+ data_sink.is_writable = [&]() -> bool { return strm.is_peer_alive (); };
33713377
33723378 data_sink.done = [&](void) { data_available = false; };
33733379
33743380 while (data_available && !is_shutting_down()) {
3375- if (!strm.wait_writable()) {
3381+ if (!strm.wait_writable() || !strm.is_peer_alive() ) {
33763382 return false;
33773383 } else if (!content_provider(offset, 0, data_sink)) {
33783384 return false;
33793385 } else if (!ok) {
33803386 return false;
33813387 }
33823388 }
3383- return true;
3389+ return !data_available; // true only if done() was called, false if shutting
3390+ // down
33843391}
33853392
33863393template <typename T, typename U>
@@ -3416,7 +3423,7 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
34163423 return ok;
34173424 };
34183425
3419- data_sink.is_writable = [&]() -> bool { return strm.wait_writable (); };
3426+ data_sink.is_writable = [&]() -> bool { return strm.is_peer_alive (); };
34203427
34213428 auto done_with_trailer = [&](const Headers *trailer) {
34223429 if (!ok) { return; }
@@ -3466,7 +3473,7 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
34663473 };
34673474
34683475 while (data_available && !is_shutting_down()) {
3469- if (!strm.wait_writable()) {
3476+ if (!strm.wait_writable() || !strm.is_peer_alive() ) {
34703477 error = Error::Write;
34713478 return false;
34723479 } else if (!content_provider(offset, 0, data_sink)) {
@@ -3478,6 +3485,11 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
34783485 }
34793486 }
34803487
3488+ if (data_available) { // exited due to is_shutting_down(), not done()
3489+ error = Error::Write;
3490+ return false;
3491+ }
3492+
34813493 error = Error::Success;
34823494 return true;
34833495}
@@ -4646,6 +4658,7 @@ class SSLSocketStream final : public Stream {
46464658 bool is_readable() const override;
46474659 bool wait_readable() const override;
46484660 bool wait_writable() const override;
4661+ bool is_peer_alive() const override;
46494662 ssize_t read(char *ptr, size_t size) override;
46504663 ssize_t write(const char *ptr, size_t size) override;
46514664 void get_remote_ip_and_port(std::string &ip, int &port) const override;
@@ -6069,8 +6082,11 @@ bool SocketStream::wait_readable() const {
60696082}
60706083
60716084bool SocketStream::wait_writable() const {
6072- return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
6073- is_socket_alive(sock_);
6085+ return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
6086+ }
6087+
6088+ bool SocketStream::is_peer_alive() const {
6089+ return detail::is_socket_alive(sock_);
60746090}
60756091
60766092ssize_t SocketStream::read(char *ptr, size_t size) {
@@ -6401,7 +6417,11 @@ bool SSLSocketStream::wait_readable() const {
64016417
64026418bool SSLSocketStream::wait_writable() const {
64036419 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
6404- is_socket_alive(sock_) && !tls::is_peer_closed(session_, sock_);
6420+ !tls::is_peer_closed(session_, sock_);
6421+ }
6422+
6423+ bool SSLSocketStream::is_peer_alive() const {
6424+ return !tls::is_peer_closed(session_, sock_);
64056425}
64066426
64076427ssize_t SSLSocketStream::read(char *ptr, size_t size) {
@@ -6925,35 +6945,33 @@ bool Server::write_response_core(Stream &strm, bool close_connection,
69256945 if (post_routing_handler_) { post_routing_handler_(req, res); }
69266946
69276947 // Response line and headers
6928- {
6929- detail::BufferStream bstrm;
6930- if (!detail::write_response_line(bstrm, res.status)) { return false; }
6931- if (header_writer_(bstrm, res.headers) <= 0) { return false; }
6948+ detail::BufferStream bstrm;
6949+ if (!detail::write_response_line(bstrm, res.status)) { return false; }
6950+ if (header_writer_(bstrm, res.headers) <= 0) { return false; }
69326951
6933- // Flush buffer
6934- auto &data = bstrm.get_buffer();
6935- detail::write_data(strm, data. data(), data .size());
6952+ // Combine small body with headers to reduce write syscalls
6953+ if (req.method != "HEAD" && !res.body.empty() && !res.content_provider_) {
6954+ bstrm.write(res.body. data(), res.body .size());
69366955 }
69376956
6938- // Body
6957+ // Log before writing to avoid race condition with client-side code that
6958+ // accesses logger-captured data immediately after receiving the response.
6959+ output_log(req, res);
6960+
6961+ // Flush buffer
6962+ auto &data = bstrm.get_buffer();
6963+ if (!detail::write_data(strm, data.data(), data.size())) { return false; }
6964+
6965+ // Streaming body
69396966 auto ret = true;
6940- if (req.method != "HEAD") {
6941- if (!res.body.empty()) {
6942- if (!detail::write_data(strm, res.body.data(), res.body.size())) {
6943- ret = false;
6944- }
6945- } else if (res.content_provider_) {
6946- if (write_content_with_provider(strm, req, res, boundary, content_type)) {
6947- res.content_provider_success_ = true;
6948- } else {
6949- ret = false;
6950- }
6967+ if (req.method != "HEAD" && res.content_provider_) {
6968+ if (write_content_with_provider(strm, req, res, boundary, content_type)) {
6969+ res.content_provider_success_ = true;
6970+ } else {
6971+ ret = false;
69516972 }
69526973 }
69536974
6954- // Log
6955- output_log(req, res);
6956-
69576975 return ret;
69586976}
69596977
0 commit comments