From f2cc447f733e077aec748542d3469881ae913f52 Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Tue, 27 Oct 2026 19:12:49 +0300 Subject: [PATCH] Relays: no buffers made again after a connection's last mapping sockmap() made both buffers again as it returned, for whatever came after it, and the callbacks before the end: after the last mapping there is only the end, the log line and a refusal sent from a buffer of its own, with no use for them. They are made again only for a mapping that is not the last, and for a fiber mapping on after a reload; the functions reading a side's buffer make it if it is not there, and a worker taken from the pool for its next connection gets its own again. proxy.c's other last mappings, a CONNECT through a parent and an answer ending as the server closes, are marked so too (maplast(), proxytail), as the CONNECT of its own was: their end, and with callbacks the fiber, is the loop's. Co-Authored-By: Claude Opus 5.5 --- src/mainclient.c | 11 +++++++++-- src/mainloop.c | 7 ++++--- src/proxy.c | 48 ++++++++++++++++++++++++++++------------------- src/sockgetchar.c | 8 +++++++- src/sockmap.c | 6 ++++-- 5 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/mainclient.c b/src/mainclient.c index 9b67ea4..ac1f2f0 100644 --- a/src/mainclient.c +++ b/src/mainclient.c @@ -240,8 +240,15 @@ void * threadfunc (void *p) { #endif struct clientparam *param = (struct clientparam *)p; - do serveparam(param); - while(poolpark(param)); + do { + /* a worker taken from the pool: its buffers may have been let go on + its last connection's last mapping */ + if(bufalloc(param)){ + param->res = 21; + releaseparam(param); + } + else serveparam(param); + } while(poolpark(param)); freeworker(param); #ifdef _WIN32 return 0; diff --git a/src/mainloop.c b/src/mainloop.c index 85d2d7c..a651017 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -909,9 +909,10 @@ static void cbmapfinish(void *arg){ int res = m->res, timeo = m->timeosec, usesplice = m->usesplice0; free(m); - /* the buffers given back while relayed are the child's again: without - them only the end is logged */ - if(bufalloc(param)){ + /* the buffers given back while relayed are made again only to map on + after a reload: the end has no use for them. Without them only the + end is logged */ + if(res == -2 && bufalloc(param)){ param->res = (res == -2)? 21 : res; param->maptail = NULL; dolog(param, NULL); diff --git a/src/proxy.c b/src/proxy.c index ad58ed9..e05d453 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -252,6 +252,7 @@ static int hdrread(struct clientparam *param, DIRECTION which, unsigned front, u unsigned lo, start, pos, hend = 0, tail = 0, rest, linelen; int mark = -1, len; + if(!*bufp && bufget(param, which)) return -2; /* what was read and not taken yet moves to one byte into the buffer; a request marked earlier (handed over by httpsrv) is kept from the mark, which moves with it */ @@ -556,6 +557,29 @@ static void proxytail(struct clientparam *param, void *data){ logurl(param, (char *)data, 0); } +/* The connection's last mapping. Its end, the status and the log line, can + be a loop's (proxytail), the request line handed over for them, but for + a request httpsrv hands over, whose end is httpsrv's: the rest of the end + may then never come back here. What it would free is freed now, and the + request line is back if mapping returns. */ +static int maplast(struct clientparam *param, unsigned char **req, unsigned char **ftpbase, int timeo){ + int res; + + if(!param->onerequest && *req){ + freeptr(ftpbase); + param->maptaildata = *req; + *req = NULL; + param->maptail = proxytail; + } + res = mapsocket(param, timeo); + if(param->maptail == proxytail){ + *req = param->maptaildata; + param->maptaildata = NULL; + param->maptail = NULL; + } + return res; +} + void * proxychild(struct clientparam* param) { int res=0, i=0; unsigned char* buf = NULL, *newbuf; @@ -1321,23 +1345,7 @@ for(;;){ return (void *)param->redirectfunc; } hdrfree(&buf, &bufown); - /* mapping its last thing, but in a request httpsrv hands over, whose - end is httpsrv's: the rest of the end can be a loop's, which this - function's own may then never reach. What it would free is freed - now, and the request line is the tail's, back here if mapping - returns. */ - if(!param->onerequest && req){ - freeptr(&ftpbase); - param->maptaildata = req; - req = NULL; - param->maptail = proxytail; - } - param->res = mapsocket(param, param->srv->timeouts[CONNECTION_L]); - if(param->maptail == proxytail){ - req = param->maptaildata; - param->maptaildata = NULL; - param->maptail = NULL; - } + param->res = maplast(param, &req, &ftpbase, param->srv->timeouts[CONNECTION_L]); RETURN(param->res); } @@ -1692,13 +1700,15 @@ for(;;){ BODYSENT: if(isconnect && res == 200 && param->operation){ hdrfree(&buf, &bufown); - RETURN (mapsocket(param, param->srv->timeouts[CONNECTION_S])); + RETURN (maplast(param, &req, &ftpbase, param->srv->timeouts[CONNECTION_S])); } else if(isconnect){ ckeepalive = keepalive = 1; } else if(!hascontent && !param->chunked) { - RETURN(mapsocket(param, param->srv->timeouts[CONNECTION_S])); + /* the answer ends as the server's side does: the connection with it */ + hdrfree(&buf, &bufown); + RETURN(maplast(param, &req, &ftpbase, param->srv->timeouts[CONNECTION_S])); } contentlength64 = 0; REQUESTEND: diff --git a/src/sockgetchar.c b/src/sockgetchar.c index 5f27cb5..ffba954 100644 --- a/src/sockgetchar.c +++ b/src/sockgetchar.c @@ -173,6 +173,8 @@ int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec){ return (int)param->clibuf[param->clioffset++]; } if(param->climark == -2) return EOF; /* nothing more until it is cleared */ + /* let go on a connection's last mapping */ + if(!param->clibuf && bufget(param, CLIENT)) return EOF; if(param->climark >= 0){ /* what was read since the mark stays, moved to the start */ keep = param->cliinbuf - (unsigned)param->climark; @@ -197,13 +199,16 @@ int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec){ buffer, at the end of the stream, or when nothing comes in time; returns how much is there, at most size. */ unsigned long sockfillbuf(struct clientparam * param, DIRECTION which, unsigned long size, int timeosec){ - unsigned char *buf = which? param->srvbuf : param->clibuf; + unsigned char *buf; unsigned *inbuf = which? ¶m->srvinbuf : ¶m->cliinbuf; unsigned *offset = which? ¶m->srvoffset : ¶m->clioffset; unsigned bufsize = which? param->srvbufsize : param->clibufsize; unsigned want; int len; + /* let go on a connection's last mapping */ + if(!(which? param->srvbuf : param->clibuf) && bufget(param, which)) return 0; + buf = which? param->srvbuf : param->clibuf; if(*offset){ memmove(buf, buf + *offset, *inbuf - *offset); *inbuf -= *offset; @@ -232,6 +237,7 @@ int sockgetcharsrv(struct clientparam * param, int timeosec, int timeousec){ return (int)param->srvbuf[param->srvoffset++]; } bufrelease(param, SERVER); + if(!param->srvbuf && bufget(param, SERVER)) return EOF; if ((len = sockrecvfrom(param, param->remsock, (struct sockaddr *)¶m->sinsr, param->srvbuf, param->srvbufsize, timeosec*1000 + timeousec))<=0) return EOF; param->srvinbuf = len; param->srvoffset = 1; diff --git a/src/sockmap.c b/src/sockmap.c index d6ce27c..16af624 100644 --- a/src/sockmap.c +++ b/src/sockmap.c @@ -1018,8 +1018,10 @@ log("timeout"); CLEANRET: - /* what comes after a mapping finds the buffers there */ - if(((!param->clibuf && bufget(param, CLIENT)) || (!param->srvbuf && bufget(param, SERVER))) && !res) res = 21; + /* what comes after a mapping finds the buffers there; after the last + one only its end, which has no use for them */ + if(!param->maptail && + ((!param->clibuf && bufget(param, CLIENT)) || (!param->srvbuf && bufget(param, SERVER))) && !res) res = 21; #ifdef WITHSPLICE /* empty pipes stay with the connection for its next mapping; one still