httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Jeff Trawick <trawi...@bellsouth.net>
Subject [PATCH] dechunking, body filtering
Date Sat, 14 Oct 2000 05:43:09 GMT
Okay, I didn't test body filtering but I think all the ducks are in a
row :)  An eos bucket marks the end of the request body.  Filters
between dechunk_filter() and ap_get_client_block() don't have to watch
out for lengths, so it doesn't seem any trickier than filtering the
body of a response.

Note that this is the product of a sleep-deprived person, but it seems
to work.  A few tweaks were made here and there to create one possible
implementation of dechunking and what I think is a simpler interface
for request body filters than worrying about length parameters.

--------------------------------------
A few notes, with varying levels of completeness...

X means still-to-do

Data structure changes

  http_filter's ctx:

    Add remaining field.  ap_setup_client_block() and dechunk_filter() know how to
    find this ctx and set this field.

  r
 
    Add "void *filtered_input" field; ap_get_client_block() holds onto 
    leftover body in this field.  Ugly that we clutter the r...  Ugly that
    I made it "void *" instead of "ap_bucket_brigade *" (avoids an additional
    #include in "httpd.h" for (probably) no good reason).

Code changes

ap_setup_client_block()

  remember that the read_policy parameter is copied to r->read_body;

  if read_policy is REQUEST_CHUNKED_PASS and the transport encoding is chunked...
 
    Disable filtering of the request body, because chunk headers would be invalid
    if the filter changed the length.
 
      r->input_filters = r->connection->input_filters;
 
    dechunk_filter() will look at the read policy to know whether or not to leave the
    chunk headers.

  if transport encoding is chunked, add dechunk_filter() to r->input_filters; we'll
  play games with the registered filter type so that it gets added at the right spot
  (not *my* problem :) )

  if transport encoding is not chunked (have content-length), set http_filter's
  ctx->remaining to the content length

ap_get_client_block()

  lots of logic goes away as we don't care how the body is presented

    recap:

      using content-length: ap_setup_client_block() told http_filter() everything
                            it needs to know

      using chunked encoding: ap_setup_client_block() inserted the dechunk filter

  on entry, if (!r->filtered_input) then allocate r->filtered_input (a brigade)

  eos will be present at the end of the body; that is the only way we know we've
  seen everything; if the filter below us gives us too much, we have to squirrel it
  away in the brigade r->filtered_input

http_filter()

  header logic is unchanged

  when ctx->remaining > 0...

    decrement ctx->remaining as we gather buckets to deliver to our caller;

    when ctx->remaining reaches zero, split as needed to avoid delivering part of
    the next request (or chunk header) as body; also, append an eos bucket to
    signify the end of the request body (or of the current chunk)

    as soon as we have data to deliver to the caller, do so...  we won't try
    to gather any certain amount before returning;

core's register_hooks()

    register the dechunk filter; play games with filter type so that it is
    added "properly"

dechunk_filter()

  This is in the r->input_filters list, not in the c->input_filters list.
 
  This is added by ap_setup_client_block() when the transport encoding is chunked.
  Ordinarily, it will remove the chunk headers/trailers from the stream of data.
  However, if the module wants to see them (REQUEST_CHUNKED_PASS), we'll leave them
  in.
 
  This decides what to do based on the state...
 
    waiting-for-chunk-header
 
      ensure that http_filter's ctx->remaining is zero, which tells http_filter() 
      to read a header, then call ap_get_brigade()
 
      when we read the header, get the length and store it in http_filter's
      ctx->remaining; 
 
      if the length is zero (final chunk), add an eos bucket to the brigade we
      pass to the caller; otherwise, set state to processing-body
 
    processing-body
 
      we just call ap_get_brigade(); http_filter() returns us body bytes; it
      will add an eos bucket after the last byte of the chunk; we always remove
      that eos bucket; once we see the eos bucket, we change he state to 
      waiting-for-chunk-trailer; http_filter's ctx->remaining should be zero
      at this point
 
    waiting-for-trailer
 
      http_filter() will return an empty line for this

joe_input_filter()

X call ap_get_next_brigade() to get data, of course...  we place no limitations
X on how much we get back and we feel free to return whatever is convenient to
X our caller
X
X we watch out for the eos bucket which signifies end of body
  
Not implemented yet:

X getting rid of the length parameters on ap_get_brigade() and the input filters (a
  trivial change)

X limiting the request body size; dechunk() needs to be part of this...      

X CORE_IN shouldn't deliver the whole request body if it is big... one I/O
  buffer worth of request body is plenty... otherwise we eat up virtual storage

Temporary hacks:

  ap_setup_client_block() allows chunked encoding on POST

Not-so-temporary hacks (i.e., I don't know how to fix it):

  There must be a nice way to avoid r->filtered_input (other than imposing
  length games on all input filters).

Known bugs:

X post 10000 bytes -- CGI sees 10002 bytes; the extra two are CR+LF at the end of
  the body; depends on whether or not I'm debugging it :(

Index: include/http_protocol.h
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/include/http_protocol.h,v
retrieving revision 1.28
diff -u -r1.28 http_protocol.h
--- include/http_protocol.h	2000/10/12 16:35:37	1.28
+++ include/http_protocol.h	2000/10/14 05:25:06
@@ -530,6 +530,7 @@
 API_EXPORT(const char *) ap_method_name_of(int methnum);
 
 int http_filter(ap_filter_t *f, ap_bucket_brigade *b, apr_ssize_t length);
+apr_status_t dechunk_filter(ap_filter_t *f, ap_bucket_brigade *b, apr_ssize_t length);
 
   /* Hooks */
   /*
Index: include/httpd.h
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/include/httpd.h,v
retrieving revision 1.101
diff -u -r1.101 httpd.h
--- include/httpd.h	2000/10/13 18:39:16	1.101
+++ include/httpd.h	2000/10/14 05:25:08
@@ -836,6 +836,9 @@
     /** A flag to determine if the eos bucket has been sent yet
      *  @defvar int eos_sent */
     int eos_sent;
+    /** ap_get_client_block() holds onto additional data here
+     *  @defvar voide *filtered_input */
+    void *filtered_input;
 
 /* Things placed at the end of the record to avoid breaking binary
  * compatibility.  It would be nice to remember to reorder the entire
Index: main/http_core.c
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/main/http_core.c,v
retrieving revision 1.168
diff -u -r1.168 http_core.c
--- main/http_core.c	2000/10/13 17:36:51	1.168
+++ main/http_core.c	2000/10/14 05:25:52
@@ -3582,6 +3582,7 @@
      */
     ap_hook_insert_filter(core_insert_filter, NULL, NULL, AP_HOOK_MIDDLE);
     ap_register_input_filter("HTTP_IN", http_filter, AP_FTYPE_CONNECTION);
+    ap_register_input_filter("DECHUNK", dechunk_filter, AP_FTYPE_CONNECTION + 1);
     ap_register_input_filter("CORE_IN", core_input_filter, AP_FTYPE_CONNECTION);
     ap_register_output_filter("CORE", core_output_filter, AP_FTYPE_CONNECTION + 1);
     ap_register_output_filter("CHUNK", chunk_filter, AP_FTYPE_CONNECTION);
Index: main/http_protocol.c
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/main/http_protocol.c,v
retrieving revision 1.169
diff -u -r1.169 http_protocol.c
--- main/http_protocol.c	2000/10/13 18:39:18	1.169
+++ main/http_protocol.c	2000/10/14 05:25:58
@@ -856,10 +856,147 @@
     return AP_HTTP_METHODS[methnum];
 }
 
+static apr_status_t brigade_bgets(ap_bucket_brigade *bb,
+                                  int remove_buckets,
+                                  char *line,
+                                  apr_ssize_t max_line_size)
+{
+    const char *buf;
+    apr_ssize_t len;
+    ap_bucket *e = AP_BRIGADE_FIRST(bb), *old;
+
+    while (e != AP_BRIGADE_SENTINEL(bb)) {
+        ap_bucket_read(e, &buf, &len, 0);
+        if (len + 2 >= max_line_size) { /* overflow */
+            return APR_EINVAL;          /* what a stupid choice */
+        }
+        memcpy(line, buf, len);
+        line += len;
+        max_line_size -= len;
+        old = e;
+        e = AP_BUCKET_NEXT(e);
+        if (remove_buckets) {
+            AP_BUCKET_REMOVE(old);
+            ap_bucket_destroy(old);
+        }
+    }
+    --line;            /* point at '\n' */
+    ap_debug_assert(*line == '\n');
+    if (*(line - 1) == '\r') {
+        --line;
+    }
+    *line = '\0';
+    return APR_SUCCESS;
+}
+
 typedef struct http_filter_ctx {
     ap_bucket_brigade *b;
+    apr_ssize_t remaining;
 } http_ctx_t;
 
+static http_ctx_t *find_http_ctx(conn_rec *c)
+{
+    ap_debug_assert(!strcmp(c->input_filters->frec->name, "HTTP_IN"));
+    return c->input_filters->ctx;
+}
+
+struct dechunk_ctx {
+    apr_ssize_t chunk_size;
+    apr_ssize_t bytes_delivered;
+    enum {WANT_HDR /* must have value zero */, WANT_BODY, WANT_TRL} state;
+    int remove_chunk_proto;
+};
+
+static long get_chunk_size(char *);
+
+apr_status_t dechunk_filter(ap_filter_t *f, ap_bucket_brigade *bb,
+                            apr_ssize_t length)
+{
+    apr_status_t rv;
+    struct dechunk_ctx *ctx = f->ctx;
+    ap_bucket *b;
+    const char *buf;
+    apr_ssize_t len;
+
+    if (!ctx) {
+        f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(struct dechunk_ctx));
+        ctx->remove_chunk_proto = f->r->read_body != REQUEST_CHUNKED_PASS;
+    }
+
+    do {
+        if (ctx->chunk_size == ctx->bytes_delivered) {
+            /* Time to read another chunk header or trailer...  http_filter() is 
+             * the next filter in line and it knows how to return a brigade with 
+             * one line.
+             */
+            char line[30];
+            
+            ap_debug_assert(!strcmp(f->next->frec->name, "HTTP_IN"));
+            if ((rv = ap_get_brigade(f->next, bb, AP_GET_LINE)) != APR_SUCCESS) {
+                return rv;
+            }
+            if ((rv = brigade_bgets(bb, ctx->remove_chunk_proto,
+                                    line, sizeof(line))) != APR_SUCCESS) {
+                return rv;
+            }
+            switch(ctx->state) {
+            case WANT_HDR:
+                ctx->chunk_size = get_chunk_size(line);
+                ctx->bytes_delivered = 0;
+                if (ctx->chunk_size == 0) {
+                    ctx->state = WANT_TRL;
+                }
+                else {
+                    ctx->state = WANT_BODY;
+                }
+                break;
+            case WANT_TRL:
+                /* XXX sanity check end chunk here */
+                if (strlen(line)) {
+                    /* bad trailer */
+                }
+                if (ctx->chunk_size == 0) { /* we just finished the last chunk? */
+                    /* append eos bucket and get out */
+                    b = ap_bucket_create_eos();
+                    AP_BRIGADE_INSERT_TAIL(bb, b);
+                    return APR_SUCCESS;
+                }
+                ctx->state = WANT_HDR;
+                break;
+            default:
+                ap_assert(ctx->state == WANT_HDR || ctx->state == WANT_TRL);
+            }
+        }
+    } while (ctx->state != WANT_BODY);
+
+    if (ctx->state == WANT_BODY) {
+        /* Tell http_filter() how many bytes to deliver. */
+        find_http_ctx(f->c)->remaining = ctx->chunk_size - ctx->bytes_delivered;
+        if ((rv = ap_get_brigade(f->next, bb, 999)) != APR_SUCCESS) {
+            return rv;
+        }
+        /* Walk through the body, accounting for bytes, and removing an eos bucket if
+         * http_filter() delivered the entire chunk.
+         */
+        b = AP_BRIGADE_FIRST(bb);
+        while (b != AP_BRIGADE_SENTINEL(bb) &&
+               b->type != ap_eos_type()) {
+            ap_bucket_read(b, &buf, &len, 1);
+            ap_debug_assert(len <= ctx->chunk_size - ctx->bytes_delivered);
+            ctx->bytes_delivered += len;
+            b = AP_BUCKET_NEXT(b);
+        }
+        if (ctx->bytes_delivered == ctx->chunk_size) {
+            ap_debug_assert(b->type == ap_eos_type());
+            AP_BUCKET_REMOVE(b);
+            ap_bucket_destroy(b);
+            ctx->state = WANT_TRL;
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
 apr_status_t http_filter(ap_filter_t *f, ap_bucket_brigade *b, apr_ssize_t length)
 {
 #define ASCII_LF '\012'
@@ -889,32 +1026,35 @@
         }
     }
 
-    if (length > 0) {
-        int remain = length;
+    if (ctx->remaining > 0) {
         const char *ignore;
 
         e = AP_BRIGADE_FIRST(b);
         while (e != AP_BRIGADE_SENTINEL(b)) {
             ap_bucket_read(e, &ignore, &len, 0);
-            if (remain <= len) {
+            if (ctx->remaining < len) {
                 break;
             }
-            remain -= len;
+            ctx->remaining -= len;
             e = AP_BUCKET_NEXT(e);
         }
         if (e != AP_BRIGADE_SENTINEL(b)) {
-            if (remain <= len) {
-                ap_bucket_split(e, remain);
-                remain = 0;
+            if (ctx->remaining < len) {
+                ap_bucket_split(e, ctx->remaining);
+                ctx->remaining = 0;
             }
             bb = ap_brigade_split(b, AP_BUCKET_NEXT(e));
             ctx->b = bb;
-            return APR_SUCCESS;
         }
         else {
             ctx->b = NULL;
-            return APR_SUCCESS;
         }
+        if (ctx->remaining == 0) {
+            ap_bucket *eos = ap_bucket_create_eos();
+
+            AP_BRIGADE_INSERT_TAIL(b, eos);
+        }
+        return APR_SUCCESS;
     }
 
     AP_BRIGADE_FOREACH(e, b) {
@@ -2269,6 +2409,11 @@
 			  "Unknown Transfer-Encoding %s", tenc);
             return HTTP_NOT_IMPLEMENTED;
         }
+/*XXX hack to use mod_cgi[d] for testing chunked request bodies XXX */
+        if (r->read_body == REQUEST_CHUNKED_ERROR) {
+            r->read_body = REQUEST_CHUNKED_DECHUNK;
+        }
+/*XXX end of this particular hack :) */
         if (r->read_body == REQUEST_CHUNKED_ERROR) {
             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
 			  "chunked Transfer-Encoding forbidden: %s", r->uri);
@@ -2276,6 +2421,11 @@
         }
 
         r->read_chunked = 1;
+        if (r->read_body == REQUEST_CHUNKED_PASS) {
+            /* can't filter the body *and* preserve the chunk headers */
+            r->input_filters = r->connection->input_filters;
+        }
+        ap_add_input_filter("DECHUNK", NULL, r, r->connection);
     }
     else if (lenp) {
         const char *pos = lenp;
@@ -2290,6 +2440,7 @@
         }
 
         r->remaining = atol(lenp);
+        find_http_ctx(r->connection)->remaining = r->remaining;
     }
 
     if ((r->read_body == REQUEST_NO_BODY) &&
@@ -2388,208 +2539,73 @@
  */
 API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz)
 {
-    int c;
-    apr_size_t len_to_read;
     apr_ssize_t len_read, total;
-    long chunk_start = 0;
-    long max_body;
     apr_status_t rv;
     apr_int32_t timeout;
     ap_bucket *b, *old;
     ap_bucket_brigade *bb;
-
-    if (!r->read_chunked) {     /* Content-length read */
-        const char *tempbuf;
+    const char *tempbuf;
 
-        len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
-
-        if (len_to_read == 0) {
-            return 0;
-        }
-        bb = ap_brigade_create(r->pool);
-        do {
-            if (AP_BRIGADE_EMPTY(bb)) {
-                apr_getsocketopt(r->connection->client->bsock, APR_SO_TIMEOUT, &timeout);
-                apr_setsocketopt(r->connection->client->bsock, APR_SO_TIMEOUT, 0);
-                if (ap_get_brigade(r->input_filters, bb, len_to_read) != APR_SUCCESS)
{
-                    /* if we actually fail here, we want to just return and
-                     * stop trying to read data from the client.
-                     */
-                    apr_setsocketopt(r->connection->client->bsock, APR_SO_TIMEOUT,
timeout);
-                    r->connection->keepalive = -1;
-                    ap_brigade_destroy(bb);
-                    return -1;
-                }
-                apr_setsocketopt(r->connection->client->bsock, APR_SO_TIMEOUT, timeout);
-            }
-            b = AP_BRIGADE_FIRST(bb);
-            
-            while (b->length == 0 && b != AP_BRIGADE_SENTINEL(bb)) {
-                ap_bucket *e = b;
-                b = AP_BUCKET_NEXT(e);
-                AP_BUCKET_REMOVE(e);
-                ap_bucket_destroy(e);
-            }
-        } while (AP_BRIGADE_EMPTY(bb));
-
-        total = 0;
-        do {
-            rv = ap_bucket_read(b, &tempbuf, &len_read, 0);
-            ap_debug_assert(total + len_read <= bufsiz); /* because we told the filter

-                                                          * below us not to give us too much
*/
-            ap_debug_assert(r->remaining >= len_read);
-            memcpy(buffer, tempbuf, len_read);
-            buffer += len_read;
-            r->read_length += len_read;
-            total += len_read;
-            r->remaining -= len_read;
-            old = b;
-            b = AP_BUCKET_NEXT(b);
-            AP_BUCKET_REMOVE(old);
-            ap_bucket_destroy(old);
-        } while (b != AP_BRIGADE_SENTINEL(bb));
-        ap_brigade_destroy(bb);
-        return total;
+    if (!r->filtered_input) {
+        r->filtered_input = ap_brigade_create(r->pool);
     }
+    bb = (ap_bucket_brigade *)r->filtered_input;
 
-    /*
-     * Handle chunked reading Note: we are careful to shorten the input
-     * bufsiz so that there will always be enough space for us to add a CRLF
-     * (if necessary).
-     */
-    if (r->read_body == REQUEST_CHUNKED_PASS)
-        bufsiz -= 2;
-    if (bufsiz <= 0)
-        return -1;              /* Cannot read chunked with a small buffer */
-
-    /* Check to see if we have already read too much request data.
-     * For efficiency reasons, we only check this at the top of each
-     * caller read pass, since the limit exists just to stop infinite
-     * length requests and nobody cares if it goes over by one buffer.
-     */
-    max_body = ap_get_limit_req_body(r);
-    if (max_body && (r->read_length > max_body)) {
-        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-		      "Chunked request body is larger than the "
-		      "configured limit of %lu", max_body);
-        r->connection->keepalive = -1;
-        return -1;
-    }
-
-    if (r->remaining == 0) {    /* Start of new chunk */
-
-        chunk_start = getline(buffer, bufsiz, r->connection, 0);
-        if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1))
-            || !apr_isxdigit(*buffer)) {
-            r->connection->keepalive = -1;
-            return -1;
-        }
-
-        len_to_read = get_chunk_size(buffer);
-
-        if (len_to_read == 0) { /* Last chunk indicated, get footers */
-            if (r->read_body == REQUEST_CHUNKED_DECHUNK) {
-                get_mime_headers(r);
-                apr_snprintf(buffer, bufsiz, "%ld", r->read_length);
-                apr_table_unset(r->headers_in, "Transfer-Encoding");
-                apr_table_setn(r->headers_in, "Content-Length",
-			       apr_pstrdup(r->pool, buffer));
-                return 0;
-            }
-            r->remaining = -1;  /* Indicate footers in-progress */
-        }
-        else {
-            r->remaining = len_to_read;
-        }
-        if (r->read_body == REQUEST_CHUNKED_PASS) {
-            buffer[chunk_start++] = CR; /* Restore chunk-size line end  */
-            buffer[chunk_start++] = LF;
-            buffer += chunk_start;      /* and pass line on to caller   */
-            bufsiz -= chunk_start;
-        }
-        else {
-            /* REQUEST_CHUNKED_DECHUNK -- do not include the length of the
-             * header in the return value
-             */
-            chunk_start = 0;
-        }
-    }
-                                /* When REQUEST_CHUNKED_PASS, we are */
-    if (r->remaining == -1) {   /* reading footers until empty line  */
-        len_read = chunk_start;
-
-        while ((bufsiz > 1)
-	       && ((len_read = getline(buffer, bufsiz, r->connection,
-				       1)) > 0)) {
-
-            if (len_read != (bufsiz - 1)) {
-                buffer[len_read++] = CR;        /* Restore footer line end  */
-                buffer[len_read++] = LF;
+    do {
+        if (AP_BRIGADE_EMPTY(bb)) {
+            apr_getsocketopt(r->connection->client->bsock, APR_SO_TIMEOUT, &timeout);
+            apr_setsocketopt(r->connection->client->bsock, APR_SO_TIMEOUT, 0);
+            if (ap_get_brigade(r->input_filters, bb, 9999) != APR_SUCCESS) {
+                /* if we actually fail here, we want to just return and
+                 * stop trying to read data from the client.
+                 */
+                apr_setsocketopt(r->connection->client->bsock, APR_SO_TIMEOUT, timeout);
+                r->connection->keepalive = -1;
+                ap_brigade_destroy(bb);
+                return -1;
             }
-            chunk_start += len_read;
-            buffer += len_read;
-            bufsiz -= len_read;
+            apr_setsocketopt(r->connection->client->bsock, APR_SO_TIMEOUT, timeout);
         }
-        if (len_read < 0) {
-            r->connection->keepalive = -1;
-            return -1;
+        b = AP_BRIGADE_FIRST(bb);
+        while (b->type != ap_eos_type() &&
+               b->length == 0 && b != AP_BRIGADE_SENTINEL(bb)) {
+            ap_bucket *e = b;
+            b = AP_BUCKET_NEXT(e);
+            AP_BUCKET_REMOVE(e);
+            ap_bucket_destroy(e);
         }
+    } while (AP_BRIGADE_EMPTY(bb));
 
-        if (len_read == 0) {    /* Indicates an empty line */
-            buffer[0] = CR;
-            buffer[1] = LF;
-            chunk_start += 2;
-            r->remaining = -2;
-        }
-        r->read_length += chunk_start;
-        return chunk_start;
-    }
-                                /* When REQUEST_CHUNKED_PASS, we     */
-    if (r->remaining == -2) {   /* finished footers when last called */
-        r->remaining = 0;       /* so now we must signal EOF         */
+    if (b->type == ap_eos_type()) {         /* reached eos on previous invocation */
+        AP_BUCKET_REMOVE(b);
+        ap_bucket_destroy(b);
         return 0;
     }
-
-    /* Otherwise, we are in the midst of reading a chunk of data */
-
-    len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
-
-    (void) ap_bread(r->connection->client, buffer, len_to_read, &len_read);
-    if (len_read == 0) {        /* error or eof */
-        r->connection->keepalive = -1;
-        return -1;
-    }
-
-    r->remaining -= len_read;
-
-    if (r->remaining == 0) {    /* End of chunk, get trailing CRLF */
-#ifdef APACHE_XLATE
-        /* Chunk end is Protocol stuff! Set conversion = 1 to read CR LF: */
-        AP_PUSH_INPUTCONVERSION_STATE(r->connection->client,
-                                      ap_hdrs_from_ascii);
-#endif /*APACHE_XLATE*/
-
-        if ((c = ap_bgetc(r->connection->client)) == CR) {
-            c = ap_bgetc(r->connection->client);
-        }
 
-#ifdef APACHE_XLATE
-        /* restore previous input translation handle */
-        AP_POP_INPUTCONVERSION_STATE(r->connection->client);
-#endif /*APACHE_XLATE*/
-
-        if (c != LF) {
-            r->connection->keepalive = -1;
+    total = 0;
+    while (total < bufsiz &&  b != AP_BRIGADE_SENTINEL(bb) && b->type
!= ap_eos_type()) {
+        if ((rv = ap_bucket_read(b, &tempbuf, &len_read, 0)) != APR_SUCCESS) {
             return -1;
         }
-        if (r->read_body == REQUEST_CHUNKED_PASS) {
-            buffer[len_read++] = CR;
-            buffer[len_read++] = LF;
-        }
+        if (total + len_read > bufsiz) {
+            ap_bucket_split(b, bufsiz - total);
+            len_read = bufsiz - total;
+        }
+        memcpy(buffer, tempbuf, len_read);
+        buffer += len_read;
+        total += len_read;
+        /* XXX the next two fields shouldn't be mucked with here, as they are in terms
+         * of bytes in the unfiltered body 
+         */
+        r->read_length += len_read;      /* XXX are these fields important? */
+        r->remaining -= len_read;        /* XXX if so, tell http_filter() where the r
is? */
+        old = b;
+        b = AP_BUCKET_NEXT(b);
+        AP_BUCKET_REMOVE(old);
+        ap_bucket_destroy(old);
     }
-    r->read_length += (chunk_start + len_read);
 
-    return (chunk_start + len_read);
+    return total;
 }
 
 /* In HTTP/1.1, any method can have a body.  However, most GET handlers


Off to catch some zzzs...

-- 
Jeff Trawick | trawick@ibm.net | PGP public key at web site:
     http://www.geocities.com/SiliconValley/Park/9289/
          Born in Roswell... married an alien...

Mime
View raw message