httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dgau...@locus.apache.org
Subject cvs commit: apache-2.0/src/main buff.c
Date Thu, 02 Mar 2000 09:45:08 GMT
dgaudet     00/03/02 01:45:08

  Modified:    src/main buff.c
  Log:
  fix chunked encoding.
  
  this pretty much assumes/requires the API semantic clarification
  i checked in previously to sendrecv.c -- which means we need to
  actually fix all those XXXs before this works perfectly right.
  
  specifically:  bytes_written/read is always correct, regardless of
  whether an error occured or not.  this further propagates this type
  of an API through buff.c -- gets rid of a few ifdefs manoj(?) left,
  and fixes some other bugs.
  
  plus i added a bunch of comments.
  
  this passes a very simple CGI that pumps out a bunch of 4000 byte
  packets.  (used src/test/check_chunked to verify the output).
  i haven't tried mod_test_chunk yet.
  
  Revision  Changes    Path
  1.32      +86 -52    apache-2.0/src/main/buff.c
  
  Index: buff.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/src/main/buff.c,v
  retrieving revision 1.31
  retrieving revision 1.32
  diff -u -r1.31 -r1.32
  --- buff.c	2000/02/21 16:41:41	1.31
  +++ buff.c	2000/03/02 09:45:07	1.32
  @@ -220,12 +220,27 @@
       return APR_EINVAL;
   }
   
  +/*
  +    start chunked encoding.
  +
  +    in order for ap_bputc() to be an efficient macro we need to have
  +    the buffer setup for the chunk already.  if B_CHUNK is set, then
  +    routines here using end_chunk() must be sure to call start_chunk()
  +    or set an error condition before they return to the caller.
  + */
   static void start_chunk(BUFF *fb)
   {
       fb->outchunk = fb->outcnt;
       fb->outcnt += CHUNK_HEADER_SIZE;
   }
   
  +/*
  +    we've got a chunk in progress which we want to end -- the caller
  +    possibly wants to write extra bytes beyond those in fb->outbase.
  +    if extra != 0 then the caller is assumed to write the chunk CRLF
  +    trailer before calling start_chunk again.  there's more info below
  +    at large_write_chunk().
  +*/
   static int end_chunk(BUFF *fb, int extra)
   {
       int i;
  @@ -247,7 +262,7 @@
   	chunk_size = MAX_CHUNK_SIZE;
       }
   
  -    /* we know this will fit because of how we wrote it in start_chunk() */
  +    /* we know this will fit because of space we saved in start_chunk() */
       i = ap_snprintf((char *) &fb->outbase[fb->outchunk], CHUNK_HEADER_SIZE,
                   "%x", chunk_size);
   
  @@ -264,9 +279,11 @@
       *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';
  +    if (extra == 0) {
  +	/* tack on the trailing CRLF, we've reserved room for this */
  +	fb->outbase[fb->outcnt++] = '\015';
  +	fb->outbase[fb->outcnt++] = '\012';
  +    }
   
       fb->outchunk = -1;
       
  @@ -382,11 +399,7 @@
   	rv = read_with_errors(fb, buf, nbyte, &i);
   	if (rv != APR_SUCCESS) {
               *bytes_read = nrd;
  -#ifdef MIDWAY_ERROR_RETURNS_ERROR_BLAH_BLAH_BLAH
               return rv;
  -#else
  -	    return *bytes_read ? APR_SUCCESS : rv;
  -#endif
   	}
       }
       else {
  @@ -395,11 +408,7 @@
   	rv = read_with_errors(fb, fb->inptr, fb->bufsiz, &i);
   	if (rv != APR_SUCCESS) {
               *bytes_read = nrd;
  -#ifdef MIDWAY_ERROR_RETURNS_ERROR_BLAH_BLAH_BLAH
               return rv;
  -#else
  -	    return *bytes_read ? APR_SUCCESS : rv;
  -#endif
   	}
   	fb->incnt = i;
   	if (i > nbyte)
  @@ -556,13 +565,13 @@
   {
       ap_status_t rv;
       rv = iol_writev(fb->iol, vec, nvec, bytes_written);
  +    fb->bytes_sent += *bytes_written;
       if (rv != APR_SUCCESS) {
   	fb->saved_errno = rv;
   	if (rv != APR_EAGAIN) {
   	    doerror(fb, B_WR);
   	}
       }
  -    fb->bytes_sent += *bytes_written;
       return rv;
   }
   
  @@ -578,16 +587,10 @@
       *bytes_written = 0;
       while (i < nvec) {
   	rv = writev_with_errors(fb, vec + i, nvec - i, &n);
  +	*bytes_written += n;
   	if (rv != APR_SUCCESS) {
  -/* XXX - When we decide which way to go here, the ifdef will do away. the macro
  - * is undefined by default */
  -#ifdef MIDWAY_ERROR_RETURNS_ERROR_BLAH_BLAH_BLAH
               return rv;
  -#else
  -            return *bytes_written ? APR_SUCCESS : rv;
  -#endif
   	}
  -	*bytes_written += n;
   	if (fb->flags & B_NONBLOCK) {
   	    return APR_SUCCESS;
   	}
  @@ -610,6 +613,9 @@
   
   /* write the contents of fb->outbase, and buf,
      stop at first partial write for a non-blocking buff
  +
  +   bytes_written is the number of bytes written from buf, it
  +   doesn't include any bytes potentially written from fb->outbase.
   */
   static ap_status_t large_write(BUFF *fb, const char *buf, ap_size_t nbyte,
                                  ap_ssize_t *bytes_written)
  @@ -632,22 +638,50 @@
   	nvec = 1;
       }
       rv = writev_it_all(fb, vec, nvec, bytes_written);
  -    if (*bytes_written == 0) {   /* error or eof */
  -        return rv;
  -    }
       if (*bytes_written < fb->outcnt) {
   	/* shift bytes forward in buffer */
   	fb->outcnt -= *bytes_written;
   	memmove(fb->outbase, fb->outbase + *bytes_written, fb->outcnt);
           *bytes_written = 0;
  -	return APR_SUCCESS;
  +	return rv;
       }
       *bytes_written -= fb->outcnt;
       fb->outcnt = 0;
  -    return APR_SUCCESS;
  +    return rv;
   }
   
   
  +/*
  +    large_write_chunk is similar to large_write(), but used when chunking
  +    is in effect.
  +
  +    in apache-1.3 we used bcwrite() and large_write() when writing large
  +    chunks, and we did so in a way that caused us to write at least two
  +    small buffers into the iovec -- one for the chunk size, and one for
  +    the trailing CRLF.  (we had iovecs of size 3 or 4 when chunking)
  +
  +    in 2.0 i wanted the ability to do non-blocking chunked i/o, which can
  +    result in partial writes at times.  this can be awfully inconvenient
  +    with the 1.3 method of chunking because there are a godawful number
  +    of special cases trying to remember how far into the iovec you got
  +    before EAGAIN.
  +
  +    the insight i found was that we can combine the CRLF end of chunk
  +    N with the size of chunk N+1.  i do this right in the fb->outbase
  +    buffer, because any partial write of this chunk end/start data is
  +    the same as a partial write of any other buffered data.  this gets
  +    rid of almost all the special cases, and probably works out to be
  +    faster than letting the kernel handle a 2 byte and a 6 byte buffer.
  +
  +    one special case is when we've told the client that we'd write a
  +    chunk of K bytes, and we get an EAGAIN before we write K bytes.
  +    i call this an "overcommit".  in this case we keep note of the fact
  +    that we've got an overcommit so that subsequent bwrites (which
  +    the layer above BUFF is performing in the non-blocking case) can
  +    decrement from the overcommit before they begin another chunk.
  +
  +    -djg
  +*/
   static ap_status_t large_write_chunk(BUFF *fb, const char *buf, ap_size_t nbyte,
                                        ap_ssize_t *bytes_written)
   {
  @@ -655,40 +689,41 @@
       ap_ssize_t total, n, amt;
   
       ap_assert(nbyte > 0);
  +    total = 0;
       if (fb->chunk_overcommit) {
   	amt = nbyte > fb->chunk_overcommit ? fb->chunk_overcommit : nbyte;
   	rv = large_write(fb, buf, amt, &total);
  -	if (total == 0) {       /* error or eof */
  -            *bytes_written = total;
  -            return rv;
  -	}
   	fb->chunk_overcommit -= total;
  -	if (fb->chunk_overcommit == 0) {
  -	    fb->outbase[0] = '\015';
  -	    fb->outbase[1] = '\012';
  -	    fb->outcnt = 2;
  -	    start_chunk(fb);
  +	if (rv != APR_SUCCESS) {
  +	    *bytes_written = total;
  +            return rv;
   	}
  -	if (total < amt || amt == nbyte) {
  -            *bytes_written = total;
  +	if (fb->chunk_overcommit) {
  +	    /* above we ensured that we'd write at most the overcommit
  +	       bytes -- so either we had a partial write,
  +	       or we used up all of buf.  either way we're done.  */
  +	    ap_assert(amt < fb->chunk_overcommit || *bytes_written < amt);
  +	    *bytes_written = total;
   	    return APR_SUCCESS;
   	}
  +	/* we've finished the overcommit so we end off the previous
  +	   chunk, begin a new one, and shift buf */
  +	fb->outbase[0] = '\015';
  +	fb->outbase[1] = '\012';
  +	fb->outcnt = 2;
  +	start_chunk(fb);
   	nbyte -= total;
   	buf += total;
       }
       ap_assert(fb->chunk_overcommit == 0 && fb->outchunk != -1);
  -    total = 0;
  -    do {
  +    while (nbyte) {
   	amt = end_chunk(fb, nbyte);
   	rv = large_write(fb, buf, amt, &n);
   	if (n < amt) {
  +	    /* can only be non-blocking or an error... */
   	    fb->chunk_overcommit = amt - n;
               *bytes_written = total + n;
  -#ifdef MIDWAY_ERROR_RETURNS_ERROR_BLAH_BLAH_BLAH
               return rv;
  -#else
  -            return *bytes_written ? APR_SUCCESS : rv;
  -#endif
   	}
   	fb->outbase[0] = '\015';
   	fb->outbase[1] = '\012';
  @@ -697,7 +732,7 @@
   	nbyte -= amt;
   	buf += amt;
   	total += amt;
  -    } while (nbyte);
  +    }
       *bytes_written = total;
       return APR_SUCCESS;
   }
  @@ -711,13 +746,13 @@
       ap_status_t rv;
   
       rv = iol_write(fb->iol, buf, nbyte, bytes_written);
  +    fb->bytes_sent += *bytes_written;
       if (rv != APR_SUCCESS) {
   	fb->saved_errno = rv;
   	if (rv != APR_EAGAIN) {
   	    doerror(fb, B_WR);
   	}
       }
  -    fb->bytes_sent += *bytes_written;
       return rv;
   }
   
  @@ -734,14 +769,9 @@
       while (fb->outcnt > 0) {
   	rv = write_with_errors(fb, fb->outbase + *bytes_written, fb->outcnt,
                                    &n);
  -	if (n <= 0) {       /* error or eof */
  +	if (rv != APR_SUCCESS) {
   	    if (*bytes_written) {
   		memmove(fb->outbase, fb->outbase + *bytes_written, fb->outcnt);
  -#ifdef MIDWAY_ERROR_RETURNS_ERROR_BLAH_BLAH_BLAH
  -                return rv;
  -#else
  -		return APR_SUCCESS;
  -#endif
   	    }
   	    return rv;
   	}
  @@ -768,6 +798,7 @@
       int amt;
       int total;
       ap_ssize_t n;
  +    ap_status_t rv;
   
       if (fb->flags & (B_WRERR | B_EOUT)) {
           *bytes_written = 0;
  @@ -803,8 +834,11 @@
   	fb->outcnt += amt;
   	buf = (const char *) buf + amt;
   	nbyte -= amt;
  -        (void) bflush_core(fb, &n);
  -	if (n < amt) {
  +        rv = bflush_core(fb, &n);
  +	if (rv != APR_SUCCESS) {
  +	    /* don't continue past an error or a partial write */
  +	    /* regardless of how much flushed we've at least copied amt
  +	       into our buffers... */
               *bytes_written = amt;
               return APR_SUCCESS;
   	}
  
  
  

Mime
View raw message