commit 476c5d48ea9fbd4d1c6d4ef3f128b6e4898a297f

Fix linger-on-close behaviour to avoid rare failure conditions.

     - Don't assume that when FIONREAD returns 0, that it's safe to close the
       socket.  There may still be data that's about to arrive, and we'll still
       send an RST if the socket is confused, potentially confusing the client.

     - Don't close the connection immediately after sending a successful
       response; linger-on-close was only happening in the case of errors, but it
       has to happen in case of success too, because the client doesn't
       necessarily know we're about to close after this request, and may have
       sent additional ones. (eg. if server.max-keep-alive-requests is small.)

     - Don't close the connection immediately even if keep_alive is 0; there are
       several reasons keep_alive can be 0.  If the client requested Connection:
       close, then it would be okay to close right away, since we can assume he
       didn't send anything else.  But it's harmless (and more resilient) to do
       the lingering regardless.

     - Increase the lingering timeout from 1s to 30s.  In the vast majority of
       cases, the timeout never kicks in anyway.  The only times when it might
       be needed are a) in race conditions, in which case timing out too early
       defeats the purpose of lingering at all; b) if there's a lot of data,
       which is basically the same as (a); or c) if the remote end disappears,
       in which case we now suffer through a longer timeout... but we would
       anyway, if we were waiting for them to receive our transmission.

git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2636 152afb58-edef-0310-8abb-c4023f1b3aa9
svn/tags/lighttpd-1.4.24
Stefan Bühler 13 years ago
parent 925a5ed5c4
commit 7c65bd74ff
  1. 57
      src/connections.c

@ -1250,9 +1250,11 @@ static handler_t connection_handle_fdevent(void *s, void *context, int revents)
/* */
read(con->fd, buf, sizeof(buf));
} else {
/* nothing to read */
con->close_timeout_ts = srv->cur_ts - 2;
/* nothing to read - yet. But that doesn't
* mean something won't show up in our buffers
* sometime soon, so we can't quite until
* poll() gives us the HUP notification.
*/
}
}
@ -1569,7 +1571,12 @@ int connection_state_machine(server *srv, connection *con) {
}
}
#endif
connection_close(srv, con);
if ((0 == shutdown(con->fd, SHUT_WR))) {
con->close_timeout_ts = srv->cur_ts;
connection_set_state(srv, con, CON_STATE_CLOSE);
} else {
connection_close(srv, con);
}
srv->con_closed++;
}
@ -1594,28 +1601,31 @@ int connection_state_machine(server *srv, connection *con) {
"state for fd", con->fd, connection_get_state(con->state));
}
if (con->keep_alive) {
if (ioctl(con->fd, FIONREAD, &b)) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"ioctl() failed", strerror(errno));
}
if (b > 0) {
char buf[1024];
log_error_write(srv, __FILE__, __LINE__, "sdd",
"CLOSE-read()", con->fd, b);
/* */
read(con->fd, buf, sizeof(buf));
} else {
/* nothing to read */
/* we have to do the linger_on_close stuff regardless
* of con->keep_alive; even non-keepalive sockets may
* still have unread data, and closing before reading
* it will make the client not see all our output.
*/
if (ioctl(con->fd, FIONREAD, &b)) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"ioctl() failed", strerror(errno));
}
if (b > 0) {
char buf[1024];
log_error_write(srv, __FILE__, __LINE__, "sdd",
"CLOSE-read()", con->fd, b);
con->close_timeout_ts = srv->cur_ts - 2;
}
/* */
read(con->fd, buf, sizeof(buf));
} else {
con->close_timeout_ts = srv->cur_ts - 2;
/* nothing to read - yet. But that doesn't
* mean something won't show up in our buffers
* sometime soon, so we can't quite until
* poll() gives us the HUP notification.
*/
}
if (srv->cur_ts - con->close_timeout_ts > 1) {
if (srv->cur_ts - con->close_timeout_ts > 30) {
connection_close(srv, con);
if (srv->srvconf.log_state_handling) {
@ -1739,8 +1749,7 @@ int connection_state_machine(server *srv, connection *con) {
connection_reset(srv, con);
/* close the connection */
if ((con->keep_alive == 1) &&
(0 == shutdown(con->fd, SHUT_WR))) {
if ((0 == shutdown(con->fd, SHUT_WR))) {
con->close_timeout_ts = srv->cur_ts;
connection_set_state(srv, con, CON_STATE_CLOSE);

Loading…
Cancel
Save