From 7718745f53a0b4ea22784d01934db214a84c401e Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Tue, 27 Oct 2026 18:38:20 +0300 Subject: [PATCH] HTTP proxy: what came of the body with the headers goes with them An answer's headers were sent by themselves, and what hdrread() had read of the body after them by sockmap() next: two segments where one does, for most small answers. The headers, still where hdrread() put them in the server's buffer, move up against the body, and go with what of it was read in one send; an answer whose whole body came with its headers is not mapped at all. Not for chunked answers, answers with no body, or where a data filter is to see the body; the bytes were counted as they were read. Co-Authored-By: Claude Opus 5.5 --- src/proxy.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/proxy.c b/src/proxy.c index d453ee7..74e3947 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -1595,8 +1595,35 @@ for(;;){ (hascontent && ckeepalive)?"keep-alive":"close"); } hlen += sprintf((char *)buf + hlen, "\r\n"); - if(socksend(param, param->clisock, buf, hlen, param->srv->timeouts[STRING_S]) != hlen) { - RETURN(521); + { + /* what of the body came with the headers goes with them, in one + piece: the headers, still where hdrread() left them, moved up + against it. Counted as it was read; no filter is to see it. */ + unsigned char *out = buf; + int n = 0; + + if(!param->chunked && hascontent == 1 && contentlength64 > 0 && + param->operation != HTTP_HEAD && res != 204 && res != 304 && + (param->nolongdatfilter || !param->ndatfilterssrv) && + !bufown && buf + bufsize == param->srvbuf + param->srvoffset && + param->srvinbuf > param->srvoffset){ + n = (int)((uint64_t)(param->srvinbuf - param->srvoffset) < contentlength64? + param->srvinbuf - param->srvoffset : contentlength64); + out = param->srvbuf + param->srvoffset - hlen; + /* up, over what may be its own end: memmove */ + memmove(out, buf, hlen); + buf = out; + } + if(socksend(param, param->clisock, out, hlen + n, param->srv->timeouts[STRING_S]) != hlen + n) { + RETURN(521); + } + if(n){ + param->srvoffset += n; + contentlength64 -= n; + bufrelease(param, SERVER); + /* all of it sent: nothing more to map */ + if(!contentlength64) goto BODYSENT; + } } } if((param->chunked || contentlength64 > 0) && param->operation != HTTP_HEAD && res != 204 && res != 304) { @@ -1648,6 +1675,7 @@ for(;;){ } } while(param->chunked); } +BODYSENT: if(isconnect && res == 200 && param->operation){ hdrfree(&buf, &bufown); RETURN (mapsocket(param, param->srv->timeouts[CONNECTION_S]));