fielding 97/01/29 18:43:02
Modified: src CHANGES buff.h buff.c http_main.c http_protocol.c
Log:
Improved buffered output to the client by delaying the flush decision
until the BUFF code is actually about to read the next request.
This fixes a problem introduced in 1.2b5 with clients that send
an extra CRLF after a POST request.
Submitted by: Dean Gaudet
Reviewed by: Roy Fielding
Revision Changes Path
1.140 +6 -1 apache/src/CHANGES
Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apache/src/CHANGES,v
retrieving revision 1.139
retrieving revision 1.140
diff -C3 -r1.139 -r1.140
*** CHANGES 1997/01/30 02:27:06 1.139
--- CHANGES 1997/01/30 02:42:56 1.140
***************
*** 1,6 ****
! Changes with Apache 1.2b6
*) Continue persistent connection after 304 response. [Dean Gaudet]
Changes with Apache 1.2b6
--- 1,11 ----
! Changes with Apache 1.2b7
*) Continue persistent connection after 304 response. [Dean Gaudet]
+
+ *) Improved buffered output to the client by delaying the flush decision
+ until the BUFF code is actually about to read the next request.
+ This fixes a problem introduced in 1.2b5 with clients that send
+ an extra CRLF after a POST request. [Dean Gaudet]
Changes with Apache 1.2b6
1.11 +2 -1 apache/src/buff.h
Index: buff.h
===================================================================
RCS file: /export/home/cvs/apache/src/buff.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -C3 -r1.10 -r1.11
*** buff.h 1997/01/25 22:37:09 1.10
--- buff.h 1997/01/30 02:42:57 1.11
***************
*** 68,73 ****
--- 68,75 ----
#define B_ERROR (48)
/* Use chunked writing */
#define B_CHUNK (64)
+ /* bflush() if a read would block */
+ #define B_SAFEREAD (128)
typedef struct buff_struct BUFF;
***************
*** 120,126 ****
extern int bvputs(BUFF *fb, ...);
extern int bprintf(BUFF *fb,const char *fmt,...);
extern int vbprintf(BUFF *fb,const char *fmt,va_list vlist);
- extern int btestread(BUFF *fb);
/* Internal routines */
extern int bflsbuf(int c, BUFF *fb);
--- 122,127 ----
1.16 +57 -49 apache/src/buff.c
Index: buff.c
===================================================================
RCS file: /export/home/cvs/apache/src/buff.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -C3 -r1.15 -r1.16
*** buff.c 1997/01/25 22:37:10 1.15
--- buff.c 1997/01/30 02:42:57 1.16
***************
*** 175,193 ****
}
/*
! * Set a flag on (1) or off (0). Currently, these flags work
! * as a function of the bcwrite() function, so we make sure to
! * flush before setting them one way or the other; otherwise
! * writes could end up with the wrong flag.
*/
int bsetflag(BUFF *fb, int flag, int value)
{
! bflush(fb);
if (value) fb->flags |= flag;
else fb->flags &= ~flag;
return value;
}
/*
* Read up to nbyte bytes into buf.
* If fewer than byte bytes are currently available, then return those.
--- 175,239 ----
}
/*
! * Set a flag on (1) or off (0).
*/
int bsetflag(BUFF *fb, int flag, int value)
{
! if( flag & B_CHUNK ) {
! /*
! * This shouldn't be true for much longer -djg
! * Currently, these flags work
! * as a function of the bcwrite() function, so we make sure to
! * flush before setting them one way or the other; otherwise
! * writes could end up with the wrong flag.
! */
! bflush(fb);
! }
if (value) fb->flags |= flag;
else fb->flags &= ~flag;
return value;
}
+
+ /*
+ * This is called instead of read() everywhere in here. It implements
+ * the B_SAFEREAD functionality -- which is to force a flush() if a read()
+ * would block. It also deals with the EINTR errno result from read().
+ * return code is like read() except EINTR is eliminated.
+ */
+ static int
+ saferead( BUFF *fb, void *buf, int nbyte )
+ {
+ int rv;
+
+ if( fb->flags & B_SAFEREAD ) {
+ fd_set fds;
+ struct timeval tv;
+
+ /* test for a block */
+ do {
+ FD_ZERO( &fds );
+ FD_SET( fb->fd_in, &fds );
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ #ifdef HPUX
+ rv = select( fb->fd_in + 1, (int *)&fds, NULL, NULL, &tv );
+ #else
+ rv = select( fb->fd_in + 1, &fds, NULL, NULL, &tv );
+ #endif
+ } while( rv < 0 && errno == EINTR );
+ /* treat any error as if it would block as well */
+ if( rv != 1 ) {
+ bflush(fb);
+ }
+ }
+ do {
+ rv = read( fb->fd_in, buf, nbyte );
+ } while ( rv == -1 && errno == EINTR );
+ return( rv );
+ }
+
+
/*
* Read up to nbyte bytes into buf.
* If fewer than byte bytes are currently available, then return those.
***************
*** 204,211 ****
if (!(fb->flags & B_RD))
{
/* Unbuffered reading */
! do i = read(fb->fd_in, buf, nbyte);
! while (i == -1 && errno == EINTR);
if (i == -1 && errno != EAGAIN) doerror(fb, B_RD);
return i;
}
--- 250,256 ----
if (!(fb->flags & B_RD))
{
/* Unbuffered reading */
! i = saferead( fb, buf, nbyte );
if (i == -1 && errno != EAGAIN) doerror(fb, B_RD);
return i;
}
***************
*** 233,240 ****
if (nbyte >= fb->bufsiz)
{
/* read directly into buffer */
! do i = read(fb->fd_in, buf, nbyte);
! while (i == -1 && errno == EINTR);
if (i == -1)
{
if (nrd == 0)
--- 278,284 ----
if (nbyte >= fb->bufsiz)
{
/* read directly into buffer */
! i = saferead( fb, buf, nbyte );
if (i == -1)
{
if (nrd == 0)
***************
*** 248,255 ****
{
/* read into hold buffer, then memcpy */
fb->inptr = fb->inbase;
! do i = read(fb->fd_in, fb->inptr, fb->bufsiz);
! while (i == -1 && errno == EINTR);
if (i == -1)
{
if (nrd == 0)
--- 292,298 ----
{
/* read into hold buffer, then memcpy */
fb->inptr = fb->inbase;
! i = saferead( fb, fb->inptr, fb->bufsiz );
if (i == -1)
{
if (nrd == 0)
***************
*** 310,317 ****
fb->inptr = fb->inbase;
fb->incnt = 0;
if (fb->flags & B_EOF) break;
! do i = read(fb->fd_in, fb->inptr, fb->bufsiz);
! while (i == -1 && errno == EINTR);
if (i == -1)
{
buff[ct] = '\0';
--- 353,359 ----
fb->inptr = fb->inbase;
fb->incnt = 0;
if (fb->flags & B_EOF) break;
! i = saferead( fb, fb->inptr, fb->bufsiz );
if (i == -1)
{
buff[ct] = '\0';
***************
*** 381,389 ****
if (fb->flags & B_EOF)
return 0;
! do {
! i = read(fb->fd_in, fb->inptr, fb->bufsiz);
! } while (i == -1 && errno == EINTR);
if (i == -1) {
if (errno != EAGAIN)
--- 423,429 ----
if (fb->flags & B_EOF)
return 0;
! i = saferead( fb, fb->inptr, fb->bufsiz );
if (i == -1) {
if (errno != EAGAIN)
***************
*** 402,438 ****
}
/*
- * Tests if there is data to be read. Returns 0 if there is data,
- * -1 otherwise.
- */
- int
- btestread(BUFF *fb)
- {
- fd_set fds;
- struct timeval tv;
- int rv;
-
- /* the simple case, we've already got data in the buffer */
- if( fb->incnt ) {
- return( 0 );
- }
-
- /* otherwise see if the descriptor would block if we try to read it */
- do {
- FD_ZERO( &fds );
- FD_SET( fb->fd_in, &fds );
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- #ifdef HPUX
- rv = select( fb->fd_in + 1, (int *)&fds, NULL, NULL, &tv );
- #else
- rv = select( fb->fd_in + 1, &fds, NULL, NULL, &tv );
- #endif
- } while( rv < 0 && errno == EINTR );
- return( rv == 1 ? 0 : -1 );
- }
-
- /*
* Skip data until a linefeed character is read
* Returns 1 on success, 0 if no LF found, or -1 on error
*/
--- 442,447 ----
***************
*** 464,471 ****
fb->inptr = fb->inbase;
fb->incnt = 0;
if (fb->flags & B_EOF) return 0;
! do i = read(fb->fd_in, fb->inptr, fb->bufsiz);
! while (i == -1 && errno == EINTR);
if (i == 0) fb->flags |= B_EOF;
if (i == -1 && errno != EAGAIN) doerror(fb, B_RD);
if (i == 0 || i == -1) return i;
--- 473,479 ----
fb->inptr = fb->inbase;
fb->incnt = 0;
if (fb->flags & B_EOF) return 0;
! i = saferead( fb, fb->inptr, fb->bufsiz );
if (i == 0) fb->flags |= B_EOF;
if (i == -1 && errno != EAGAIN) doerror(fb, B_RD);
if (i == 0 || i == -1) return i;
1.115 +0 -5 apache/src/http_main.c
Index: http_main.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_main.c,v
retrieving revision 1.114
retrieving revision 1.115
diff -C3 -r1.114 -r1.115
*** http_main.c 1997/01/26 21:11:53 1.114
--- http_main.c 1997/01/30 02:42:58 1.115
***************
*** 1675,1684 ****
if (r) increment_counts(child_num,r,1);
#endif
while (r && current_conn->keepalive) {
- /* If there's no request waiting for us to read then flush the
- * socket. This is to avoid tickling a bug in older Netscape
- * clients. */
- if( btestread(conn_io) == -1 ) bflush(conn_io);
destroy_pool(r->pool);
(void)update_child_status (child_num, SERVER_BUSY_KEEPALIVE,
(request_rec*)NULL);
--- 1675,1680 ----
***************
*** 2153,2159 ****
if (r) process_request (r); /* else premature EOF (ignore) */
while (r && conn->keepalive) {
- if( btestread(cio) == -1 ) bflush(cio);
destroy_pool(r->pool);
r = read_request (conn);
if (r) process_request (r);
--- 2149,2154 ----
1.95 +16 -1 apache/src/http_protocol.c
Index: http_protocol.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.c,v
retrieving revision 1.94
retrieving revision 1.95
diff -C3 -r1.94 -r1.95
*** http_protocol.c 1997/01/30 02:27:07 1.94
--- http_protocol.c 1997/01/30 02:42:58 1.95
***************
*** 520,530 ****
/* Read past empty lines until we get a real request line,
* a read error, the connection closes (EOF), or we timeout.
*/
while ((len = getline(l, HUGE_STRING_LEN, conn->client, 0)) <= 0) {
! if ((len < 0) || bgetflag(conn->client, B_EOF))
return 0;
}
if (len == (HUGE_STRING_LEN - 1))
return 0; /* Should be a 414 error status instead */
--- 520,545 ----
/* Read past empty lines until we get a real request line,
* a read error, the connection closes (EOF), or we timeout.
+ *
+ * We skip empty lines because browsers have to tack a CRLF on to the
end
+ * of POSTs to support old CERN webservers. But note that we may not
+ * have flushed any previous response completely to the client yet.
+ * We delay the flush as long as possible so that we can improve
+ * performance for clients that are pipelining requests. If a request
+ * is pipelined then we won't block during the (implicit) read() below.
+ * If the requests aren't pipelined, then the client is still waiting
+ * for the final buffer flush from us, and we will block in the implicit
+ * read(). B_SAFEREAD ensures that the BUFF layer flushes if it will
+ * have to block during a read.
*/
+ bsetflag( conn->client, B_SAFEREAD, 1 );
while ((len = getline(l, HUGE_STRING_LEN, conn->client, 0)) <= 0) {
! if ((len < 0) || bgetflag(conn->client, B_EOF)) {
! bsetflag( conn->client, B_SAFEREAD, 0 );
return 0;
+ }
}
+ bsetflag( conn->client, B_SAFEREAD, 0 );
if (len == (HUGE_STRING_LEN - 1))
return 0; /* Should be a 414 error status instead */