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 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-27 19:12:49 +03:00
parent f991edbb3a
commit f2cc447f73
5 changed files with 53 additions and 27 deletions

View File

@ -240,8 +240,15 @@ void * threadfunc (void *p) {
#endif #endif
struct clientparam *param = (struct clientparam *)p; struct clientparam *param = (struct clientparam *)p;
do serveparam(param); do {
while(poolpark(param)); /* 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); freeworker(param);
#ifdef _WIN32 #ifdef _WIN32
return 0; return 0;

View File

@ -909,9 +909,10 @@ static void cbmapfinish(void *arg){
int res = m->res, timeo = m->timeosec, usesplice = m->usesplice0; int res = m->res, timeo = m->timeosec, usesplice = m->usesplice0;
free(m); free(m);
/* the buffers given back while relayed are the child's again: without /* the buffers given back while relayed are made again only to map on
them only the end is logged */ after a reload: the end has no use for them. Without them only the
if(bufalloc(param)){ end is logged */
if(res == -2 && bufalloc(param)){
param->res = (res == -2)? 21 : res; param->res = (res == -2)? 21 : res;
param->maptail = NULL; param->maptail = NULL;
dolog(param, NULL); dolog(param, NULL);

View File

@ -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; unsigned lo, start, pos, hend = 0, tail = 0, rest, linelen;
int mark = -1, len; 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; /* 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 a request marked earlier (handed over by httpsrv) is kept from the
mark, which moves with it */ mark, which moves with it */
@ -556,6 +557,29 @@ static void proxytail(struct clientparam *param, void *data){
logurl(param, (char *)data, 0); 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) { void * proxychild(struct clientparam* param) {
int res=0, i=0; int res=0, i=0;
unsigned char* buf = NULL, *newbuf; unsigned char* buf = NULL, *newbuf;
@ -1321,23 +1345,7 @@ for(;;){
return (void *)param->redirectfunc; return (void *)param->redirectfunc;
} }
hdrfree(&buf, &bufown); hdrfree(&buf, &bufown);
/* mapping its last thing, but in a request httpsrv hands over, whose param->res = maplast(param, &req, &ftpbase, param->srv->timeouts[CONNECTION_L]);
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;
}
RETURN(param->res); RETURN(param->res);
} }
@ -1692,13 +1700,15 @@ for(;;){
BODYSENT: 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 (maplast(param, &req, &ftpbase, param->srv->timeouts[CONNECTION_S]));
} }
else if(isconnect){ else if(isconnect){
ckeepalive = keepalive = 1; ckeepalive = keepalive = 1;
} }
else if(!hascontent && !param->chunked) { 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; contentlength64 = 0;
REQUESTEND: REQUESTEND:

View File

@ -173,6 +173,8 @@ int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec){
return (int)param->clibuf[param->clioffset++]; return (int)param->clibuf[param->clioffset++];
} }
if(param->climark == -2) return EOF; /* nothing more until it is cleared */ 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){ if(param->climark >= 0){
/* what was read since the mark stays, moved to the start */ /* what was read since the mark stays, moved to the start */
keep = param->cliinbuf - (unsigned)param->climark; 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 buffer, at the end of the stream, or when nothing comes in time; returns
how much is there, at most size. */ how much is there, at most size. */
unsigned long sockfillbuf(struct clientparam * param, DIRECTION which, unsigned long size, int timeosec){ 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? &param->srvinbuf : &param->cliinbuf; unsigned *inbuf = which? &param->srvinbuf : &param->cliinbuf;
unsigned *offset = which? &param->srvoffset : &param->clioffset; unsigned *offset = which? &param->srvoffset : &param->clioffset;
unsigned bufsize = which? param->srvbufsize : param->clibufsize; unsigned bufsize = which? param->srvbufsize : param->clibufsize;
unsigned want; unsigned want;
int len; 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){ if(*offset){
memmove(buf, buf + *offset, *inbuf - *offset); memmove(buf, buf + *offset, *inbuf - *offset);
*inbuf -= *offset; *inbuf -= *offset;
@ -232,6 +237,7 @@ int sockgetcharsrv(struct clientparam * param, int timeosec, int timeousec){
return (int)param->srvbuf[param->srvoffset++]; return (int)param->srvbuf[param->srvoffset++];
} }
bufrelease(param, SERVER); bufrelease(param, SERVER);
if(!param->srvbuf && bufget(param, SERVER)) return EOF;
if ((len = sockrecvfrom(param, param->remsock, (struct sockaddr *)&param->sinsr, param->srvbuf, param->srvbufsize, timeosec*1000 + timeousec))<=0) return EOF; if ((len = sockrecvfrom(param, param->remsock, (struct sockaddr *)&param->sinsr, param->srvbuf, param->srvbufsize, timeosec*1000 + timeousec))<=0) return EOF;
param->srvinbuf = len; param->srvinbuf = len;
param->srvoffset = 1; param->srvoffset = 1;

View File

@ -1018,8 +1018,10 @@ log("timeout");
CLEANRET: CLEANRET:
/* what comes after a mapping finds the buffers there */ /* what comes after a mapping finds the buffers there; after the last
if(((!param->clibuf && bufget(param, CLIENT)) || (!param->srvbuf && bufget(param, SERVER))) && !res) res = 21; 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 #ifdef WITHSPLICE
/* empty pipes stay with the connection for its next mapping; one still /* empty pipes stay with the connection for its next mapping; one still