[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