From 7a5fdae1bd8fc800b76fe24619b73db07329b8cd Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Thu, 22 Oct 2026 16:45:59 -0500 Subject: [PATCH] Send an http client request's head and body in one write request.end(body) on a request whose head was not sent yet wrote the head and then the body. On a socket that was already connected (a reused keep-alive connection), each write is sent at once, so the two went out as separate segments, and the server could handle the head and answer before the body arrived. Node sends the head together with the body. When the client and the server run in one program, the server's 'end' and 'close' for the request and the client's reading of the response then depended on which socket the poller reported first: corpus 5409-http-held-request-body printed the client's line before the request's 'end' and 'close' in a macOS dev build. The head and a body known at end() now go out as one write, as Node sends them (the server's response already did). --- packages/runtime/src/scr_http.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/runtime/src/scr_http.c b/packages/runtime/src/scr_http.c index a5847e59b..cb88297bf 100644 --- a/packages/runtime/src/scr_http.c +++ b/packages/runtime/src/scr_http.c @@ -4323,14 +4323,32 @@ static void scr_http_client_end_raw(ScrHttpClientReq *c, const char *data, size_ } if (!c->head_sent) { /* whole body known NOW: Content-Length framing (or none — the head - * serializer's empty-body method split) */ + * serializer's empty-body method split). Head and body go out as one + * write, as Node sends them: on a connected (reused) socket two writes + * would be two segments, and the server could answer before the body + * arrives. */ + c->hold_head = true; scr_http_client_send_head(c, (long long)len); + c->hold_head = false; if (scr_exc_pending()) return; if (c->chunked) { scr_http_client_write_raw(c, data, len); if (c->sock) scr_http_send_chunk_end(c->sock, &c->trailers); - } else if (c->sock && len > 0) { - scr_net_sock_write_native(c->sock, data, len); + } else if (c->held_head != NULL) { + char *head = c->held_head; + size_t head_len = c->held_head_len; + c->held_head = NULL; + c->held_head_len = c->held_head_cap = 0; + if (c->sock) { + if (len > 0) { + char *joined = realloc(head, head_len + len); + if (!joined) scr_http_oom(); + head = joined; + memcpy(head + head_len, data, len); + } + scr_net_sock_write_native(c->sock, head, head_len + len); + } + free(head); } } else { scr_http_client_write_raw(c, data, len);