diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 8467c1f9c52..a5d56be0fb6 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -1040,9 +1040,11 @@ void HTTPRequest::WriteReply(HTTPStatusCode status, std::span r const std::string headers{res.StringifyHeaders()}; const auto headers_bytes{std::as_bytes(std::span{headers})}; + bool send_buffer_was_empty{false}; // Fill the send buffer with the complete serialized response headers + body { LOCK(m_client->m_send_mutex); + send_buffer_was_empty = m_client->m_send_buffer.empty(); m_client->m_send_buffer.insert(m_client->m_send_buffer.end(), headers_bytes.begin(), headers_bytes.end()); // We've been using std::span up until now but it is finally time to copy @@ -1051,10 +1053,6 @@ void HTTPRequest::WriteReply(HTTPStatusCode status, std::span r m_client->m_send_buffer.insert(m_client->m_send_buffer.end(), reply_body.begin(), reply_body.end()); } - // Inform HTTPServer I/O loop that there is data that is ready to be sent to - // this client in the next loop iteration. - m_client->m_send_ready = true; - LogDebug( BCLog::HTTP, "HTTPResponse (status code: %d size: %lld) added to send buffer for client %s (id=%llu)", @@ -1062,6 +1060,18 @@ void HTTPRequest::WriteReply(HTTPStatusCode status, std::span r headers_bytes.size() + reply_body.size(), m_client->m_origin, m_client->m_id); + + // If the send buffer was empty before we wrote this reply, we can try an + // optimistic send akin to CConnman::PushMessage() in which we + // push the data directly out the socket to client right now, instead + // of waiting for the next iteration of the I/O loop. + if (send_buffer_was_empty) { + m_client->MaybeSendBytesFromBuffer(); + } else { + // Inform HTTPServer I/O that data is ready to be sent to this client + // in the next loop iteration. + m_client->m_send_ready = true; + } } util::Expected HTTPServer::BindAndStartListening(const CService& to) @@ -1536,6 +1546,10 @@ bool HTTPRemoteClient::MaybeSendBytesFromBuffer() // Do not attempt to read from this client. return false; } + } else { + // The send buffer isn't flushed yet, try to push more on the next loop. + m_send_ready = true; + m_connection_busy = true; } } diff --git a/src/httpserver.h b/src/httpserver.h index 4018837d8ce..cbb9dd43298 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -573,9 +573,10 @@ public: //! Initialized to true while server waits for first request from client. //! Set to false after data is written to m_send_buffer and then that buffer is flushed to client. //! Reset to true when we receive new request data from client. - //! Checked during DisconnectClients(). All of these operations take place in the HTTPServer I/O loop. + //! Checked during DisconnectClients() and set by read/write operations + //! called in either the HTTPServer I/O loop or by a worker thread during an "optimistic send". //! `m_connection_busy=true` can be overridden by `m_disconnect=true` (we disconnect). - bool m_connection_busy{true}; + std::atomic_bool m_connection_busy{true}; //! Client has requested to keep the connection open after all requests have been responded to. //! Set by (potentially multiple) worker threads and checked in the HTTPServer I/O loop.