mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-15 16:15:49 +08:00
Fix socket draining / lingering
This commit is contained in:
parent
24ac413fc5
commit
1cea4fef4b
@ -387,7 +387,6 @@ int
|
||||
return shutdown(s, how);
|
||||
}
|
||||
int WINAPI def_closesocket(void* state, SOCKET s){
|
||||
usleep(SLEEPTIME);
|
||||
return closesocket(s);
|
||||
}
|
||||
#else
|
||||
@ -715,7 +714,9 @@ int doconnect(struct clientparam * param){
|
||||
if(param->srv->so._getpeername(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsr, &size)==-1) {return (14);}
|
||||
}
|
||||
else {
|
||||
#ifndef _WIN32
|
||||
struct linger lg = {1,conf.timeouts[LINGER_TO]};
|
||||
#endif
|
||||
int tries = RANGETRIES;
|
||||
|
||||
if(SAISNULL(¶m->sinsr)){
|
||||
@ -747,7 +748,10 @@ int doconnect(struct clientparam * param){
|
||||
*SAPORT(¶m->sinsl) = 0;
|
||||
setopts(param->remsock, param->srv->srvsockopts);
|
||||
|
||||
#ifndef _WIN32
|
||||
/* not on Windows, see the accepting side in proxymain.c */
|
||||
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
#endif
|
||||
if(extbindtodevice(param, param->remsock)) return 12;
|
||||
#ifdef WITH_UN
|
||||
if(*SAFAMILY(¶m->sinsl) != AF_UNIX)
|
||||
|
||||
@ -23,14 +23,18 @@ void * ftpprchild(struct clientparam* param) {
|
||||
SOCKET sc = INVALID_SOCKET, ss = INVALID_SOCKET, clidatasock = INVALID_SOCKET;
|
||||
SASIZETYPE sasize;
|
||||
char * req = NULL;
|
||||
#ifndef _WIN32
|
||||
struct linger lg;
|
||||
#endif
|
||||
struct pollfd fds;
|
||||
|
||||
if(!(buf = malloc(BUFSIZE))) RETURN(876);
|
||||
param->ctrlsock = param->clisock;
|
||||
param->operation = CONNECT;
|
||||
#ifndef _WIN32
|
||||
lg.l_onoff = 1;
|
||||
lg.l_linger = conf.timeouts[STRING_L];;
|
||||
lg.l_linger = conf.timeouts[STRING_L];
|
||||
#endif
|
||||
if(socksend(param, param->ctrlsock, (unsigned char *)"220 Ready\r\n", 11, conf.timeouts[STRING_S])!=11) {RETURN (801);}
|
||||
for(;;){
|
||||
i = sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 10, '\n', conf.timeouts[CONNECTION_S]);
|
||||
@ -262,8 +266,11 @@ void * ftpprchild(struct clientparam* param) {
|
||||
}
|
||||
sc = param->remsock;
|
||||
param->remsock = ss;
|
||||
#ifndef _WIN32
|
||||
/* not on Windows, see the accepting side in proxymain.c */
|
||||
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
param->srv->so._setsockopt(param->sostate, clidatasock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
#endif
|
||||
param->clisock = clidatasock;
|
||||
res = mapsocket(param, conf.timeouts[CONNECTION_S]);
|
||||
if(param->remsock != INVALID_SOCKET) {
|
||||
|
||||
@ -1154,6 +1154,37 @@ static void httpsrv_drain(struct clientparam *param, uint64_t len)
|
||||
}
|
||||
}
|
||||
|
||||
/* Ends a connection this server is closing while the client may still be
|
||||
sending: a body which was not read, or a request sent behind one answered
|
||||
with a close. Closing with that unread resets the connection, and the
|
||||
client loses the answer. The sending side is shut down instead and what
|
||||
arrives is thrown away until the client closes too, for a bounded time
|
||||
and amount, which is what lets the answer be read. */
|
||||
static void httpsrv_linger(struct clientparam *param)
|
||||
{
|
||||
char buf[HTTPSRV_BLOCK];
|
||||
struct pollfd fd;
|
||||
uint64_t left = HTTPSRV_MAXBODY;
|
||||
time_t deadline = time(NULL) + conf.timeouts[LINGER_TO];
|
||||
|
||||
if(param->clisock == INVALID_SOCKET) return;
|
||||
if(param->srv->so._shutdown(param->sostate, param->clisock, SHUT_WR)) return;
|
||||
while(left){
|
||||
int timeo = (int)(deadline - time(NULL));
|
||||
int got;
|
||||
|
||||
if(timeo <= 0) break;
|
||||
memset(&fd, 0, sizeof(fd));
|
||||
fd.fd = param->clisock;
|
||||
fd.events = POLLIN;
|
||||
if(param->srv->so._poll(param->sostate, &fd, 1, timeo * 1000) <= 0) break;
|
||||
got = (int)param->srv->so._recv(param->sostate, param->clisock, buf,
|
||||
(left > sizeof(buf))? (int)sizeof(buf) : (int)left, 0);
|
||||
if(got <= 0) break;
|
||||
left -= (uint64_t)got;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keeps the request as the client wrote it. Only what a handoff needs: the
|
||||
request line and the headers, exactly as they came, since the proxy code
|
||||
reads them again from the beginning. */
|
||||
@ -1451,6 +1482,12 @@ static int httpsrv_request(struct clientparam *param, struct httpreq *r)
|
||||
}
|
||||
}
|
||||
if(i && i != 10){
|
||||
/* The body goes before the answer, as for any answer this server
|
||||
gives: left in the stream it would be read as the next request. */
|
||||
if(!r->drained){
|
||||
if(r->contentlen) httpsrv_drain(param, r->contentlen);
|
||||
r->drained = 1;
|
||||
}
|
||||
/* 4 no credentials, 5 unknown user, 6 wrong password: all of them
|
||||
should let the client offer credentials again. */
|
||||
if(i >= 4 && i <= 6) op_authrequired(r);
|
||||
@ -1573,7 +1610,12 @@ void * httpsrvchild(struct clientparam *param)
|
||||
dolog(param, (unsigned char *)logbuf);
|
||||
}
|
||||
|
||||
if(handoff || !r.keepalive) break;
|
||||
if(handoff) break;
|
||||
if(!r.keepalive){
|
||||
/* the client may still be sending; let it read the answer */
|
||||
httpsrv_linger(param);
|
||||
break;
|
||||
}
|
||||
first = 0;
|
||||
}
|
||||
/* A child named by an access rule takes the connection over, which the
|
||||
|
||||
@ -306,7 +306,9 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
unsigned char *cbc_string = NULL, *cbl_string = NULL;
|
||||
PROXYSOCKADDRTYPE cbsa;
|
||||
FILE *fp = NULL;
|
||||
#ifndef _WIN32
|
||||
struct linger lg;
|
||||
#endif
|
||||
int nlog = 5000;
|
||||
int haveslot = 0, nfull = 0;
|
||||
#ifdef __linux__
|
||||
@ -1117,10 +1119,14 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
#else
|
||||
fcntl(new_sock,F_SETFL,O_NONBLOCK | fcntl(new_sock,F_GETFL));
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
/* Not on Windows: there closesocket() on a non-blocking socket with a linger
|
||||
timeout fails with WSAEWOULDBLOCK while data is queued, leaving the socket
|
||||
open. Without it the close completes in the background, data first. */
|
||||
lg.l_onoff = 1;
|
||||
lg.l_linger = conf.timeouts[STRING_L];
|
||||
|
||||
srv.so._setsockopt(srv.so.state, new_sock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
#endif
|
||||
srv.so._setsockopt(srv.so.state, new_sock, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(int));
|
||||
}
|
||||
#ifndef NOUDPMAIN
|
||||
@ -1413,21 +1419,34 @@ void freeparam(struct clientparam * param) {
|
||||
}
|
||||
if(param->clibuf) free(param->clibuf);
|
||||
if(param->srvbuf) free(param->srvbuf);
|
||||
/* Not shut down on Windows: there shutting down receiving resets the
|
||||
connection as soon as the peer sends anything more, and close()
|
||||
alone sends whatever is queued before its FIN. Elsewhere it stays:
|
||||
on the BSDs it discards what is left unread, so the close that
|
||||
follows is not turned into a reset. */
|
||||
if(param->srv) {
|
||||
if(param->ctrlsocksrv != INVALID_SOCKET && param->ctrlsocksrv != param->remsock) {
|
||||
#ifndef _WIN32
|
||||
param->srv->so._shutdown(param->sostate, param->ctrlsocksrv, SHUT_RDWR);
|
||||
#endif
|
||||
param->srv->so._closesocket(param->sostate, param->ctrlsocksrv);
|
||||
}
|
||||
if(param->ctrlsock != INVALID_SOCKET && param->ctrlsock != param->clisock) {
|
||||
#ifndef _WIN32
|
||||
param->srv->so._shutdown(param->sostate, param->ctrlsock, SHUT_RDWR);
|
||||
#endif
|
||||
param->srv->so._closesocket(param->sostate, param->ctrlsock);
|
||||
}
|
||||
if(param->remsock != INVALID_SOCKET) {
|
||||
#ifndef _WIN32
|
||||
param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR);
|
||||
#endif
|
||||
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||
}
|
||||
if(param->clisock != INVALID_SOCKET) {
|
||||
#ifndef _WIN32
|
||||
param->srv->so._shutdown(param->sostate, param->clisock, SHUT_RDWR);
|
||||
#endif
|
||||
param->srv->so._closesocket(param->sostate, param->clisock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -819,9 +819,6 @@ log("timeout");
|
||||
else if(inclientpipe || inserverpipe) res = 94;
|
||||
#endif
|
||||
|
||||
if((param->nwrites > 0 && !SERVERTERMWRITE) || (param->nreads > 0 && !CLIENTTERMWRITE))
|
||||
usleep(SLEEPTIME * 10);
|
||||
|
||||
CLEANRET:
|
||||
|
||||
#ifdef WITHSPLICE
|
||||
|
||||
Loading…
Reference in New Issue
Block a user