[nginx] HTTP/2: rewritten handling of request body.

Alexey Ivanov savetherbtz at gmail.com
Thu May 5 01:52:24 UTC 2016


There is a small issue with setting `SETTINGS_INITIAL_WINDOW_SIZE` to 0: now when client tries to POST data it needs to wait for an additional RTT(between `send HEADERS` and `recv WINDOW_UPDATE`) to start sending data.

Here is `nghttp` output:

𝛌 ~ echo -n '{}' | nghttp -nv -d - https://www.dropbox.com
[  0.079] Connected
The negotiated protocol: h2
[  0.237] recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
          (niv=3)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):128]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):0]
          [SETTINGS_MAX_FRAME_SIZE(0x05):16777215]
[  0.237] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=2147418112)
[  0.237] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.237] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.237] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.237] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.237] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.237] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.237] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.237] send HEADERS frame <length=44, flags=0x24, stream_id=13>
          ; END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: POST
          :path: /
          :scheme: https
          :authority: www.dropbox.com
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.9.2
          content-length: 2
[  0.311] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.311] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=13>
          (window_size_increment=2)
[  0.311] send DATA frame <length=2, flags=0x00, stream_id=13>
[  0.386] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=13>
          (window_size_increment=2)
[  0.386] send DATA frame <length=0, flags=0x01, stream_id=13>
          ; END_STREAM
[  0.471] recv (stream_id=13) :status: 405
[  0.471] recv (stream_id=13) server: nginx
[  0.471] recv (stream_id=13) date: Thu, 05 May 2016 00:49:19 GMT
[  0.471] recv (stream_id=13) content-type: text/html
[  0.471] recv (stream_id=13) content-length: 701
[  0.471] recv (stream_id=13) x-dropbox-request-id: d4215cbcea76766b45b05c6aa4cb4a11
[  0.471] recv (stream_id=13) strict-transport-security: max-age=15552000; includeSubDomains
[  0.471] recv HEADERS frame <length=135, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0)
          ; First response header
[  0.471] recv DATA frame <length=701, flags=0x00, stream_id=13>
[  0.471] recv DATA frame <length=0, flags=0x01, stream_id=13>
          ; END_STREAM
[  0.471] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])


Also note that `nghttp` waits for an additional RTT to send `END_STREAM` even though it a zero-length frame, despite that spec does not forbid it:

	Frames with zero length with the END_STREAM flag set (that is, an empty DATA frame) MAY be sent if there is no available space in either flow-control window.

Therefore sending `WINDOW_UPDATE` for exactly `content-length` bytes may not be optimal for some clients.

> On Apr 1, 2016, at 5:57 AM, Valentin Bartenev <vbart at nginx.com> wrote:
> 
> details:   http://hg.nginx.org/nginx/rev/887cca40ba6a
> branches:
> changeset: 6496:887cca40ba6a
> user:      Valentin Bartenev <vbart at nginx.com>
> date:      Fri Apr 01 15:56:03 2016 +0300
> description:
> 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.
> 
> diffstat:
> 
> src/http/ngx_http_request.h      |    3 +
> src/http/ngx_http_request_body.c |   26 +-
> src/http/v2/ngx_http_v2.c        |  548 ++++++++++++++++++--------------------
> src/http/v2/ngx_http_v2.h        |    7 +-
> 4 files changed, 282 insertions(+), 302 deletions(-)
> 
> diffs (768 lines):
> 
> diff -r 92464ebace8e -r 887cca40ba6a src/http/ngx_http_request.h
> --- a/src/http/ngx_http_request.h	Fri Apr 01 15:56:03 2016 +0300
> +++ b/src/http/ngx_http_request.h	Fri Apr 01 15:56:03 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 92464ebace8e -r 887cca40ba6a src/http/ngx_http_request_body.c
> --- a/src/http/ngx_http_request_body.c	Fri Apr 01 15:56:03 2016 +0300
> +++ b/src/http/ngx_http_request_body.c	Fri Apr 01 15:56:03 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 92464ebace8e -r 887cca40ba6a src/http/v2/ngx_http_v2.c
> --- a/src/http/v2/ngx_http_v2.c	Fri Apr 01 15:56:03 2016 +0300
> +++ b/src/http/v2/ngx_http_v2.c	Fri Apr 01 15:56:03 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,9 @@ 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_process_request_body(ngx_http_request_t *r,
> +    u_char *pos, size_t size, ngx_uint_t last);
> +static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r);
> 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,
> @@ -762,8 +766,7 @@ ngx_http_v2_state_data(ngx_http_v2_conne
>         if (h2c->state.length == 0) {
>             ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
>                           "client sent padded DATA frame "
> -                          "with incorrect length: %uz",
> -                          h2c->state.length);
> +                          "with incorrect length: 0");
> 
>             return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
>         }
> @@ -845,8 +848,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 +879,8 @@ 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;
> +
>     h2c->state.stream = stream;
> 
>     return ngx_http_v2_state_read_data(h2c, pos, end);
> @@ -885,16 +891,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 +903,42 @@ 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);
>     }
> 
>     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;
> +
> +    } else {
> +        last = 0;
> +    }
> +
> +    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 +2430,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);
> @@ -2878,7 +2752,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++;
> 
> @@ -3515,7 +3389,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;
> @@ -3525,142 +3399,248 @@ 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:
> +    if (stream->skip_data) {
> +        r->request_body_no_buffering = 0;
>         post_handler(r);
>         return NGX_OK;
> -
> -    case NGX_HTTP_V2_DATA_ERROR:
> +    }
> +
> +    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->rest = 1;
> +    rb->post_handler = post_handler;
> +
> +    r->request_body = rb;
> +
> +    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
> +
> +    len = r->headers_in.content_length_n;
> +
> +    if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size
> +        && !r->request_body_in_file_only)
> +    {
> +        rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
> +
> +    } else {
> +        rb->buf = ngx_calloc_buf(r->pool);
> +
> +        if (rb->buf != NULL) {
> +            rb->buf->sync = 1;
> +        }
> +    }
> +
> +    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->no_flow_control = 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;
> +    }
> +
> +    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_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_connection_t          *fc;
> +    ngx_http_request_body_t   *rb;
> +    ngx_http_core_loc_conf_t  *clcf;
> +
> +    rb = r->request_body;
> +
> +    if (rb == NULL) {
> +        return NGX_OK;
> +    }
> +
> +    fc = r->connection;
> +    buf = rb->buf;
> +
> +    if (size) {
> +        if (buf->sync) {
> +            buf->pos = buf->start = pos;
> +            buf->last = buf->end = pos + size;
> +
> +        } else {
> +            if (size > (size_t) (buf->end - buf->last)) {
> +                ngx_log_error(NGX_LOG_INFO, fc->log, 0,
> +                                "client intended to send body data "
> +                                "larger than declared");
> +
> +                return NGX_HTTP_BAD_REQUEST;
> +            }
> +
> +            buf->last = ngx_cpymem(buf->last, pos, size);
> +        }
> +    }
> +
> +    if (last) {
> +        rb->rest = 0;
> +
> +        if (fc->read->timer_set) {
> +            ngx_del_timer(fc->read);
> +        }
> +
> +        rc = ngx_http_v2_filter_request_body(r);
> +
> +        if (rc != NGX_OK) {
> +            return rc;
> +        }
> +
> +        if (buf->sync) {
> +            /* prevent reusing this buffer in the upstream module */
> +            rb->buf = NULL;
> +        }
> +
>         if (r->headers_in.content_length_n == -1) {
> -            return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
> +            r->headers_in.content_length_n = rb->received;
> +        }
> +
> +        r->read_event_handler = ngx_http_block_reading;
> +        rb->post_handler(r);
> +
> +        return NGX_OK;
> +    }
> +
> +    if (size == 0) {
> +        return NGX_OK;
> +    }
> +
> +    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
> +    ngx_add_timer(fc->read, clcf->client_body_timeout);
> +
> +    if (buf->sync) {
> +        return ngx_http_v2_filter_request_body(r);
> +    }
> +
> +    return NGX_OK;
> +}
> +
> +
> +static ngx_int_t
> +ngx_http_v2_filter_request_body(ngx_http_request_t *r)
> +{
> +    ngx_buf_t                 *b, *buf;
> +    ngx_int_t                  rc;
> +    ngx_chain_t               *cl;
> +    ngx_http_request_body_t   *rb;
> +    ngx_http_core_loc_conf_t  *clcf;
> +
> +    rb = r->request_body;
> +    buf = rb->buf;
> +
> +    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));
> +
> +    if (buf->pos != buf->last) {
> +        r->request_length += buf->last - buf->pos;
> +        rb->received += buf->last - buf->pos;
> +
> +        if (r->headers_in.content_length_n != -1) {
> +            if (rb->received > r->headers_in.content_length_n) {
> +                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
> +                              "client intended to send body data "
> +                              "larger than declared");
> +
> +                return NGX_HTTP_BAD_REQUEST;
> +            }
> +
>         } else {
> +            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
> +
> +            if (clcf->client_max_body_size
> +                && rb->received > clcf->client_max_body_size)
> +            {
> +                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;
> +            }
> +        }
> +
> +        b->temporary = 1;
> +        b->pos = buf->pos;
> +        b->last = buf->last;
> +        b->start = b->pos;
> +        b->end = b->last;
> +
> +        buf->pos = buf->last;
> +    }
> +
> +    if (!rb->rest) {
> +        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;
>         }
> 
> -    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);
> -        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;
> -
> -    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;
> +        b->last_buf = 1;
> +    }
> +
> +    b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body;
> +
> +    rc = ngx_http_top_request_body_filter(r, cl);
> +
> +    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl,
> +                            (ngx_buf_tag_t) &ngx_http_v2_filter_request_body);
> +
> +    return rc;
> }
> 
> 
> @@ -3678,7 +3658,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;
> @@ -3688,7 +3668,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 92464ebace8e -r 887cca40ba6a src/http/v2/ngx_http_v2.h
> --- a/src/http/v2/ngx_http_v2.h	Fri Apr 01 15:56:03 2016 +0300
> +++ b/src/http/v2/ngx_http_v2.h	Fri Apr 01 15:56:03 2016 +0300
> @@ -24,10 +24,6 @@
> #define NGX_HTTP_V2_MAX_FIELD                                                 \
>     (127 + (1 << (NGX_HTTP_V2_INT_OCTETS - 1) * 7) - 1)
> 
> -#define NGX_HTTP_V2_DATA_DISCARD         1
> -#define NGX_HTTP_V2_DATA_ERROR           2
> -#define NGX_HTTP_V2_DATA_INTERNAL_ERROR  3
> -
> #define NGX_HTTP_V2_FRAME_HEADER_SIZE    9
> 
> /* frame types */
> @@ -195,7 +191,8 @@ struct ngx_http_v2_stream_s {
>     unsigned                         in_closed:1;
>     unsigned                         out_closed:1;
>     unsigned                         rst_sent:1;
> -    unsigned                         skip_data:2;
> +    unsigned                         no_flow_control:1;
> +    unsigned                         skip_data:1;
> };
> 
> 
> 
> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org
> http://mailman.nginx.org/mailman/listinfo/nginx-devel

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 842 bytes
Desc: Message signed with OpenPGP using GPGMail
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20160504/c101163d/attachment.bin>


More information about the nginx-devel mailing list