dgaudet     99/06/24 00:29:33

  Modified:    mpm/src/include buff.h
               mpm/src/main buff.c http_protocol.c iol_unix.c
               mpm/src/modules/mpm/prefork prefork.c
  Log:
  new-fangled BUFF... this could easily be broken, but hey, the one that
  was in here before was broken in different ways... I've served up a few
  pages with this one.
  
  Revision  Changes    Path
  1.4       +38 -6     apache-2.0/mpm/src/include/buff.h
  
  Index: buff.h
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/include/buff.h,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- buff.h    1999/06/18 23:34:58     1.3
  +++ buff.h    1999/06/24 07:29:29     1.4
  @@ -65,6 +65,37 @@
   #include <stdarg.h>
   #include "ap_iol.h"
   
  +/*
  +    A BUFF is an i/o object which can be used in any of the following
  +    output modes:
  +
  +    blocking, buffered
  +    blocking, buffered, HTTP-chunked
  +    blocking, unbuffered
  +    blocking, unbuffered, HTTP-chunked
  +    non-blocking, unbuffered
  +    non-blocking, unbuffered, HTTP-chunked
  +
  +    In all the blocking modes, a bwrite(fb, buf, len) will return less
  +    than len only in an error state.  The error may be deferred until
  +    the next use of fb.
  +
  +    In the non-blocking, chunked mode, the caller of bwrite() makes a
  +    guarantee that if a partial write occurs, they will call back later
  +    with at least as many bytes to write -- prior to disabling chunking.
  +    This is a protocol correctness requirement -- the chunk length may
  +    already have hit the wire, and is in essence "committed".
  +
  +    bputc, bputs, bvputs, and bprintf are supported only in the buffered
  +    modes.
  +
  +    The following input modes are supported:
  +
  +    blocking, buffered
  +    blocking, unbuffered
  +    non-blocking, unbuffered
  +*/
  +
   /* Reading is buffered */
   #define B_RD     (1)
   /* Writing is buffered */
  @@ -82,16 +113,12 @@
   #undef B_ERROR
   #endif
   #define B_ERROR (48)
  -/* TODO: implement chunked encoding as a layer */
  +/* Use chunked writing */
  +#define B_CHUNK (64)
   /* bflush() if a read would block */
   /* TODO: #define B_SAFEREAD (128) */
  -/* buffer is a socket */
  -#define B_SOCKET (256)
  -
   /* caller expects non-blocking behaviour */
   #define B_NONBLOCK (512)
  -/* non-blocking bit set on fd */
  -#define B_NONBLOCK_SET (1024)
   
   /* TODO: implement a ebcdic/ascii conversion layers */
   
  @@ -103,10 +130,15 @@
       unsigned char *inptr;    /* pointer to next location to read */
       int incnt;                       /* number of bytes left to read from 
input buffer;
                                 * always 0 if had a read error  */
  +    int outchunk;               /* location of chunk header when chunking */
       int outcnt;                      /* number of byte put in output buffer 
*/
       unsigned char *inbase;
       unsigned char *outbase;
       int bufsiz;
  +    int chunk_overcommit;    /* when we start a chunk and get a partial 
write we
  +                              * keep track of the #remaining bytes in the 
chunk
  +                              * here
  +                              */
       void (*error) (BUFF *fb, int op, void *data);
       void *error_data;
       long int bytes_sent;     /* number of bytes actually written */
  
  
  
  1.5       +266 -143  apache-2.0/mpm/src/main/buff.c
  
  Index: buff.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/main/buff.c,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- buff.c    1999/06/20 12:25:54     1.4
  +++ buff.c    1999/06/24 07:29:31     1.5
  @@ -76,6 +76,17 @@
   #define DEFAULT_BUFSIZE (4096)
   #endif
   
  +/* the maximum size of any chunk */
  +#ifndef MAX_CHUNK_SIZE
  +#define MAX_CHUNK_SIZE (0x8000)
  +#endif
  +
  +/* This must be enough to represent MAX_CHUNK_SIZE in hex,
  + * plus two extra characters.
  + */
  +#ifndef CHUNK_HEADER_SIZE
  +#define CHUNK_HEADER_SIZE (6)
  +#endif
   
   /* bwrite()s of greater than this size can result in a large_write() call,
    * which can result in a writev().  It's a little more work to set up the
  @@ -88,7 +99,6 @@
   #define LARGE_WRITE_THRESHOLD 31
   #endif
   
  -
   /*
    * Buffered I/O routines.
    * These are a replacement for the stdio routines.
  @@ -136,15 +146,18 @@
       fb = ap_palloc(p, sizeof(BUFF));
       fb->pool = p;
       fb->bufsiz = DEFAULT_BUFSIZE;
  -    fb->flags = flags & (B_RDWR | B_SOCKET);
  +    fb->flags = flags & B_RDWR;
   
       if (flags & B_RD)
        fb->inbase = ap_palloc(p, fb->bufsiz);
       else
        fb->inbase = NULL;
   
  +    /* overallocate so that we can put a chunk trailer of CRLF into this
  +     * buffer... and possibly the beginning of a new chunk
  +     */
       if (flags & B_WR)
  -     fb->outbase = ap_palloc(p, fb->bufsiz);
  +     fb->outbase = ap_palloc(p, fb->bufsiz + 2 + CHUNK_HEADER_SIZE + 1);
       else
        fb->outbase = NULL;
   
  @@ -152,8 +165,10 @@
   
       fb->incnt = 0;
       fb->outcnt = 0;
  +    fb->outchunk = -1;
       fb->error = NULL;
       fb->bytes_sent = 0;
  +    fb->chunk_overcommit = 0;
   
       return fb;
   }
  @@ -183,6 +198,11 @@
        return 0;
   
       case BO_TIMEOUT:
  +     fb->flags &= ~B_NONBLOCK;
  +     if (optval == 0) {
  +         fb->flags |= B_NONBLOCK;
  +         /* XXX: should remove B_WR now... */
  +     }
        return iol_setopt(&fb->iol, AP_IOL_TIMEOUT, optval);
       }
       errno = EINVAL;
  @@ -207,19 +227,82 @@
       errno = EINVAL;
       return -1;
   }
  +
  +static void start_chunk(BUFF *fb)
  +{
  +    fb->outchunk = fb->outcnt;
  +    fb->outcnt += CHUNK_HEADER_SIZE;
  +}
  +
  +static int end_chunk(BUFF *fb, int extra)
  +{
  +    int i;
  +    unsigned char *strp;
  +    int chunk_size;
  +
  +    chunk_size = fb->outcnt - fb->outchunk - CHUNK_HEADER_SIZE + extra;
  +    if (chunk_size == 0) {
  +        /* nothing was written into this chunk, and we can't write a 0 size
  +         * chunk because that signifies EOF, so just erase it
  +         */
  +        fb->outcnt = fb->outchunk;
  +        fb->outchunk = -1;
  +        return 0;
  +    }
  +
  +    if (chunk_size > MAX_CHUNK_SIZE) {
  +     extra -= chunk_size - MAX_CHUNK_SIZE;
  +     chunk_size = MAX_CHUNK_SIZE;
  +    }
  +
  +    /* we know this will fit because of how we wrote it in start_chunk() */
  +    i = ap_snprintf((char *) &fb->outbase[fb->outchunk], CHUNK_HEADER_SIZE,
  +                "%x", chunk_size);
  +
  +    /* we may have to tack some trailing spaces onto the number we just wrote
  +     * in case it was smaller than our estimated size.  We've also written
  +     * a \0 into the buffer with ap_snprintf so we might have to put a
  +     * \r back in.
  +     */
  +    strp = &fb->outbase[fb->outchunk + i];
  +    while (i < CHUNK_HEADER_SIZE - 2) {
  +        *strp++ = ' ';
  +        ++i;
  +    }
  +    *strp++ = '\015';
  +    *strp = '\012';
  +
  +    /* tack on the trailing CRLF, we've reserved room for this */
  +    fb->outbase[fb->outcnt++] = '\015';
  +    fb->outbase[fb->outcnt++] = '\012';
  +
  +    fb->outchunk = -1;
  +    
  +    return extra;
  +}
   
  -static int bflush_core(BUFF *fb);
   
  +
   /*
    * Set a flag on (1) or off (0).
    */
   API_EXPORT(int) ap_bsetflag(BUFF *fb, int flag, int value)
   {
  +    int old_flags = fb->flags;
  +
       if (value) {
        fb->flags |= flag;
  +     /* start chunking if we haven't already */
  +        if ((flag ^ old_flags) & B_CHUNK) {
  +            start_chunk(fb);
  +        }
       }
       else {
        fb->flags &= ~flag;
  +     /* stop chunking if we haven't already */
  +        if ((flag ^ old_flags) & B_CHUNK) {
  +            end_chunk(fb, 0);
  +        }
       }
       return value;
   }
  @@ -236,7 +319,7 @@
       }
       else if (rv == -1) {
        fb->saved_errno = errno;
  -     if (errno != EWOULDBLOCK) {
  +     if (errno != EAGAIN) {
            doerror(fb, B_RD);
        }
       }
  @@ -443,14 +526,14 @@
        return buf[0];
   }
   
  -/* A wrapper for write which deals with error conditions and
  +/* A wrapper for writev which deals with error conditions and
    * bytes_sent.
    */
  -static int write_with_errors(BUFF *fb, const void *buf, int nbyte)
  +static int writev_with_errors(BUFF *fb, const struct iovec *vec, int nvec)
   {
       int rv;
   
  -    rv = iol_write(&fb->iol, buf, nbyte);
  +    rv = iol_writev(&fb->iol, vec, nvec);
       if (rv == -1) {
        fb->saved_errno = errno;
        if (errno != EAGAIN) {
  @@ -458,22 +541,144 @@
        }
        return -1;
       }
  -    else if (rv == 0) {
  -     errno = EAGAIN;
  -     return -1;
  -    }
       fb->bytes_sent += rv;
       return rv;
   }
   
  -/* A wrapper for writev which deals with error conditions and
  +
  +static int writev_it_all(BUFF *fb, struct iovec *vec, int nvec)
  +{
  +    int i;
  +    int rv;
  +    int total;
  +
  +    i = 0;
  +    total = 0;
  +    while (i < nvec) {
  +     rv = writev_with_errors(fb, vec, 2);
  +     if (rv < 0) {
  +         return total ? -1 : total;
  +     }
  +     total += rv;
  +     if (fb->flags & B_NONBLOCK) {
  +         return total;
  +     }
  +     /* recalculate to deal with partial writes */
  +     while (rv > 0) {
  +            if (rv < vec[i].iov_len) {
  +                vec[i].iov_base = (char *) vec[i].iov_base + rv;
  +                vec[i].iov_len -= rv;
  +                break;
  +            }
  +            else {
  +                rv -= vec[i].iov_len;
  +                ++i;
  +            }
  +        }
  +    }
  +    return total;
  +}
  +
  +
  +/* write the contents of fb->outbase, and buf,
  +   stop at first partial write for a non-blocking buff
  +
  +   return number of bytes of buf which were written
  +   -1 for errors
  +*/
  +static int large_write(BUFF *fb, const char *buf, int nbyte)
  +{
  +    struct iovec vec[2];
  +    int nvec;
  +    int rv;
  +
  +    ap_assert(nbyte > 0);
  +    if (fb->outcnt) {
  +     vec[0].iov_base = fb->outbase;
  +     vec[0].iov_len = fb->outcnt;
  +     vec[1].iov_base = (void *)buf;
  +     vec[1].iov_len = nbyte;
  +     nvec = 2;
  +    }
  +    else {
  +     vec[0].iov_base = (void *)buf;
  +     vec[0].iov_len = nbyte;
  +     nvec = 1;
  +    }
  +    rv = writev_it_all(fb, vec, nvec);
  +    if (rv <= 0) {
  +     return rv;
  +    }
  +    if (rv < fb->outcnt) {
  +     /* shift bytes forward in buffer */
  +     memmove(fb->outbase, fb->outbase + rv, fb->outcnt - rv);
  +     fb->outcnt -= rv;
  +     return 0;
  +    }
  +    rv -= fb->outcnt;
  +    fb->outcnt = 0;
  +    return rv;
  +}
  +
  +
  +static int large_write_chunk(BUFF *fb, const char *buf, int nbyte)
  +{
  +    int rv;
  +    int amt;
  +    int total;
  +
  +    ap_assert(nbyte > 0);
  +    if (fb->chunk_overcommit) {
  +     amt = nbyte > fb->chunk_overcommit ? fb->chunk_overcommit : nbyte;
  +     rv = large_write(fb, buf, amt);
  +     if (rv <= 0) {
  +         return rv;
  +     }
  +     fb->chunk_overcommit -= rv;
  +     if (fb->chunk_overcommit == 0) {
  +         fb->outbase[0] = '\015';
  +         fb->outbase[1] = '\012';
  +         fb->outcnt = 2;
  +         start_chunk(fb);
  +     }
  +     if (rv < amt || amt == nbyte) {
  +         return rv;
  +     }
  +     nbyte -= rv;
  +     buf += rv;
  +    }
  +    ap_assert(fb->chunk_overcommit == 0 && fb->outchunk != -1);
  +    total = 0;
  +    do {
  +     amt = end_chunk(fb, nbyte);
  +     rv = large_write(fb, buf, amt);
  +     if (rv < amt) {
  +         if (rv < 0) {
  +             fb->chunk_overcommit = amt;
  +             return total ? total : -1;
  +         }
  +         fb->chunk_overcommit = amt - rv;
  +         return total + rv;
  +     }
  +     fb->outbase[0] = '\015';
  +     fb->outbase[1] = '\012';
  +     fb->outcnt = 2;
  +     start_chunk(fb);
  +     nbyte -= amt;
  +     buf += amt;
  +     total += amt;
  +    } while (nbyte);
  +    return total;
  +}
  +
  +/* A wrapper for write which deals with error conditions and
    * bytes_sent.
    */
  -static int writev_with_errors(BUFF *fb, const struct iovec *vec, int nvec)
  +static int write_with_errors(BUFF *fb, const void *buf, int nbyte)
   {
       int rv;
   
  -    rv = iol_writev(&fb->iol, vec, nvec);
  +    rv = iol_write(&fb->iol, buf, nbyte);
       if (rv == -1) {
        fb->saved_errno = errno;
        if (errno != EAGAIN) {
  @@ -481,42 +686,36 @@
        }
        return -1;
       }
  -    else if (rv == 0) {
  -     errno = EAGAIN;
  -     return -1;
  -    }
       fb->bytes_sent += rv;
       return rv;
   }
   
   
  -/*
  - * Used to combine the contents of the fb buffer, and a large buffer
  - * passed in.  The return code is how many bytes of buf were written,
  - * or -1.
  - */
  -static int large_write(BUFF *fb, const void *buf, int nbyte)
  +static int bflush_core(BUFF *fb)
   {
  -    struct iovec vec[2];
  +    int total;
       int rv;
   
  -    vec[0].iov_base = (void *) fb->outbase;
  -    vec[0].iov_len = fb->outcnt;
  -    vec[1].iov_base = (void *) buf;
  -    vec[1].iov_len = nbyte;
  -    rv = writev_with_errors(fb, vec, 2);
  -    if (rv >= fb->outcnt) {
  -     rv -= fb->outcnt;
  -     fb->outcnt = 0;
  -     return rv;
  +    if (fb->flags & B_CHUNK) {
  +     end_chunk(fb, 0);
       }
  -    else if (rv > 0) {
  -     /* shift bytes forward in buffer */
  -     memmove(fb->outbase, fb->outbase + rv, fb->outcnt - rv);
  +    total = 0;
  +    while (fb->outcnt > 0) {
  +     rv = write_with_errors(fb, fb->outbase + total, fb->outcnt);
  +     if (rv <= 0) {
  +         if (total) {
  +             memmove(fb->outbase, fb->outbase + total, fb->outcnt);
  +             return total;
  +         }
  +         return -1;
  +     }
        fb->outcnt -= rv;
  -     return 0;
  +     total += rv;
       }
  -    return rv;
  +    if (fb->flags & B_CHUNK) {
  +     start_chunk(fb);
  +    }
  +    return total;
   }
   
   
  @@ -529,7 +728,8 @@
    */
   API_EXPORT(int) ap_bwrite(BUFF *fb, const void *buf, int nbyte)
   {
  -    int i, nwr;
  +    int amt;
  +    int total;
   
       if (fb->flags & (B_WRERR | B_EOUT)) {
        errno = fb->saved_errno;
  @@ -538,10 +738,6 @@
       if (nbyte == 0)
        return 0;
   
  -    if (!(fb->flags & B_WR)) {
  -     return write_with_errors(fb, buf, nbyte);
  -    }
  -
   /*
    * Detect case where we're asked to write a large buffer, and combine our
    * current buffer with it in a single writev().  Note we don't consider
  @@ -549,104 +745,31 @@
    * us to use writev() too frequently.  In those cases we really should just
    * start a new buffer.
    */
  -    if (fb->outcnt > 0 && nbyte > LARGE_WRITE_THRESHOLD
  -     && nbyte + fb->outcnt >= fb->bufsiz) {
  -     int n=large_write(fb, buf, nbyte);
  -     if (n == nbyte)
  -         return nbyte;
  -     buf+=n;
  -     nbyte-=n;
  -    }
  -
  -/*
  - * Whilst there is data in the buffer, keep on adding to it and writing it
  - * out
  - */
  -    nwr = 0;
  -    while (fb->outcnt > 0) {
  -/* can we accept some data? */
  -     i = fb->bufsiz - fb->outcnt;
  -     if (i > 0) {
  -         if (i > nbyte)
  -             i = nbyte;
  -         memcpy(fb->outbase + fb->outcnt, buf, i);
  -         fb->outcnt += i;
  -         nbyte -= i;
  -         buf = i + (const char *) buf;
  -         nwr += i;
  -         if (nbyte == 0)
  -             return nwr;     /* return if none left */
  -     }
  -
  -/* the buffer must be full */
  -     i = write_with_errors(fb, fb->outbase, fb->outcnt);
  -     if (i <= 0) {
  -         return nwr ? nwr : -1;
  -     }
  -
  -     /* deal with a partial write */
  -     if (i < fb->outcnt) {
  -         memmove(fb->outbase, fb->outbase + i, fb->outcnt - i);
  -         fb->outcnt -= i;
  -     }
  -     else
  -         fb->outcnt = 0;
  -
  -     if (fb->flags & B_EOUT)
  -         return -1;
  -    }
  -/* we have emptied the file buffer. Now try to write the data from the
  - * original buffer until there is less than bufsiz left.
  - */
  -    while (nbyte >= fb->bufsiz) {
  -     i = write_with_errors(fb, buf, nbyte);
  -     if (i <= 0) {
  -         return nwr ? nwr : -1;
  -     }
  -
  -     buf = i + (const char *) buf;
  -     nwr += i;
  -     nbyte -= i;
  -
  -     if (fb->flags & B_EOUT)
  -         return -1;
  -    }
  -/* copy what's left to the file buffer */
  -    /* assert(fb->outcnt == 0); */
  -    if (nbyte > 0)
  -     memcpy(fb->outbase, buf, nbyte);
  -    fb->outcnt = nbyte;
  -    nwr += nbyte;
  -    return nwr;
  -}
  -
  -
  -static int bflush_core(BUFF *fb)
  -{
  -    int i;
  -
  -    while (fb->outcnt > 0) {
  -     i = write_with_errors(fb, fb->outbase, fb->outcnt);
  -     if (i <= 0)
  -         return -1;
  -
  -     /*
  -      * We should have written all the data, but if the fd was in a
  -      * strange (non-blocking) mode, then we might not have done so.
  -      */
  -     if (i < fb->outcnt) {
  -         memmove(fb->outbase, fb->outbase + i, fb->outcnt - i);
  -     }
  -     fb->outcnt -= i;
  -
  -     /* If a soft timeout occurs while flushing, the handler should
  -      * have set the buffer flag B_EOUT.
  -      */
  -     if (fb->flags & B_EOUT)
  -         return -1;
  -    }
  -
  -    return 0;
  +    if (!(fb->flags & B_WR)
  +     || (nbyte > LARGE_WRITE_THRESHOLD && nbyte + fb->outcnt >= fb->bufsiz)) 
{
  +     if (fb->flags & B_CHUNK) {
  +         return large_write_chunk(fb, buf, nbyte);
  +     }
  +     return large_write(fb, buf, nbyte);
  +    }
  +
  +    /* at this point we know that nbyte < fb->bufsize */
  +    amt = fb->bufsiz - fb->outcnt;
  +    total = 0;
  +    if (nbyte > amt) {
  +     memcpy(fb->outbase + fb->outcnt, buf, amt);
  +     fb->outcnt += amt;
  +     buf = (const char *) buf + amt;
  +     nbyte -= amt;
  +     if (bflush_core(fb) < amt) {
  +         return amt;
  +     }
  +     total = amt;
  +    }
  +    /* now we know that nbyte < fb->bufsiz */
  +    memcpy(fb->outbase + fb->outcnt, buf, nbyte);
  +    fb->outcnt += nbyte;
  +    return total + nbyte;
   }
   
   /*
  
  
  
  1.6       +0 -4      apache-2.0/mpm/src/main/http_protocol.c
  
  Index: http_protocol.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/main/http_protocol.c,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- http_protocol.c   1999/06/20 11:19:46     1.5
  +++ http_protocol.c   1999/06/24 07:29:31     1.6
  @@ -1551,10 +1551,8 @@
       r->sent_bodyct = 1;         /* Whatever follows is real body stuff... */
   
       /* Set buffer flags for the body */
  -#if 0        /* TODO: implemented chunked layer */
       if (r->chunked)
           ap_bsetflag(r->connection->client, B_CHUNK, 1);
  -#endif
   #ifdef CHARSET_EBCDIC
       if (!convert)
           ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
  @@ -1573,9 +1571,7 @@
            * Turn off chunked encoding --- we can only do this once.
            */
           r->chunked = 0;
  -#if 0        /* TODO: implemented chunked layer */
           ap_bsetflag(r->connection->client, B_CHUNK, 0);
  -#endif
   
           ap_rputs("0\015\012", r);
           /* If we had footer "headers", we'd send them now */
  
  
  
  1.5       +1 -1      apache-2.0/mpm/src/main/iol_unix.c
  
  Index: iol_unix.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/main/iol_unix.c,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- iol_unix.c        1999/06/19 20:40:17     1.4
  +++ iol_unix.c        1999/06/24 07:29:31     1.5
  @@ -164,7 +164,7 @@
            if (set_nonblock(fd->fd)) { \
                return -1; \
            } \
  -         fd->flags |= B_NONBLOCK_SET; \
  +         fd->flags |= FD_NONBLOCKING_SET; \
        } \
    \
        /* try writing, ignoring EINTR, the upper layer has to handle \
  
  
  
  1.2       +1 -1      apache-2.0/mpm/src/modules/mpm/prefork/prefork.c
  
  Index: prefork.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/modules/mpm/prefork/prefork.c,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- prefork.c 1999/06/24 01:57:58     1.1
  +++ prefork.c 1999/06/24 07:29:32     1.2
  @@ -2630,7 +2630,7 @@
        (void) ap_update_child_status(my_child_num, SERVER_BUSY_READ,
                                   (request_rec *) NULL);
   
  -     conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
  +     conn_io = ap_bcreate(ptrans, B_RDWR);
   
   #ifdef B_SFIO
        (void) sfdisc(conn_io->sf_in, SF_POPDISC);
  
  
  

Reply via email to