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 */
    
  
  
  

Reply via email to