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