[PATCH 2 of 4] HTTP/2: rewritten handling of request body

Valentin V. Bartenev vbart at nginx.com
Fri Feb 26 15:45:57 UTC 2016


 src/http/ngx_http_request.h      |    3 +
 src/http/ngx_http_request_body.c |   26 +-
 src/http/v2/ngx_http_v2.c        |  541 ++++++++++++++++++--------------------
 src/http/v2/ngx_http_v2.h        |    3 +-
 4 files changed, 279 insertions(+), 294 deletions(-)


# HG changeset patch
# User Valentin Bartenev <vbart at nginx.com>
# Date 1456498922 -10800
#      Fri Feb 26 18:02:02 2016 +0300
# Node ID edacc9766e8e7f610921f71739e6797f771cf2b6
# Parent  1f4781e6b9043414d0c8ec7884f31f7e033b14c6
HTTP/2: rewritten handling of request body.

There are two improvements:

  1. Support for request body filters;

  2. Receiving of request body is started only after
     the ngx_http_read_client_request_body() call.

The last one fixes the problem when the client_max_body_size value might not be
respected from the right location if the location was changed either during the
process of receiving body or after the whole body had been received.

diff -r 1f4781e6b904 -r edacc9766e8e src/http/ngx_http_request.h
--- a/src/http/ngx_http_request.h	Fri Feb 26 14:39:50 2016 +0300
+++ b/src/http/ngx_http_request.h	Fri Feb 26 18:02:02 2016 +0300
@@ -284,6 +284,9 @@ typedef struct {
     ngx_chain_t                      *bufs;
     ngx_buf_t                        *buf;
     off_t                             rest;
+#if (NGX_HTTP_V2)
+    off_t                             received;
+#endif
     ngx_chain_t                      *free;
     ngx_chain_t                      *busy;
     ngx_http_chunked_t               *chunked;
diff -r 1f4781e6b904 -r edacc9766e8e src/http/ngx_http_request_body.c
--- a/src/http/ngx_http_request_body.c	Fri Feb 26 14:39:50 2016 +0300
+++ b/src/http/ngx_http_request_body.c	Fri Feb 26 18:02:02 2016 +0300
@@ -40,20 +40,20 @@ ngx_http_read_client_request_body(ngx_ht
 
     r->main->count++;
 
+    if (r != r->main || r->request_body || r->discard_body) {
+        r->request_body_no_buffering = 0;
+        post_handler(r);
+        return NGX_OK;
+    }
+
 #if (NGX_HTTP_V2)
-    if (r->stream && r == r->main) {
+    if (r->stream) {
         r->request_body_no_buffering = 0;
         rc = ngx_http_v2_read_request_body(r, post_handler);
         goto done;
     }
 #endif
 
-    if (r != r->main || r->request_body || r->discard_body) {
-        r->request_body_no_buffering = 0;
-        post_handler(r);
-        return NGX_OK;
-    }
-
     if (ngx_http_test_expect(r) != NGX_OK) {
         rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
         goto done;
@@ -503,17 +503,17 @@ ngx_http_discard_request_body(ngx_http_r
     ngx_int_t     rc;
     ngx_event_t  *rev;
 
+    if (r != r->main || r->discard_body || r->request_body) {
+        return NGX_OK;
+    }
+
 #if (NGX_HTTP_V2)
-    if (r->stream && r == r->main) {
-        r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD;
+    if (r->stream) {
+        r->stream->skip_data = 1;
         return NGX_OK;
     }
 #endif
 
-    if (r != r->main || r->discard_body || r->request_body) {
-        return NGX_OK;
-    }
-
     if (ngx_http_test_expect(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
diff -r 1f4781e6b904 -r edacc9766e8e src/http/v2/ngx_http_v2.c
--- a/src/http/v2/ngx_http_v2.c	Fri Feb 26 14:39:50 2016 +0300
+++ b/src/http/v2/ngx_http_v2.c	Fri Feb 26 18:02:02 2016 +0300
@@ -51,6 +51,8 @@
 #define NGX_HTTP_V2_MAX_WINDOW                   ((1U << 31) - 1)
 #define NGX_HTTP_V2_DEFAULT_WINDOW               65535
 
+#define NGX_HTTP_V2_INITIAL_WINDOW               0
+
 #define NGX_HTTP_V2_ROOT                         (void *) -1
 
 
@@ -163,7 +165,10 @@ static ngx_int_t ngx_http_v2_cookie(ngx_
     ngx_http_v2_header_t *header);
 static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r);
 static void ngx_http_v2_run_request(ngx_http_request_t *r);
-static ngx_int_t ngx_http_v2_init_request_body(ngx_http_request_t *r);
+static ngx_int_t ngx_http_v2_account_request_body(ngx_http_request_t *r,
+    size_t size, ngx_uint_t last);
+static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r,
+    u_char *pos, size_t size, ngx_uint_t last);
 static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r);
 
 static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,
@@ -754,6 +759,7 @@ ngx_http_v2_state_head(ngx_http_v2_conne
 static u_char *
 ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
 {
+    ngx_int_t              rc;
     ngx_http_v2_node_t    *node;
     ngx_http_v2_stream_t  *stream;
 
@@ -845,8 +851,9 @@ ngx_http_v2_state_data(ngx_http_v2_conne
 
     stream->recv_window -= h2c->state.length;
 
-    if (stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) {
-
+    if (stream->no_flow_control
+        && stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4)
+    {
         if (ngx_http_v2_send_window_update(h2c, node->id,
                                            NGX_HTTP_V2_MAX_WINDOW
                                            - stream->recv_window)
@@ -875,6 +882,16 @@ ngx_http_v2_state_data(ngx_http_v2_conne
         return ngx_http_v2_state_skip_padded(h2c, pos, end);
     }
 
+    stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
+
+    rc = ngx_http_v2_account_request_body(stream->request, h2c->state.length,
+                                          stream->in_closed);
+    if (rc != NGX_OK) {
+        stream->skip_data = 1;
+        ngx_http_finalize_request(stream->request, rc);
+        return ngx_http_v2_state_skip_padded(h2c, pos, end);
+    }
+
     h2c->state.stream = stream;
 
     return ngx_http_v2_state_read_data(h2c, pos, end);
@@ -885,16 +902,10 @@ static u_char *
 ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
     u_char *end)
 {
-    size_t                     size;
-    ssize_t                    n;
-    ngx_buf_t                 *buf;
-    ngx_int_t                  rc;
-    ngx_temp_file_t           *tf;
-    ngx_connection_t          *fc;
-    ngx_http_request_t        *r;
-    ngx_http_v2_stream_t      *stream;
-    ngx_http_request_body_t   *rb;
-    ngx_http_core_loc_conf_t  *clcf;
+    size_t                 size;
+    ngx_int_t              rc;
+    ngx_uint_t             last;
+    ngx_http_v2_stream_t  *stream;
 
     stream = h2c->state.stream;
 
@@ -903,168 +914,39 @@ ngx_http_v2_state_read_data(ngx_http_v2_
     }
 
     if (stream->skip_data) {
-        stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
-
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
-                       "skipping http2 DATA frame, reason: %d",
-                       stream->skip_data);
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
+                       "skipping http2 DATA frame");
 
         return ngx_http_v2_state_skip_padded(h2c, pos, end);
     }
 
+    last = 0;
     size = end - pos;
 
-    if (size > h2c->state.length) {
+    if (size >= h2c->state.length) {
         size = h2c->state.length;
-    }
-
-    r = stream->request;
-
-    if (r->request_body == NULL
-        && ngx_http_v2_init_request_body(r) != NGX_OK)
-    {
-        stream->skip_data = NGX_HTTP_V2_DATA_INTERNAL_ERROR;
-        return ngx_http_v2_state_skip_padded(h2c, pos, end);
-    }
-
-    fc = r->connection;
-    rb = r->request_body;
-    tf = rb->temp_file;
-    buf = rb->buf;
-
-    if (size) {
-        rb->rest += size;
-
-        if (r->headers_in.content_length_n != -1
-            && r->headers_in.content_length_n < rb->rest)
-        {
-            ngx_log_error(NGX_LOG_INFO, fc->log, 0,
-                          "client intended to send body data "
-                          "larger than declared");
-
-            stream->skip_data = NGX_HTTP_V2_DATA_ERROR;
-            goto error;
-
-        } else {
-            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
-            if (clcf->client_max_body_size
-                && clcf->client_max_body_size < rb->rest)
-            {
-                ngx_log_error(NGX_LOG_ERR, fc->log, 0,
-                              "client intended to send "
-                              "too large chunked body: %O bytes", rb->rest);
-
-                stream->skip_data = NGX_HTTP_V2_DATA_ERROR;
-                goto error;
-            }
-        }
-
-        h2c->state.length -= size;
-
-        if (tf) {
-            buf->start = pos;
-            buf->pos = pos;
-
-            pos += size;
-
-            buf->end = pos;
-            buf->last = pos;
-
-            n = ngx_write_chain_to_temp_file(tf, rb->bufs);
-
-            /* TODO: n == 0 or not complete and level event */
-
-            if (n == NGX_ERROR) {
-                stream->skip_data = NGX_HTTP_V2_DATA_INTERNAL_ERROR;
-                goto error;
-            }
-
-            tf->offset += n;
-
-        } else {
-            buf->last = ngx_cpymem(buf->last, pos, size);
-            pos += size;
-        }
-
-        r->request_length += size;
-    }
+        last = stream->in_closed;
+    }
+
+    rc = ngx_http_v2_process_request_body(stream->request, pos, size, last);
+    if (rc != NGX_OK) {
+        stream->skip_data = 1;
+        ngx_http_finalize_request(stream->request, rc);
+    }
+
+    pos += size;
+    h2c->state.length -= size;
 
     if (h2c->state.length) {
-        if (rb->post_handler) {
-            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-            ngx_add_timer(fc->read, clcf->client_body_timeout);
-        }
-
         return ngx_http_v2_state_save(h2c, pos, end,
                                       ngx_http_v2_state_read_data);
     }
 
-    if (h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG) {
-        stream->in_closed = 1;
-
-        if (r->headers_in.content_length_n < 0) {
-            r->headers_in.content_length_n = rb->rest;
-
-        } else if (r->headers_in.content_length_n != rb->rest) {
-            ngx_log_error(NGX_LOG_INFO, fc->log, 0,
-                          "client prematurely closed stream: "
-                          "only %O out of %O bytes of request body received",
-                          rb->rest, r->headers_in.content_length_n);
-
-            stream->skip_data = NGX_HTTP_V2_DATA_ERROR;
-            goto error;
-        }
-
-        if (tf) {
-            ngx_memzero(buf, sizeof(ngx_buf_t));
-
-            buf->in_file = 1;
-            buf->file_last = tf->file.offset;
-            buf->file = &tf->file;
-
-            rb->buf = NULL;
-        }
-
-        if (rb->post_handler) {
-            if (fc->read->timer_set) {
-                ngx_del_timer(fc->read);
-            }
-
-            r->read_event_handler = ngx_http_block_reading;
-            rb->post_handler(r);
-        }
-
-    } else if (rb->post_handler) {
-        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-        ngx_add_timer(fc->read, clcf->client_body_timeout);
-    }
-
     if (h2c->state.padding) {
         return ngx_http_v2_state_skip_padded(h2c, pos, end);
     }
 
     return ngx_http_v2_state_complete(h2c, pos, end);
-
-error:
-
-    if (rb->post_handler) {
-        if (fc->read->timer_set) {
-            ngx_del_timer(fc->read);
-        }
-
-        if (stream->skip_data == NGX_HTTP_V2_DATA_ERROR) {
-            rc = (r->headers_in.content_length_n == -1)
-                 ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE : NGX_HTTP_BAD_REQUEST;
-
-        } else {
-            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
-
-        ngx_http_finalize_request(r, rc);
-    }
-
-    return ngx_http_v2_state_skip_padded(h2c, pos, end);
 }
 
 
@@ -2556,7 +2438,7 @@ ngx_http_v2_send_settings(ngx_http_v2_co
         buf->last = ngx_http_v2_write_uint16(buf->last,
                                          NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING);
         buf->last = ngx_http_v2_write_uint32(buf->last,
-                                             NGX_HTTP_V2_MAX_WINDOW);
+                                             NGX_HTTP_V2_INITIAL_WINDOW);
 
         buf->last = ngx_http_v2_write_uint16(buf->last,
                                            NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING);
@@ -2867,7 +2749,7 @@ ngx_http_v2_create_stream(ngx_http_v2_co
     stream->connection = h2c;
 
     stream->send_window = h2c->init_window;
-    stream->recv_window = NGX_HTTP_V2_MAX_WINDOW;
+    stream->recv_window = NGX_HTTP_V2_INITIAL_WINDOW;
 
     h2c->processing++;
 
@@ -3504,7 +3386,7 @@ ngx_http_v2_run_request(ngx_http_request
         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                       "client prematurely closed stream");
 
-        r->stream->skip_data = NGX_HTTP_V2_DATA_ERROR;
+        r->stream->skip_data = 1;
 
         ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
         return;
@@ -3514,142 +3396,241 @@ ngx_http_v2_run_request(ngx_http_request
 }
 
 
-static ngx_int_t
-ngx_http_v2_init_request_body(ngx_http_request_t *r)
-{
-    ngx_buf_t                 *buf;
-    ngx_temp_file_t           *tf;
-    ngx_http_request_body_t   *rb;
-    ngx_http_core_loc_conf_t  *clcf;
-
-    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
-    if (rb == NULL) {
-        return NGX_ERROR;
-    }
-
-    r->request_body = rb;
-
-    if (r->stream->in_closed) {
-        return NGX_OK;
-    }
-
-    rb->rest = r->headers_in.content_length_n;
-
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
-    if (r->request_body_in_file_only
-        || rb->rest > (off_t) clcf->client_body_buffer_size
-        || rb->rest < 0)
-    {
-        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
-        if (tf == NULL) {
-            return NGX_ERROR;
-        }
-
-        tf->file.fd = NGX_INVALID_FILE;
-        tf->file.log = r->connection->log;
-        tf->path = clcf->client_body_temp_path;
-        tf->pool = r->pool;
-        tf->warn = "a client request body is buffered to a temporary file";
-        tf->log_level = r->request_body_file_log_level;
-        tf->persistent = r->request_body_in_persistent_file;
-        tf->clean = r->request_body_in_clean_file;
-
-        if (r->request_body_file_group_access) {
-            tf->access = 0660;
-        }
-
-        rb->temp_file = tf;
-
-        if (r->stream->in_closed
-            && ngx_create_temp_file(&tf->file, tf->path, tf->pool,
-                                    tf->persistent, tf->clean, tf->access)
-               != NGX_OK)
-        {
-            return NGX_ERROR;
-        }
-
-        buf = ngx_calloc_buf(r->pool);
-        if (buf == NULL) {
-            return NGX_ERROR;
-        }
-
-    } else {
-
-        if (rb->rest == 0) {
-            return NGX_OK;
-        }
-
-        buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest);
-        if (buf == NULL) {
-            return NGX_ERROR;
-        }
-    }
-
-    rb->buf = buf;
-
-    rb->bufs = ngx_alloc_chain_link(r->pool);
-    if (rb->bufs == NULL) {
-        return NGX_ERROR;
-    }
-
-    rb->bufs->buf = buf;
-    rb->bufs->next = NULL;
-
-    rb->rest = 0;
-
-    return NGX_OK;
-}
-
-
 ngx_int_t
 ngx_http_v2_read_request_body(ngx_http_request_t *r,
     ngx_http_client_body_handler_pt post_handler)
 {
+    off_t                      len;
     ngx_http_v2_stream_t      *stream;
+    ngx_http_request_body_t   *rb;
     ngx_http_core_loc_conf_t  *clcf;
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http2 read request body");
-
     stream = r->stream;
 
-    switch (stream->skip_data) {
-
-    case NGX_HTTP_V2_DATA_DISCARD:
-        post_handler(r);
+    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+    if (rb == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     rb->bufs = NULL;
+     *     rb->buf = NULL;
+     *     rb->received = 0;
+     *     rb->free = NULL;
+     *     rb->busy = NULL;
+     */
+
+    rb->post_handler = post_handler;
+
+    r->request_body = rb;
+
+    if (stream->skip_data) {
+        return NGX_HTTP_BAD_REQUEST;
+    }
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    len = r->headers_in.content_length_n;
+
+    if (stream->in_closed && len < 0) {
+        len = 0;
+    }
+
+    if (len != 0) {
+        if (len < 0
+            || len > (off_t) clcf->client_body_buffer_size
+            || r->request_body_in_file_only)
+        {
+            rb->buf = ngx_calloc_buf(r->pool);
+
+            if (rb->buf != NULL) {
+                rb->buf->memory = 1;
+                rb->buf->sync = 1;
+            }
+
+        } else {
+            rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
+        }
+
+        if (rb->buf == NULL) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    if (stream->in_closed) {
+        return ngx_http_v2_process_request_body(r, NULL, 0, 1);
+    }
+
+    stream->recv_window = NGX_HTTP_V2_MAX_WINDOW;
+
+    if (ngx_http_v2_send_window_update(stream->connection, stream->node->id,
+                                       stream->recv_window)
+        == NGX_ERROR)
+    {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    stream->no_flow_control = 1;
+
+    ngx_add_timer(r->connection->read, clcf->client_body_timeout);
+
+    r->read_event_handler = ngx_http_v2_read_client_request_body_handler;
+    r->write_event_handler = ngx_http_request_empty_handler;
+
+    return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_v2_account_request_body(ngx_http_request_t *r, size_t size,
+    ngx_uint_t last)
+{
+    ngx_http_request_body_t   *rb;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    rb = r->request_body;
+
+    if (rb == NULL) {
         return NGX_OK;
-
-    case NGX_HTTP_V2_DATA_ERROR:
-        if (r->headers_in.content_length_n == -1) {
-            return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
-        } else {
+    }
+
+    if (size) {
+        r->request_length += size;
+        rb->received += size;
+
+        if (r->headers_in.content_length_n != -1
+            && r->headers_in.content_length_n < rb->received)
+        {
+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                          "client intended to send body data "
+                          "larger than declared");
+
             return NGX_HTTP_BAD_REQUEST;
         }
 
-    case NGX_HTTP_V2_DATA_INTERNAL_ERROR:
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    if (!r->request_body && ngx_http_v2_init_request_body(r) != NGX_OK) {
-        stream->skip_data = NGX_HTTP_V2_DATA_INTERNAL_ERROR;
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    if (stream->in_closed) {
-        post_handler(r);
+        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+        if (clcf->client_max_body_size
+            && clcf->client_max_body_size < rb->received)
+        {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                         "client intended to send too large chunked body: "
+                         "%O bytes", rb->received);
+
+            return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+        }
+    }
+
+    if (last) {
+        if (r->headers_in.content_length_n != -1
+            && r->headers_in.content_length_n != rb->received)
+        {
+            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                          "client prematurely closed stream: "
+                          "only %O out of %O bytes of request body received",
+                          rb->received, r->headers_in.content_length_n);
+
+            return NGX_HTTP_BAD_REQUEST;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,
+    size_t size, ngx_uint_t last)
+{
+    ngx_buf_t                 *buf;
+    ngx_int_t                  rc;
+    ngx_chain_t                out;
+    ngx_connection_t          *fc;
+    ngx_http_request_body_t   *rb;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    fc = r->connection;
+    rb = r->request_body;
+
+    if (rb == NULL) {
         return NGX_OK;
     }
 
-    r->request_body->post_handler = post_handler;
-
-    r->read_event_handler = ngx_http_v2_read_client_request_body_handler;
-    r->write_event_handler = ngx_http_request_empty_handler;
+    rb->rest = !last;
+
+    buf = rb->buf;
+
+    if (size) {
+        if (buf->sync) {
+            buf->start = pos;
+            buf->pos = pos;
+
+            pos += size;
+
+            buf->end = pos;
+            buf->last = pos;
+
+            buf->last_buf = last;
+
+            out.buf = buf;
+            out.next = NULL;
+
+            rc = ngx_http_top_request_body_filter(r, &out);
+
+            if (rc != NGX_OK) {
+                return rc;
+            }
+
+        } else {
+            buf->last = ngx_cpymem(buf->last, pos, size);
+        }
+    }
+
+    if (last) {
+        if (r->headers_in.content_length_n < 0) {
+            r->headers_in.content_length_n = rb->received;
+        }
+
+        if (buf) {
+            if (buf->sync) {
+                /* prevent reusing this buffer in the upstream module */
+                rb->buf = NULL;
+
+                rc = size ? NGX_OK : ngx_http_top_request_body_filter(r, NULL);
+
+            } else {
+                buf->last_buf = 1;
+
+                out.buf = buf;
+                out.next = NULL;
+
+                rc = ngx_http_top_request_body_filter(r, &out);
+            }
+
+        } else {
+            rc = ngx_http_top_request_body_filter(r, NULL);
+        }
+
+        if (rc != NGX_OK) {
+            return rc;
+        }
+
+        if (fc->read->timer_set) {
+            ngx_del_timer(fc->read);
+        }
+
+        r->read_event_handler = ngx_http_block_reading;
+        rb->post_handler(r);
+
+        return NGX_OK;
+    }
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-    ngx_add_timer(r->connection->read, clcf->client_body_timeout);
-
-    return NGX_AGAIN;
+    ngx_add_timer(fc->read, clcf->client_body_timeout);
+
+    return NGX_OK;
 }
 
 
@@ -3667,7 +3648,7 @@ ngx_http_v2_read_client_request_body_han
         ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
 
         fc->timedout = 1;
-        r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD;
+        r->stream->skip_data = 1;
 
         ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
         return;
@@ -3677,7 +3658,7 @@ ngx_http_v2_read_client_request_body_han
         ngx_log_error(NGX_LOG_INFO, fc->log, 0,
                       "client prematurely closed stream");
 
-        r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD;
+        r->stream->skip_data = 1;
 
         ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
         return;
diff -r 1f4781e6b904 -r edacc9766e8e src/http/v2/ngx_http_v2.h
--- a/src/http/v2/ngx_http_v2.h	Fri Feb 26 14:39:50 2016 +0300
+++ b/src/http/v2/ngx_http_v2.h	Fri Feb 26 18:02:02 2016 +0300
@@ -194,7 +194,8 @@ struct ngx_http_v2_stream_s {
     unsigned                         exhausted:1;
     unsigned                         in_closed:1;
     unsigned                         out_closed:1;
-    unsigned                         skip_data:2;
+    unsigned                         no_flow_control:1;
+    unsigned                         skip_data:1;
 };
 
 



More information about the nginx-devel mailing list