mirror of
https://github.com/3proxy/3proxy.git
synced 2026-11-05 09:25:49 +08:00
Sockets closed without SO_LINGER
Client, outgoing and FTP data sockets, and the inetd socket of a standalone service, were given SO_LINGER, the client's for the long string timeout (60 seconds by default), the outgoing one's for the LINGER timeout. It kept nothing from being lost: without it the system sends what is queued after the close all the same, and the time running out reset the connection, losing what was still queued. All it did was make close() wait for the peer to acknowledge the data, holding the connection's thread and maxconn slot meanwhile, and, for clients served on an event loop, every client of the loop: with one loop it was idle a quarter of the time in a benchmark of many short exchanges. The LINGER timeout keeps its use: how long a half-closed connection waits for the rest of its data, and how long httpsrv reads what is left of a request before closing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c55f736a0d
commit
43f40569d4
@ -526,9 +526,11 @@ can use %A as produced archive name and %F as filename.
|
||||
\fBCHAIN\fR timeout for reading data from chained connection
|
||||
.br
|
||||
\fBLINGER\fR timeout to deliver buffered data after one of the sides has closed
|
||||
its sending side, also used as SO_LINGER value on outgoing connections. Too small
|
||||
value may cause the tail of the data to be lost on slow connections, too large one
|
||||
delays release of the sockets.
|
||||
its sending side, and for httpsrv to read what is left of a request before it
|
||||
closes the connection. Too small value may cause the tail of the data to be
|
||||
lost on slow connections, too large one delays release of the sockets.
|
||||
Sockets are closed without SO_LINGER: the system sends what is queued after
|
||||
the close, the connection's thread and slot freed at once.
|
||||
.br
|
||||
default timeouts 1 5 30 60 180 1800 15 60 15 5 5
|
||||
.br
|
||||
|
||||
@ -789,9 +789,6 @@ 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,param->srv->timeouts[LINGER_TO]};
|
||||
#endif
|
||||
int tries = RANGETRIES;
|
||||
|
||||
if(SAISNULL(¶m->sinsr)){
|
||||
@ -823,10 +820,6 @@ int doconnect(struct clientparam * param){
|
||||
*SAPORT(¶m->sinsl) = 0;
|
||||
setopts(param->remsock, ¶m->srv->srvsockopts, param->srv->maxseg);
|
||||
|
||||
#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)
|
||||
|
||||
12
src/ftppr.c
12
src/ftppr.c
@ -23,18 +23,11 @@ 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;
|
||||
|
||||
buf = param->tmpbuf; /* a filter may replace it with one of its own */
|
||||
param->ctrlsock = param->clisock;
|
||||
param->operation = CONNECT;
|
||||
#ifndef _WIN32
|
||||
lg.l_onoff = 1;
|
||||
lg.l_linger = param->srv->timeouts[STRING_L];
|
||||
#endif
|
||||
if(socksend(param, param->ctrlsock, (unsigned char *)"220 Ready\r\n", 11, param->srv->timeouts[STRING_S])!=11) {RETURN (801);}
|
||||
for(;;){
|
||||
i = sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 10, '\n', param->srv->timeouts[CONNECTION_S]);
|
||||
@ -269,11 +262,6 @@ 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, param->srv->timeouts[CONNECTION_S]);
|
||||
if(param->remsock != INVALID_SOCKET) {
|
||||
|
||||
@ -447,8 +447,7 @@ void releaseparam(struct clientparam * param) {
|
||||
/* the sockets were closed and the filters cleared with it */
|
||||
param->sostate = NULL;
|
||||
/* Leaving the service comes last. Once its last child is gone, srvfree()
|
||||
frees the service along with its socket functions, filters and mutex,
|
||||
and a close above may have lingered for seconds before getting here. */
|
||||
frees the service along with its socket functions, filters and mutex. */
|
||||
if(param->srv){
|
||||
_3proxy_mutex_lock(¶m->srv->counter_mutex);
|
||||
if(param->prev || param->next || param->srv->child == param){
|
||||
|
||||
@ -55,9 +55,7 @@ static int tcpnext(struct srvstart *s, SOCKET *client, int *nlog){
|
||||
SASIZETYPE size;
|
||||
unsigned char buf[256];
|
||||
int error = 0, opt = 1, res, waited;
|
||||
#ifndef _WIN32
|
||||
struct linger lg;
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
unsigned long ul = 1;
|
||||
#endif
|
||||
|
||||
@ -166,14 +164,10 @@ static int tcpnext(struct srvstart *s, SOCKET *client, int *nlog){
|
||||
#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 = srv->timeouts[STRING_L];
|
||||
srv->so._setsockopt(srv->so.state, new_sock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
#endif
|
||||
/* No linger: a close completes in the background, what is queued sent
|
||||
first; waiting for the peer to acknowledge it would only hold the thread
|
||||
(or the event loop) and the slot, and its time running out would reset
|
||||
the connection, losing what was still queued. */
|
||||
srv->so._setsockopt(srv->so.state, new_sock, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(int));
|
||||
*client = new_sock;
|
||||
return NEXT_CLIENT;
|
||||
|
||||
@ -215,7 +215,6 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
||||
int res;
|
||||
#ifndef _WIN32
|
||||
int opt = 1;
|
||||
struct linger lg;
|
||||
struct clientparam *newparam;
|
||||
#endif
|
||||
|
||||
@ -255,9 +254,6 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
||||
if(s.inetd) {
|
||||
fcntl(0,F_SETFL,O_NONBLOCK | fcntl(0,F_GETFL));
|
||||
if(!s.isudp){
|
||||
lg.l_onoff = 1;
|
||||
lg.l_linger = s.srv.timeouts[STRING_L];
|
||||
s.srv.so._setsockopt(s.srv.so.state, 0, SOL_SOCKET, SO_LINGER, (unsigned char *)&lg, sizeof(lg));
|
||||
s.srv.so._setsockopt(s.srv.so.state, 0, SOL_SOCKET, SO_OOBINLINE, (unsigned char *)&opt, sizeof(int));
|
||||
}
|
||||
s.defparam.clisock = 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user