fielding 97/04/07 02:44:20
Modified: src CHANGES Configure conf.h http_main.c
Log:
Improved lingering_close by adding a special timeout, removing the
spurious log messages, removing the nonblocking settings (they
are not needed with the better timeout), and adding commentary
about the NO_LINGCLOSE and USE_SO_LINGER issues. NO_LINGCLOSE is
now the default for SunOS4, Unixware, NeXT, and Irix.
Send error messages about setsockopt failures to the server error
log instead of stderr.
Reviewed by: Jim Jagielski, Chuck Murcko, Randy Terbush
Revision Changes Path
1.222 +9 -0 apache/src/CHANGES
Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apache/src/CHANGES,v
retrieving revision 1.221
retrieving revision 1.222
diff -C3 -r1.221 -r1.222
*** CHANGES 1997/04/06 07:43:38 1.221
--- CHANGES 1997/04/07 09:44:16 1.222
***************
*** 1,5 ****
--- 1,14 ----
Changes with Apache 1.2b8
+ *) Improved lingering_close by adding a special timeout, removing the
+ spurious log messages, removing the nonblocking settings (they
+ are not needed with the better timeout), and adding commentary
+ about the NO_LINGCLOSE and USE_SO_LINGER issues. NO_LINGCLOSE is
+ now the default for SunOS4, Unixware, NeXT, and Irix. [Roy Fielding]
+
+ *) Send error messages about setsockopt failures to the server error
+ log instead of stderr. [Roy Fielding]
+
*) Fix loopholes in proxy cache expiry vis a vis alarms. [Brian Moore]
*) Stopgap solution for CGI 3-second delay with server-side includes: if
1.88 +2 -2 apache/src/Configure
Index: Configure
===================================================================
RCS file: /export/home/cvs/apache/src/Configure,v
retrieving revision 1.87
retrieving revision 1.88
diff -C3 -r1.87 -r1.88
*** Configure 1997/04/05 04:31:42 1.87
--- Configure 1997/04/07 09:44:17 1.88
***************
*** 347,359 ****
*-unixware1)
DEF_WANTHSREGEX=yes
OS='Unixware'
! CFLAGS="$CFLAGS -DSVR4"
LIBS="$LIBS -lsocket -lnsl -lcrypt"
;;
*-unixware2)
DEF_WANTHSREGEX=yes
OS='Unixware'
! CFLAGS="$CFLAGS -DSVR4"
LIBS="$LIBS -lsocket -lnsl -lcrypt"
;;
*-unixware211)
--- 347,359 ----
*-unixware1)
DEF_WANTHSREGEX=yes
OS='Unixware'
! CFLAGS="$CFLAGS -DSVR4 -DNO_LINGCLOSE"
LIBS="$LIBS -lsocket -lnsl -lcrypt"
;;
*-unixware2)
DEF_WANTHSREGEX=yes
OS='Unixware'
! CFLAGS="$CFLAGS -DSVR4 -DNO_LINGCLOSE"
LIBS="$LIBS -lsocket -lnsl -lcrypt"
;;
*-unixware211)
1.91 +3 -1 apache/src/conf.h
Index: conf.h
===================================================================
RCS file: /export/home/cvs/apache/src/conf.h,v
retrieving revision 1.90
retrieving revision 1.91
diff -C3 -r1.90 -r1.91
*** conf.h 1997/04/02 13:25:16 1.90
--- conf.h 1997/04/07 09:44:17 1.91
***************
*** 71,77 ****
#define NEED_STRCASECMP
#define NEED_STRDUP
#define NEED_STRNCASECMP
- #define FNDELAY O_NDELAY
extern void GETPRIVMODE();
extern void GETUSERMODE();
extern char *inet_ntoa();
--- 71,76 ----
***************
*** 113,118 ****
--- 112,118 ----
#define HAVE_CRYPT_H
#define NO_LONG_DOUBLE
#define HAVE_BSTRING_H
+ #define NO_LINGCLOSE
#elif defined(HIUX)
#define HAVE_SYS_RESOURCE_H
***************
*** 198,203 ****
--- 198,204 ----
#undef NO_KILLPG
#define NO_SETSID
#define NEED_STRDUP
+ #define NO_LINGCLOSE
#define NO_UNISTD_H
#undef _POSIX_SOURCE
#ifndef FD_CLOEXEC
***************
*** 333,338 ****
--- 334,340 ----
#define USE_FCNTL_SERIALIZED_ACCEPT
#elif defined(UW)
+ #define NO_LINGCLOSE
#define NO_KILLPG
#undef NO_SETSID
#undef NEED_STRDUP
1.135 +168 -133 apache/src/http_main.c
Index: http_main.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_main.c,v
retrieving revision 1.134
retrieving revision 1.135
diff -C3 -r1.134 -r1.135
*** http_main.c 1997/04/06 07:43:39 1.134
--- http_main.c 1997/04/07 09:44:18 1.135
***************
*** 301,401 ****
#define accept_mutex_off()
#endif
- /*
- * More machine-dependant networking gooo... on some systems,
- * you've got to be *really* sure that all the packets are acknowledged
- * before closing the connection.
- */
-
- #ifndef NO_LINGCLOSE
- static void lingering_close (request_rec *r)
- {
- int dummybuf[512];
- struct timeval tv;
- fd_set lfds, fds_read, fds_err;
- int select_rv = 0, read_rv = 0;
- int lsd;
-
- /* Prevent a slow-drip client from holding us here indefinitely */
-
- hard_timeout("lingering_close", r);
-
- /* Send any leftover data to the client, but never try to again */
-
- bflush(r->connection->client);
- bsetflag(r->connection->client, B_EOUT, 1);
-
- /* Close our half of the connection --- send the client a FIN and
- * set the socket to non-blocking for later reads.
- */
- lsd = r->connection->client->fd;
-
- #ifdef MPE
- if (((shutdown(lsd, 1)) != 0) || (sfcntl(lsd, F_SETFL, FNDELAY) == -1))
{
- #else
- if (((shutdown(lsd, 1)) != 0) || (fcntl(lsd, F_SETFL, FNDELAY) == -1)) {
- #endif
- /* if it fails, no need to go through the rest of the routine */
- if (errno != ENOTCONN)
- log_unixerr("shutdown", NULL, "lingering_close", r->server);
- bclose(r->connection->client);
- kill_timeout(r);
- return;
- }
-
- /* Set up to wait for readable data on socket... */
-
- FD_ZERO(&lfds);
- FD_SET(lsd, &lfds);
-
- /* Wait for readable data or error condition on socket;
- * slurp up any data that arrives... We exit when we go for
- * an interval of tv length without getting any more data, get an
- * error from select(), get an exception on lsd, get an error or EOF
- * on a read, or the timer expires.
- */
-
- do {
- /* We use a 1 second timeout because current (Feb 97) browsers
- * fail to close a connection after the server closes it. Thus,
- * to avoid keeping the child busy, we are only lingering long
enough
- * for a client that is actively sending data on a connection.
- * This should be sufficient unless the connection is massively
- * losing packets, in which case we might have missed the RST
anyway.
- * These parameters are reset on each pass, since they might be
- * changed by select.
- */
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- read_rv = 0;
- fds_read = lfds;
- fds_err = lfds;
-
- #ifdef SELECT_NEEDS_CAST
- select_rv = select(lsd+1, (int*)&fds_read, NULL, (int*)&fds_err,
&tv);
- #else
- select_rv = select(lsd+1, &fds_read, NULL, &fds_err, &tv);
- #endif
- } while ((select_rv > 0) && /* Something to see on socket
*/
- !FD_ISSET(lsd, &fds_err) && /* that isn't an error condition
*/
- FD_ISSET(lsd, &fds_read) && /* and is worth trying to read
*/
- ((read_rv = read(lsd, dummybuf, sizeof dummybuf)) > 0));
-
- /* Log any errors that occurred (client close or reset is not an error)
*/
-
- if (select_rv < 0)
- log_unixerr("select", NULL, "lingering_close", r->server);
- else if (read_rv < 0 && errno != ECONNRESET)
- log_unixerr("read", NULL, "lingering_close", r->server);
-
- /* Should now have seen final ack. Safe to finally kill socket */
-
- bclose(r->connection->client);
-
- kill_timeout(r);
- }
- #endif /* ndef NO_LINGCLOSE */
-
void usage(char *bin)
{
fprintf(stderr,"Usage: %s [-d directory] [-f file] [-v] [-h]
[-l]\n",bin);
--- 301,306 ----
***************
*** 558,563 ****
--- 463,620 ----
}
}
+ /*
+ * More machine-dependent networking gooo... on some systems,
+ * you've got to be *really* sure that all the packets are acknowledged
+ * before closing the connection, since the client will not be able
+ * to see the last response if their TCP buffer is flushed by a RST
+ * packet from us, which is what the server's TCP stack will send
+ * if it receives any request data after closing the connection.
+ *
+ * In an ideal world, this function would be accomplished by simply
+ * setting the socket option SO_LINGER and handling it within the
+ * server's TCP stack while the process continues on to the next request.
+ * Unfortunately, it seems that most (if not all) operating systems
+ * block the server process on close() when SO_LINGER is used.
+ * For those that don't, see USE_SO_LINGER below. For the rest,
+ * we have created a home-brew lingering_close.
+ *
+ * Many operating systems tend to block, puke, or otherwise mishandle
+ * calls to shutdown only half of the connection. You should define
+ * NO_LINGCLOSE in conf.h if such is the case for your system.
+ */
+ #ifdef USE_SO_LINGER
+ #define NO_LINGCLOSE /* The two lingering options are exclusive */
+
+ static void sock_enable_linger (int s)
+ {
+ struct linger li;
+
+ li.l_onoff = 1;
+ li.l_linger = 30;
+
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER,
+ (char *)&li, sizeof(struct linger)) < 0) {
+ log_unixerr("setsockopt", "(SO_LINGER)", NULL, server_conf);
+ /* not a fatal error */
+ }
+ }
+
+ #else
+ #define sock_enable_linger(s) /* NOOP */
+ #endif /* USE_SO_LINGER */
+
+ #ifndef NO_LINGCLOSE
+
+ /* Special version of timeout for lingering_close */
+
+ static void lingerout(sig)
+ int sig;
+ {
+ if (alarms_blocked) {
+ alarm_pending = 1;
+ return;
+ }
+
+ if (!current_conn) {
+ #if defined(USE_LONGJMP)
+ longjmp(jmpbuffer,1);
+ #else
+ siglongjmp(jmpbuffer,1);
+ #endif
+ }
+ current_conn->aborted = 1;
+ }
+
+ static void linger_timeout ()
+ {
+ const int max_secs_to_linger = 30;
+
+ timeout_name = "lingering close";
+
+ signal(SIGALRM,(void (*)())lingerout);
+ alarm(max_secs_to_linger);
+ }
+
+ /* Since many clients will abort a connection instead of closing it,
+ * attempting to log an error message from this routine will only
+ * confuse the webmaster. There doesn't seem to be any portable way to
+ * distinguish between a dropped connection and something that might be
+ * worth logging.
+ */
+ static void lingering_close (request_rec *r)
+ {
+ int dummybuf[512];
+ struct timeval tv;
+ fd_set lfds, fds_read, fds_err;
+ int select_rv = 0, read_rv = 0;
+ int lsd;
+
+ /* Prevent a slow-drip client from holding us here indefinitely */
+
+ linger_timeout();
+
+ /* Send any leftover data to the client, but never try to again */
+
+ bflush(r->connection->client);
+ bsetflag(r->connection->client, B_EOUT, 1);
+
+ /* Close our half of the connection --- send the client a FIN */
+
+ lsd = r->connection->client->fd;
+
+ if ((shutdown(lsd, 1) != 0) || r->connection->aborted) {
+ kill_timeout(r);
+ bclose(r->connection->client);
+ return;
+ }
+
+ /* Set up to wait for readable data on socket... */
+
+ FD_ZERO(&lfds);
+ FD_SET(lsd, &lfds);
+
+ /* Wait for readable data or error condition on socket;
+ * slurp up any data that arrives... We exit when we go for
+ * an interval of tv length without getting any more data, get an
+ * error from select(), get an exception on lsd, get an error or EOF
+ * on a read, or the timer expires.
+ */
+
+ do {
+ /* We use a 2 second timeout because current (Feb 97) browsers
+ * fail to close a connection after the server closes it. Thus,
+ * to avoid keeping the child busy, we are only lingering long
enough
+ * for a client that is actively sending data on a connection.
+ * This should be sufficient unless the connection is massively
+ * losing packets, in which case we might have missed the RST
anyway.
+ * These parameters are reset on each pass, since they might be
+ * changed by select.
+ */
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ read_rv = 0;
+ fds_read = lfds;
+ fds_err = lfds;
+
+ #ifdef SELECT_NEEDS_CAST
+ select_rv = select(lsd+1, (int*)&fds_read, NULL, (int*)&fds_err,
&tv);
+ #else
+ select_rv = select(lsd+1, &fds_read, NULL, &fds_err, &tv);
+ #endif
+ } while ((select_rv > 0) && /* Something to see on socket
*/
+ !FD_ISSET(lsd, &fds_err) && /* that isn't an error condition
*/
+ FD_ISSET(lsd, &fds_read) && /* and is worth trying to read
*/
+ ((read_rv = read(lsd, dummybuf, sizeof dummybuf)) > 0));
+
+ /* Should now have seen final ack. Safe to finally kill socket */
+
+ bclose(r->connection->client);
+
+ kill_timeout(r);
+ }
+ #endif /* ndef NO_LINGCLOSE */
+
/*****************************************************************
*
* Dealing with the scoreboard... a lot of these variables are global
***************
*** 1566,1573 ****
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&just_say_no,
sizeof(int)) < 0) {
! perror ("setsockopt(TCP_NODELAY)");
! fprintf(stderr, "httpd: could not set socket option TCP_NODELAY\n");
}
}
#else
--- 1623,1629 ----
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&just_say_no,
sizeof(int)) < 0) {
! log_unixerr("setsockopt", "(TCP_NODELAY)", NULL, server_conf);
}
}
#else
***************
*** 1840,1902 ****
return 0;
}
! static int
! make_sock(pool *pconf, const struct sockaddr_in *server)
{
int s;
int one = 1;
if ((s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) {
! perror("socket");
! fprintf(stderr,"httpd: could not get socket\n");
exit(1);
}
! note_cleanups_for_fd (pconf, s); /* arrange to close on exec or restart
*/
#ifndef MPE
/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */
if (setsockopt(s, SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(int)) <
0) {
! perror("setsockopt(SO_REUSEADDR)");
! fprintf(stderr,"httpd: could not set socket option SO_REUSEADDR\n");
exit(1);
}
one = 1;
if (setsockopt(s, SOL_SOCKET,SO_KEEPALIVE,(char *)&one,sizeof(int)) <
0) {
! perror("setsockopt(SO_KEEPALIVE)");
! fprintf(stderr,"httpd: could not set socket option
SO_KEEPALIVE\n");
! exit(1);
}
#endif
sock_disable_nagle(s);
- #ifdef USE_SO_LINGER /* If puts don't complete, you could try this. */
- {
- /* Unfortunately, SO_LINGER causes problems as severe as it
- * cures on many of the affected systems; now trying the
- * lingering_close trick (see routine by that name above)
- * instead...
- */
- struct linger li;
- li.l_onoff = 1;
- li.l_linger = 900;
-
- if (setsockopt(s, SOL_SOCKET, SO_LINGER,
- (char *)&li, sizeof(struct linger)) < 0) {
- perror("setsockopt(SO_LINGER)");
- fprintf(stderr,"httpd: could not set socket option SO_LINGER\n");
- exit(1);
- }
- }
- #endif /* USE_SO_LINGER */
-
/*
* To send data over high bandwidth-delay connections at full
! * speed we must the TCP window to open wide enough to keep the
! * pipe full. Default the default window size on many systems
* is only 4kB. Cross-country WAN connections of 100ms
! * at 1Mb/s are not impossible for well connected sites in 1995.
* If we assume 100ms cross-country latency,
* a 4kB buffer limits throughput to 40kB/s.
*
--- 1896,1936 ----
return 0;
}
! static int make_sock(pool *pconf, const struct sockaddr_in *server)
{
int s;
int one = 1;
if ((s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) {
! log_unixerr("socket", NULL, "Failed to get a socket, exiting child",
! server_conf);
exit(1);
}
! note_cleanups_for_fd(pconf, s); /* arrange to close on exec or restart
*/
#ifndef MPE
/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */
if (setsockopt(s, SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(int)) <
0) {
! log_unixerr("setsockopt", "(SO_REUSEADDR)", NULL, server_conf);
exit(1);
}
one = 1;
if (setsockopt(s, SOL_SOCKET,SO_KEEPALIVE,(char *)&one,sizeof(int)) <
0) {
! log_unixerr("setsockopt", "(SO_KEEPALIVE)", NULL, server_conf);
! exit(1);
}
#endif
sock_disable_nagle(s);
+ sock_enable_linger(s);
/*
* To send data over high bandwidth-delay connections at full
! * speed we must force the TCP window to open wide enough to keep the
! * pipe full. The default window size on many systems
* is only 4kB. Cross-country WAN connections of 100ms
! * at 1Mb/s are not impossible for well connected sites.
* If we assume 100ms cross-country latency,
* a 4kB buffer limits throughput to 40kB/s.
*
***************
*** 1908,1921 ****
*
* -John Heidemann <[EMAIL PROTECTED]> 25-Oct-96
*
- *
* If no size is specified, use the kernel default.
*/
if (server_conf->send_buffer_size) {
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
(char *)&server_conf->send_buffer_size, sizeof(int)) < 0) {
! perror("setsockopt(SO_SNDBUF), using default buffer size");
! /* Fail soft. */
}
}
--- 1942,1956 ----
*
* -John Heidemann <[EMAIL PROTECTED]> 25-Oct-96
*
* If no size is specified, use the kernel default.
*/
if (server_conf->send_buffer_size) {
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
(char *)&server_conf->send_buffer_size, sizeof(int)) < 0) {
! log_unixerr("setsockopt", "(SO_SNDBUF)",
! "Failed to set SendBufferSize, using default",
! server_conf);
! /* not a fatal error */
}
}