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);