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 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-27 18:38:20 +03:00
parent 9b7bda5da2
commit 7718745f53

View File

@ -1595,8 +1595,35 @@ for(;;){
(hascontent && ckeepalive)?"keep-alive":"close"); (hascontent && ckeepalive)?"keep-alive":"close");
} }
hlen += sprintf((char *)buf + hlen, "\r\n"); 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) { if((param->chunked || contentlength64 > 0) && param->operation != HTTP_HEAD && res != 204 && res != 304) {
@ -1648,6 +1675,7 @@ for(;;){
} }
} while(param->chunked); } while(param->chunked);
} }
BODYSENT:
if(isconnect && res == 200 && param->operation){ if(isconnect && res == 200 && param->operation){
hdrfree(&buf, &bufown); hdrfree(&buf, &bufown);
RETURN (mapsocket(param, param->srv->timeouts[CONNECTION_S])); RETURN (mapsocket(param, param->srv->timeouts[CONNECTION_S]));