[nginx] svn commit: r4931 - trunk/src/http

mdounin at mdounin.ru mdounin at mdounin.ru
Wed Nov 21 01:08:11 UTC 2012


Author: mdounin
Date: 2012-11-21 01:08:11 +0000 (Wed, 21 Nov 2012)
New Revision: 4931
URL: http://trac.nginx.org/nginx/changeset/4931/nginx

Log:
Request body: chunked transfer encoding support.


Modified:
   trunk/src/http/ngx_http_core_module.c
   trunk/src/http/ngx_http_request.c
   trunk/src/http/ngx_http_request.h
   trunk/src/http/ngx_http_request_body.c

Modified: trunk/src/http/ngx_http_core_module.c
===================================================================
--- trunk/src/http/ngx_http_core_module.c	2012-11-21 01:06:53 UTC (rev 4930)
+++ trunk/src/http/ngx_http_core_module.c	2012-11-21 01:08:11 UTC (rev 4931)
@@ -848,7 +848,8 @@
             break;
         }
 
-        r->lingering_close = (r->headers_in.content_length_n > 0);
+        r->lingering_close = (r->headers_in.content_length_n > 0
+                              || r->headers_in.chunked);
         r->phase_handler = 0;
 
     } else {

Modified: trunk/src/http/ngx_http_request.c
===================================================================
--- trunk/src/http/ngx_http_request.c	2012-11-21 01:06:53 UTC (rev 4930)
+++ trunk/src/http/ngx_http_request.c	2012-11-21 01:08:11 UTC (rev 4931)
@@ -1574,19 +1574,11 @@
         if (r->headers_in.content_length_n == NGX_ERROR) {
             ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                           "client sent invalid \"Content-Length\" header");
-            ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
+            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
             return NGX_ERROR;
         }
     }
 
-    if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) {
-        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
-                  "client sent %V method without \"Content-Length\" header",
-                  &r->method_name);
-        ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
-        return NGX_ERROR;
-    }
-
     if (r->method & NGX_HTTP_TRACE) {
         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                       "client sent TRACE method");
@@ -1594,14 +1586,25 @@
         return NGX_ERROR;
     }
 
-    if (r->headers_in.transfer_encoding
-        && ngx_strcasestrn(r->headers_in.transfer_encoding->value.data,
-                           "chunked", 7 - 1))
-    {
-        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
-                      "client sent \"Transfer-Encoding: chunked\" header");
-        ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
-        return NGX_ERROR;
+    if (r->headers_in.transfer_encoding) {
+        if (r->headers_in.transfer_encoding->value.len == 7
+            && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
+                               (u_char *) "chunked", 7) == 0)
+        {
+            r->headers_in.content_length = NULL;
+            r->headers_in.content_length_n = -1;
+            r->headers_in.chunked = 1;
+
+        } else if (r->headers_in.transfer_encoding->value.len != 8
+            || ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
+                               (u_char *) "identity", 8) != 0)
+        {
+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                          "client sent unknown \"Transfer-Encoding\": \"%V\"",
+                          &r->headers_in.transfer_encoding->value);
+            ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED);
+            return NGX_ERROR;
+        }
     }
 
     if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {

Modified: trunk/src/http/ngx_http_request.h
===================================================================
--- trunk/src/http/ngx_http_request.h	2012-11-21 01:06:53 UTC (rev 4930)
+++ trunk/src/http/ngx_http_request.h	2012-11-21 01:08:11 UTC (rev 4931)
@@ -224,6 +224,7 @@
     time_t                            keep_alive_n;
 
     unsigned                          connection_type:2;
+    unsigned                          chunked:1;
     unsigned                          msie:1;
     unsigned                          msie6:1;
     unsigned                          opera:1;
@@ -276,7 +277,9 @@
     ngx_chain_t                      *bufs;
     ngx_buf_t                        *buf;
     off_t                             rest;
-    ngx_chain_t                      *to_write;
+    ngx_chain_t                      *free;
+    ngx_chain_t                      *busy;
+    ngx_http_chunked_t               *chunked;
     ngx_http_client_body_handler_pt   post_handler;
 } ngx_http_request_body_t;
 

Modified: trunk/src/http/ngx_http_request_body.c
===================================================================
--- trunk/src/http/ngx_http_request_body.c	2012-11-21 01:06:53 UTC (rev 4930)
+++ trunk/src/http/ngx_http_request_body.c	2012-11-21 01:08:11 UTC (rev 4931)
@@ -12,18 +12,21 @@
 
 static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
 static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
-static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r,
-    ngx_chain_t *body);
+static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
 static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
+static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
+    ngx_buf_t *b);
 static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
 
+static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,
+    ngx_chain_t *in);
+static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,
+    ngx_chain_t *in);
+static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,
+    ngx_chain_t *in);
+static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,
+    ngx_chain_t *in);
 
-/*
- * on completion ngx_http_read_client_request_body() adds to
- * r->request_body->bufs one or two bufs:
- *    *) one memory buf that was preread in r->header_in;
- *    *) one memory or file buf that contains the rest of the body
- */
 
 ngx_int_t
 ngx_http_read_client_request_body(ngx_http_request_t *r,
@@ -32,8 +35,7 @@
     size_t                     preread;
     ssize_t                    size;
     ngx_int_t                  rc;
-    ngx_buf_t                 *b;
-    ngx_chain_t               *cl, **next;
+    ngx_chain_t                out;
     ngx_http_request_body_t   *rb;
     ngx_http_core_loc_conf_t  *clcf;
 
@@ -55,39 +57,26 @@
         goto done;
     }
 
-    r->request_body = rb;
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     rb->bufs = NULL;
+     *     rb->buf = NULL;
+     *     rb->free = NULL;
+     *     rb->busy = NULL;
+     *     rb->chunked = NULL;
+     */
 
-    if (r->headers_in.content_length_n < 0) {
-        post_handler(r);
-        return NGX_OK;
-    }
+    rb->rest = -1;
+    rb->post_handler = post_handler;
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+    r->request_body = rb;
 
-    if (r->headers_in.content_length_n == 0) {
-
-        if (r->request_body_in_file_only) {
-            if (ngx_http_write_request_body(r, NULL) != NGX_OK) {
-                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
-                goto done;
-            }
-        }
-
+    if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
         post_handler(r);
-
         return NGX_OK;
     }
 
-    rb->post_handler = post_handler;
-
-    /*
-     * set by ngx_pcalloc():
-     *
-     *     rb->bufs = NULL;
-     *     rb->buf = NULL;
-     *     rb->rest = 0;
-     */
-
     preread = r->header_in->last - r->header_in->pos;
 
     if (preread) {
@@ -97,83 +86,62 @@
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http client request body preread %uz", preread);
 
-        b = ngx_calloc_buf(r->pool);
-        if (b == NULL) {
-            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
-            goto done;
-        }
+        out.buf = r->header_in;
+        out.next = NULL;
 
-        b->temporary = 1;
-        b->start = r->header_in->pos;
-        b->pos = r->header_in->pos;
-        b->last = r->header_in->last;
-        b->end = r->header_in->end;
+        rc = ngx_http_request_body_filter(r, &out);
 
-        rb->bufs = ngx_alloc_chain_link(r->pool);
-        if (rb->bufs == NULL) {
-            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        if (rc != NGX_OK) {
             goto done;
         }
 
-        rb->bufs->buf = b;
-        rb->bufs->next = NULL;
+        r->request_length += preread - (r->header_in->last - r->header_in->pos);
 
-        rb->buf = b;
+        if (!r->headers_in.chunked
+            && rb->rest > 0
+            && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
+        {
+            /* the whole request body may be placed in r->header_in */
 
-        if ((off_t) preread >= r->headers_in.content_length_n) {
+            rb->buf = r->header_in;
+            r->read_event_handler = ngx_http_read_client_request_body_handler;
 
-            /* the whole request body was pre-read */
+            rc = ngx_http_do_read_client_request_body(r);
+            goto done;
+        }
 
-            r->header_in->pos += (size_t) r->headers_in.content_length_n;
-            r->request_length += r->headers_in.content_length_n;
-            b->last = r->header_in->pos;
+    } else {
+        /* set rb->rest */
 
-            if (r->request_body_in_file_only) {
-                if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) {
-                    rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
-                    goto done;
-                }
-            }
-
-            post_handler(r);
-
-            return NGX_OK;
+        if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
+            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+            goto done;
         }
+    }
 
-        /*
-         * to not consider the body as pipelined request in
-         * ngx_http_set_keepalive()
-         */
-        r->header_in->pos = r->header_in->last;
+    if (rb->rest == 0) {
+        /* the whole request body was pre-read */
 
-        r->request_length += preread;
-
-        rb->rest = r->headers_in.content_length_n - preread;
-
-        if (rb->rest <= (off_t) (b->end - b->last)) {
-
-            /* the whole request body may be placed in r->header_in */
-
-            rb->to_write = rb->bufs;
-
-            r->read_event_handler = ngx_http_read_client_request_body_handler;
-
-            rc = ngx_http_do_read_client_request_body(r);
-            goto done;
+        if (r->request_body_in_file_only) {
+            if (ngx_http_write_request_body(r) != NGX_OK) {
+                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+                goto done;
+            }
         }
 
-        next = &rb->bufs->next;
+        post_handler(r);
 
-    } else {
-        b = NULL;
-        rb->rest = r->headers_in.content_length_n;
-        next = &rb->bufs;
+        return NGX_OK;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     size = clcf->client_body_buffer_size;
     size += size >> 2;
 
-    if (rb->rest < size) {
+    /* TODO: honor r->request_body_in_single_buf */
+
+    if (!r->headers_in.chunked && rb->rest < size) {
         size = (ssize_t) rb->rest;
 
         if (r->request_body_in_single_buf) {
@@ -182,9 +150,6 @@
 
     } else {
         size = clcf->client_body_buffer_size;
-
-        /* disable copying buffer for r->request_body_in_single_buf */
-        b = NULL;
     }
 
     rb->buf = ngx_create_temp_buf(r->pool, size);
@@ -193,32 +158,6 @@
         goto done;
     }
 
-    cl = ngx_alloc_chain_link(r->pool);
-    if (cl == NULL) {
-        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
-        goto done;
-    }
-
-    cl->buf = rb->buf;
-    cl->next = NULL;
-
-    if (b && r->request_body_in_single_buf) {
-        size = b->last - b->pos;
-        ngx_memcpy(rb->buf->pos, b->pos, size);
-        rb->buf->last += size;
-
-        next = &rb->bufs;
-    }
-
-    *next = cl;
-
-    if (r->request_body_in_file_only || r->request_body_in_single_buf) {
-        rb->to_write = rb->bufs;
-
-    } else {
-        rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
-    }
-
     r->read_event_handler = ngx_http_read_client_request_body_handler;
 
     rc = ngx_http_do_read_client_request_body(r);
@@ -255,9 +194,12 @@
 static ngx_int_t
 ngx_http_do_read_client_request_body(ngx_http_request_t *r)
 {
+    off_t                      rest;
     size_t                     size;
     ssize_t                    n;
+    ngx_int_t                  rc;
     ngx_buf_t                 *b;
+    ngx_chain_t               *cl, out;
     ngx_connection_t          *c;
     ngx_http_request_body_t   *rb;
     ngx_http_core_loc_conf_t  *clcf;
@@ -272,18 +214,44 @@
         for ( ;; ) {
             if (rb->buf->last == rb->buf->end) {
 
-                if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) {
+                /* pass buffer to request body filter chain */
+
+                out.buf = rb->buf;
+                out.next = NULL;
+
+                rc = ngx_http_request_body_filter(r, &out);
+
+                if (rc != NGX_OK) {
+                    return rc;
+                }
+
+                /* write to file */
+
+                if (ngx_http_write_request_body(r) != NGX_OK) {
                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
                 }
 
-                rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
+                /* update chains */
+
+                rc = ngx_http_request_body_filter(r, NULL);
+
+                if (rc != NGX_OK) {
+                    return rc;
+                }
+
+                if (rb->busy != NULL) {
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+                rb->buf->pos = rb->buf->start;
                 rb->buf->last = rb->buf->start;
             }
 
             size = rb->buf->end - rb->buf->last;
+            rest = rb->rest - (rb->buf->last - rb->buf->pos);
 
-            if ((off_t) size > rb->rest) {
-                size = (size_t) rb->rest;
+            if ((off_t) size > rest) {
+                size = (size_t) rest;
             }
 
             n = c->recv(c, rb->buf->last, size);
@@ -306,9 +274,21 @@
             }
 
             rb->buf->last += n;
-            rb->rest -= n;
             r->request_length += n;
 
+            if (n == rest) {
+                /* pass buffer to request body filter chain */
+
+                out.buf = rb->buf;
+                out.next = NULL;
+
+                rc = ngx_http_request_body_filter(r, &out);
+
+                if (rc != NGX_OK) {
+                    return rc;
+                }
+            }
+
             if (rb->rest == 0) {
                 break;
             }
@@ -345,34 +325,26 @@
 
         /* save the last part */
 
-        if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) {
+        if (ngx_http_write_request_body(r) != NGX_OK) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
-        b = ngx_calloc_buf(r->pool);
-        if (b == NULL) {
+        cl = ngx_chain_get_free_buf(r->pool, &rb->free);
+        if (cl == NULL) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
+        b = cl->buf;
+
+        ngx_memzero(b, sizeof(ngx_buf_t));
+
         b->in_file = 1;
-        b->file_pos = 0;
         b->file_last = rb->temp_file->file.offset;
         b->file = &rb->temp_file->file;
 
-        if (rb->bufs->next) {
-            rb->bufs->next->buf = b;
-
-        } else {
-            rb->bufs->buf = b;
-        }
+        rb->bufs = cl;
     }
 
-    if (rb->bufs->next
-        && (r->request_body_in_file_only || r->request_body_in_single_buf))
-    {
-        rb->bufs = rb->bufs->next;
-    }
-
     r->read_event_handler = ngx_http_block_reading;
 
     rb->post_handler(r);
@@ -382,15 +354,19 @@
 
 
 static ngx_int_t
-ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body)
+ngx_http_write_request_body(ngx_http_request_t *r)
 {
     ssize_t                    n;
+    ngx_chain_t               *cl;
     ngx_temp_file_t           *tf;
     ngx_http_request_body_t   *rb;
     ngx_http_core_loc_conf_t  *clcf;
 
     rb = r->request_body;
 
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http write client request body, bufs %p", rb->bufs);
+
     if (rb->temp_file == NULL) {
         tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
         if (tf == NULL) {
@@ -414,7 +390,7 @@
 
         rb->temp_file = tf;
 
-        if (body == NULL) {
+        if (rb->bufs == NULL) {
             /* empty body with r->request_body_in_file_only */
 
             if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
@@ -428,8 +404,12 @@
         }
     }
 
-    n = ngx_write_chain_to_temp_file(rb->temp_file, body);
+    if (rb->bufs == NULL) {
+        return NGX_OK;
+    }
 
+    n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
+
     /* TODO: n == 0 or not complete and level event */
 
     if (n == NGX_ERROR) {
@@ -438,6 +418,14 @@
 
     rb->temp_file->offset += n;
 
+    /* mark all buffers as written */
+
+    for (cl = rb->bufs; cl; cl = cl->next) {
+        cl->buf->pos = cl->buf->last;
+    }
+
+    rb->bufs = NULL;
+
     return NGX_OK;
 }
 
@@ -446,9 +434,10 @@
 ngx_http_discard_request_body(ngx_http_request_t *r)
 {
     ssize_t       size;
+    ngx_int_t     rc;
     ngx_event_t  *rev;
 
-    if (r != r->main || r->discard_body) {
+    if (r != r->main || r->discard_body || r->request_body) {
         return NGX_OK;
     }
 
@@ -464,20 +453,20 @@
         ngx_del_timer(rev);
     }
 
-    if (r->headers_in.content_length_n <= 0 || r->request_body) {
+    if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
         return NGX_OK;
     }
 
     size = r->header_in->last - r->header_in->pos;
 
     if (size) {
-        if (r->headers_in.content_length_n > size) {
-            r->header_in->pos += size;
-            r->headers_in.content_length_n -= size;
+        rc = ngx_http_discard_request_body_filter(r, r->header_in);
 
-        } else {
-            r->header_in->pos += (size_t) r->headers_in.content_length_n;
-            r->headers_in.content_length_n = 0;
+        if (rc != NGX_OK) {
+            return rc;
+        }
+
+        if (r->headers_in.content_length_n == 0) {
             return NGX_OK;
         }
     }
@@ -570,13 +559,19 @@
 static ngx_int_t
 ngx_http_read_discarded_request_body(ngx_http_request_t *r)
 {
-    size_t   size;
-    ssize_t  n;
-    u_char   buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
+    size_t     size;
+    ssize_t    n;
+    ngx_int_t  rc;
+    ngx_buf_t  b;
+    u_char     buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http read discarded body");
 
+    ngx_memzero(&b, sizeof(ngx_buf_t));
+
+    b.temporary = 1;
+
     for ( ;; ) {
         if (r->headers_in.content_length_n == 0) {
             r->read_event_handler = ngx_http_block_reading;
@@ -587,9 +582,8 @@
             return NGX_AGAIN;
         }
 
-        size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ?
-                   NGX_HTTP_DISCARD_BUFFER_SIZE:
-                   (size_t) r->headers_in.content_length_n;
+        size = (size_t) ngx_min(r->headers_in.content_length_n,
+                                NGX_HTTP_DISCARD_BUFFER_SIZE);
 
         n = r->connection->recv(r->connection, buffer, size);
 
@@ -606,12 +600,109 @@
             return NGX_OK;
         }
 
-        r->headers_in.content_length_n -= n;
+        b.pos = buffer;
+        b.last = buffer + n;
+
+        rc = ngx_http_discard_request_body_filter(r, &b);
+
+        if (rc != NGX_OK) {
+            r->connection->error = 1;
+            return NGX_OK;
+        }
     }
 }
 
 
 static ngx_int_t
+ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
+{
+    size_t                    size;
+    ngx_int_t                 rc;
+    ngx_http_request_body_t  *rb;
+
+    if (r->headers_in.chunked) {
+
+        rb = r->request_body;
+
+        if (rb == NULL) {
+
+            rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+            if (rb == NULL) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
+            if (rb == NULL) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            r->request_body = rb;
+        }
+
+        for ( ;; ) {
+
+            rc = ngx_http_parse_chunked(r, b, rb->chunked);
+
+            if (rc == NGX_OK) {
+
+                /* a chunk has been parsed successfully */
+
+                size = b->last - b->pos;
+
+                if ((off_t) size > rb->chunked->size) {
+                    b->pos += rb->chunked->size;
+                    rb->chunked->size = 0;
+
+                } else {
+                    rb->chunked->size -= size;
+                    b->pos = b->last;
+                }
+
+                continue;
+            }
+
+            if (rc == NGX_DONE) {
+
+                /* a whole response has been parsed successfully */
+
+                r->headers_in.content_length_n = 0;
+                break;
+            }
+
+            if (rc == NGX_AGAIN) {
+
+                /* set amount of data we want to see next time */
+
+                r->headers_in.content_length_n = rb->chunked->length;
+                break;
+            }
+
+            /* invalid */
+
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "client sent invalid chunked body");
+
+            return NGX_HTTP_BAD_REQUEST;
+        }
+
+    } else {
+        size = b->last - b->pos;
+
+        if ((off_t) size > r->headers_in.content_length_n) {
+            b->pos += r->headers_in.content_length_n;
+            r->headers_in.content_length_n = 0;
+
+        } else {
+            b->pos = b->last;
+            r->headers_in.content_length_n -= size;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_test_expect(ngx_http_request_t *r)
 {
     ngx_int_t   n;
@@ -651,3 +742,274 @@
 
     return NGX_ERROR;
 }
+
+
+static ngx_int_t
+ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    if (r->headers_in.chunked) {
+        return ngx_http_request_body_chunked_filter(r, in);
+
+    } else {
+        return ngx_http_request_body_length_filter(r, in);
+    }
+}
+
+
+static ngx_int_t
+ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    size_t                     size;
+    ngx_int_t                  rc;
+    ngx_buf_t                 *b;
+    ngx_chain_t               *cl, *tl, *out, **ll;
+    ngx_http_request_body_t   *rb;
+
+    rb = r->request_body;
+
+    if (rb->rest == -1) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http request body content length filter");
+
+        rb->rest = r->headers_in.content_length_n;
+    }
+
+    out = NULL;
+    ll = &out;
+
+    for (cl = in; cl; cl = cl->next) {
+
+        tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+        if (tl == NULL) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        b = tl->buf;
+
+        ngx_memzero(b, sizeof(ngx_buf_t));
+
+        b->temporary = 1;
+        b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
+        b->start = cl->buf->start;
+        b->pos = cl->buf->pos;
+        b->last = cl->buf->last;
+        b->end = cl->buf->end;
+
+        size = cl->buf->last - cl->buf->pos;
+
+        if ((off_t) size < rb->rest) {
+            cl->buf->pos = cl->buf->last;
+            rb->rest -= size;
+
+        } else {
+            cl->buf->pos += rb->rest;
+            rb->rest = 0;
+            b->last = cl->buf->pos;
+            b->last_buf = 1;
+        }
+
+        *ll = tl;
+        ll = &tl->next;
+    }
+
+    rc = ngx_http_request_body_save_filter(r, out);
+
+    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
+                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);
+
+    return rc;
+}
+
+
+static ngx_int_t
+ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    size_t                     size;
+    ngx_int_t                  rc;
+    ngx_buf_t                 *b;
+    ngx_chain_t               *cl, *out, *tl, **ll;
+    ngx_http_request_body_t   *rb;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    rb = r->request_body;
+
+    if (rb->rest == -1) {
+
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http request body chunked filter");
+
+        rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
+        if (rb->chunked == NULL) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        r->headers_in.content_length_n = 0;
+        rb->rest = 3;
+    }
+
+    out = NULL;
+    ll = &out;
+
+    for (cl = in; cl; cl = cl->next) {
+
+        for ( ;; ) {
+
+            ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+                           "http body chunked buf "
+                           "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z",
+                           cl->buf->temporary, cl->buf->in_file,
+                           cl->buf->start, cl->buf->pos,
+                           cl->buf->last - cl->buf->pos,
+                           cl->buf->file_pos,
+                           cl->buf->file_last - cl->buf->file_pos);
+
+            rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
+
+            if (rc == NGX_OK) {
+
+                /* a chunk has been parsed successfully */
+
+                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+                if (clcf->client_max_body_size
+                    && clcf->client_max_body_size
+                       < r->headers_in.content_length_n + rb->chunked->size)
+                {
+                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                  "client intended to send too large chunked "
+                                  "body: %O bytes",
+                                  r->headers_in.content_length_n
+                                  + rb->chunked->size);
+
+                    r->lingering_close = 1;
+
+                    return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+                }
+
+                tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+                if (tl == NULL) {
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+                b = tl->buf;
+
+                ngx_memzero(b, sizeof(ngx_buf_t));
+
+                b->temporary = 1;
+                b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
+                b->start = cl->buf->start;
+                b->pos = cl->buf->pos;
+                b->last = cl->buf->last;
+                b->end = cl->buf->end;
+
+                *ll = tl;
+                ll = &tl->next;
+
+                size = cl->buf->last - cl->buf->pos;
+
+                if ((off_t) size > rb->chunked->size) {
+                    cl->buf->pos += rb->chunked->size;
+                    r->headers_in.content_length_n += rb->chunked->size;
+                    rb->chunked->size = 0;
+
+                } else {
+                    rb->chunked->size -= size;
+                    r->headers_in.content_length_n += size;
+                    cl->buf->pos = cl->buf->last;
+                }
+
+                b->last = cl->buf->pos;
+
+                continue;
+            }
+
+            if (rc == NGX_DONE) {
+
+                /* a whole response has been parsed successfully */
+
+                rb->rest = 0;
+
+                tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+                if (tl == NULL) {
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+                b = tl->buf;
+
+                ngx_memzero(b, sizeof(ngx_buf_t));
+
+                b->last_buf = 1;
+
+                *ll = tl;
+                ll = &tl->next;
+
+                break;
+            }
+
+            if (rc == NGX_AGAIN) {
+
+                /* set rb->rest, amount of data we want to see next time */
+
+                rb->rest = rb->chunked->length;
+
+                break;
+            }
+
+            /* invalid */
+
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "client sent invalid chunked body");
+
+            return NGX_HTTP_BAD_REQUEST;
+        }
+    }
+
+    rc = ngx_http_request_body_save_filter(r, out);
+
+    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
+                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);
+
+    return rc;
+}
+
+
+static ngx_int_t
+ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    ngx_chain_t               *cl;
+    ngx_http_request_body_t   *rb;
+
+    rb = r->request_body;
+
+#if (NGX_DEBUG)
+
+    for (cl = rb->bufs; cl; cl = cl->next) {
+        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+                       "http body old buf t:%d f:%d %p, pos %p, size: %z "
+                       "file: %O, size: %z",
+                       cl->buf->temporary, cl->buf->in_file,
+                       cl->buf->start, cl->buf->pos,
+                       cl->buf->last - cl->buf->pos,
+                       cl->buf->file_pos,
+                       cl->buf->file_last - cl->buf->file_pos);
+    }
+
+    for (cl = in; cl; cl = cl->next) {
+        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+                       "http body new buf t:%d f:%d %p, pos %p, size: %z "
+                       "file: %O, size: %z",
+                       cl->buf->temporary, cl->buf->in_file,
+                       cl->buf->start, cl->buf->pos,
+                       cl->buf->last - cl->buf->pos,
+                       cl->buf->file_pos,
+                       cl->buf->file_last - cl->buf->file_pos);
+    }
+
+#endif
+
+    /* TODO: coalesce neighbouring buffers */
+
+    ngx_chain_add_copy(r->pool, &rb->bufs, in);
+
+    return NGX_OK;
+}



More information about the nginx-devel mailing list