dgaudet 98/02/07 02:34:47
Modified: src/main buff.c Log: Sorry guys I know we're trying to get 1.3b4 rolled, but I really really really want chunking to work since it is affecting the deployment of HTTP/1.1. I've stressed this patch a fair amount with a module I'll check in shortly. Two more chunking bugs: - start_chunk() called bflush() called start_chunk() caused chaos - if we ended up in the tail of bwrite() where a memcpy happens to copy the remainder, in certain boundary cases with chunking we would go past the end of the buffer Just generally clean up chunking a bit. This would be a lot easier if chunking were just a layered I/O handler. Revision Changes Path 1.63 +56 -47 apache-1.3/src/main/buff.c Index: buff.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/buff.c,v retrieving revision 1.62 retrieving revision 1.63 diff -u -r1.62 -r1.63 --- buff.c 1998/02/03 20:00:58 1.62 +++ buff.c 1998/02/07 10:34:43 1.63 @@ -74,6 +74,13 @@ #ifndef DEFAULT_BUFSIZE #define DEFAULT_BUFSIZE (4096) #endif +/* This must be enough to represent (DEFAULT_BUFSIZE - 3) in hex, + * plus two extra characters. + */ +#ifndef CHUNK_HEADER_SIZE +#define CHUNK_HEADER_SIZE (5) +#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 @@ -381,6 +388,8 @@ } } +static int bflush_core(BUFF *fb); + /* * Start chunked encoding. * @@ -392,9 +401,6 @@ */ static void start_chunk(BUFF *fb) { - char chunksize[16]; /* Big enough for practically anything */ - int chunk_header_size; - if (fb->outchunk != -1) { /* already chunking */ return; @@ -404,26 +410,15 @@ return; } - /* we know that the chunk header is going to take at least 3 bytes... */ - chunk_header_size = ap_snprintf(chunksize, sizeof(chunksize), - "%x\015\012", fb->bufsiz - fb->outcnt - 3); /* we need at least the header_len + at least 1 data byte * remember that we've overallocated fb->outbase so that we can always * fit the two byte CRLF trailer */ - if (fb->bufsiz - fb->outcnt < chunk_header_size + 1) { - bflush(fb); + if (fb->bufsiz - fb->outcnt < CHUNK_HEADER_SIZE + 1) { + bflush_core(fb); } - /* assume there's enough space now */ -#ifdef CHARSET_EBCDIC - /* Chunks are an HTTP/1.1 Protocol feature. They must ALWAYS be in ASCII */ - ebcdic2ascii(&fb->outbase[fb->outcnt], chunksize, chunk_header_size); -#else /*CHARSET_EBCDIC*/ - memcpy(&fb->outbase[fb->outcnt], chunksize, chunk_header_size); -#endif /*CHARSET_EBCDIC*/ fb->outchunk = fb->outcnt; - fb->outcnt += chunk_header_size; - fb->outchunk_header_size = chunk_header_size; + fb->outcnt += CHUNK_HEADER_SIZE; } @@ -433,13 +428,14 @@ static void end_chunk(BUFF *fb) { int i; + char *strp; if (fb->outchunk == -1) { /* not chunking */ return; } - if (fb->outchunk + fb->outchunk_header_size == fb->outcnt) { + if (fb->outchunk + CHUNK_HEADER_SIZE == fb->outcnt) { /* nothing was written into this chunk, and we can't write a 0 size * chunk because that signifies EOF, so just erase it */ @@ -449,26 +445,24 @@ } /* we know this will fit because of how we wrote it in start_chunk() */ - i = ap_snprintf((char *) &fb->outbase[fb->outchunk], - fb->outchunk_header_size, - "%x", fb->outcnt - fb->outchunk - fb->outchunk_header_size); + i = ap_snprintf((char *) &fb->outbase[fb->outchunk], CHUNK_HEADER_SIZE, + "%x", fb->outcnt - fb->outchunk - CHUNK_HEADER_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. */ - i += fb->outchunk; - while (fb->outbase[i] != '\015' && fb->outbase[i] != '\012') { - fb->outbase[i++] = ' '; - } - if (fb->outbase[i] == '\012') { - /* we overwrote the \r, so put it back */ - fb->outbase[i - 1] = '\015'; + strp = &fb->outbase[fb->outchunk + i]; + while (i < CHUNK_HEADER_SIZE - 2) { + *strp++ = ' '; + ++i; } + *strp++ = '\015'; + *strp = '\012'; #ifdef CHARSET_EBCDIC /* Chunks are an HTTP/1.1 Protocol feature. They must ALWAYS be in ASCII */ - ebcdic2ascii(&fb->outbase[fb->outchunk], &fb->outbase[fb->outchunk], fb->outchunk_header_size); + ebcdic2ascii(&fb->outbase[fb->outchunk], &fb->outbase[fb->outchunk], CHUNK_HEADER_SIZE); #endif /*CHARSET_EBCDIC*/ /* tack on the trailing CRLF, we've reserved room for this */ @@ -1156,7 +1150,7 @@ */ API_EXPORT(int) bwrite(BUFF *fb, const void *buf, int nbyte) { - int i, nwr; + int i, nwr, useable_bufsiz; #ifdef CHARSET_EBCDIC static char *cbuf = NULL; static int csize = 0; @@ -1259,8 +1253,13 @@ * Note also that bcwrite never does a partial write if we're chunking, * so we're guaranteed to either end in an error state, or make it * out of this loop and call start_chunk() below. + * + * Remember we may not be able to use the entire buffer if we're + * chunking. */ - while (nbyte >= fb->bufsiz) { + useable_bufsiz = fb->bufsiz; + if (fb->flags & B_CHUNK) useable_bufsiz -= CHUNK_HEADER_SIZE; + while (nbyte >= useable_bufsiz) { i = bcwrite(fb, buf, nbyte); if (i <= 0) { return nwr ? nwr : -1; @@ -1284,23 +1283,11 @@ return nwr; } -/* - * Flushes the buffered stream. - * Returns 0 on success or -1 on error - */ -API_EXPORT(int) bflush(BUFF *fb) + +static int bflush_core(BUFF *fb) { int i; - if (!(fb->flags & B_WR) || (fb->flags & B_EOUT)) - return 0; - - if (fb->flags & B_WRERR) - return -1; - - if (fb->flags & B_CHUNK) - end_chunk(fb); - while (fb->outcnt > 0) { i = write_with_errors(fb, fb->outbase, fb->outcnt); if (i <= 0) @@ -1325,11 +1312,33 @@ return -1; } - if (fb->flags & B_CHUNK) { + return 0; +} + +/* + * Flushes the buffered stream. + * Returns 0 on success or -1 on error + */ +API_EXPORT(int) bflush(BUFF *fb) +{ + int ret; + + if (!(fb->flags & B_WR) || (fb->flags & B_EOUT)) + return 0; + + if (fb->flags & B_WRERR) + return -1; + + if (fb->flags & B_CHUNK) + end_chunk(fb); + + ret = bflush_core(fb); + + if (ret == 0 && (fb->flags & B_CHUNK)) { start_chunk(fb); } - return 0; + return ret; } /*