diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/http/ngx_http_request.h | 3 | ||||
-rw-r--r-- | src/http/ngx_http_request_body.c | 24 | ||||
-rw-r--r-- | src/http/v2/ngx_http_v2.c | 462 | ||||
-rw-r--r-- | src/http/v2/ngx_http_v2.h | 7 |
4 files changed, 238 insertions, 258 deletions
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 8b881392d..cfde7dc7e 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -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 --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index b5803d57b..55e7d0802 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -40,20 +40,20 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, 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,16 +503,16 @@ ngx_http_discard_request_body(ngx_http_request_t *r) ngx_int_t rc; ngx_event_t *rev; -#if (NGX_HTTP_V2) - if (r->stream && r == r->main) { - r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD; + if (r != r->main || r->discard_body || r->request_body) { return NGX_OK; } -#endif - if (r != r->main || r->discard_body || r->request_body) { +#if (NGX_HTTP_V2) + if (r->stream) { + r->stream->skip_data = 1; return NGX_OK; } +#endif if (ngx_http_test_expect(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 95c737cdb..e8d3303a5 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -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_http_request_t *r, 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_connection_t *h2c, u_char *pos, u_char *end) 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_connection_t *h2c, u_char *pos, u_char *end) 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_connection_t *h2c, u_char *pos, u_char *end) 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_connection_t *h2c, u_char *pos, } 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; + last = stream->in_closed; - 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); + } else { + last = 0; } - 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"); + rc = ngx_http_v2_process_request_body(stream->request, pos, size, last); - 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; + if (rc != NGX_OK) { + stream->skip_data = 1; + ngx_http_finalize_request(stream->request, rc); } - 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); - } + pos += size; + h2c->state.length -= size; + if (h2c->state.length) { 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_connection_t *h2c, ngx_uint_t ack) 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_connection_t *h2c) 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_t *r) 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_t *r) } -static ngx_int_t -ngx_http_v2_init_request_body(ngx_http_request_t *r) +ngx_int_t +ngx_http_v2_read_request_body(ngx_http_request_t *r, + ngx_http_client_body_handler_pt post_handler) { - ngx_buf_t *buf; - ngx_temp_file_t *tf; + off_t len; + ngx_http_v2_stream_t *stream; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; + stream = r->stream; + + if (stream->skip_data) { + r->request_body_no_buffering = 0; + post_handler(r); + return NGX_OK; + } + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { - return NGX_ERROR; + return NGX_HTTP_INTERNAL_SERVER_ERROR; } - r->request_body = rb; + /* + * set by ngx_pcalloc(): + * + * rb->bufs = NULL; + * rb->buf = NULL; + * rb->received = 0; + * rb->free = NULL; + * rb->busy = NULL; + */ - if (r->stream->in_closed) { - return NGX_OK; - } + rb->rest = 1; + rb->post_handler = post_handler; - rb->rest = r->headers_in.content_length_n; + r->request_body = rb; 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) + len = r->headers_in.content_length_n; + + if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size + && !r->request_body_in_file_only) { - tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); - if (tf == NULL) { - return NGX_ERROR; - } + rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); + + } else { + rb->buf = ngx_calloc_buf(r->pool); - 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; + if (rb->buf != NULL) { + rb->buf->sync = 1; } + } - rb->temp_file = tf; + if (rb->buf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - 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; + 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); } + } - buf = ngx_calloc_buf(r->pool); - if (buf == NULL) { - return NGX_ERROR; + if (last) { + rb->rest = 0; + + if (fc->read->timer_set) { + ngx_del_timer(fc->read); } - } else { + rc = ngx_http_v2_filter_request_body(r); - if (rb->rest == 0) { - return NGX_OK; + if (rc != NGX_OK) { + return rc; } - buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest); - if (buf == NULL) { - return NGX_ERROR; + if (buf->sync) { + /* prevent reusing this buffer in the upstream module */ + rb->buf = NULL; } - } - rb->buf = buf; + if (r->headers_in.content_length_n == -1) { + r->headers_in.content_length_n = rb->received; + } - rb->bufs = ngx_alloc_chain_link(r->pool); - if (rb->bufs == NULL) { - return NGX_ERROR; + r->read_event_handler = ngx_http_block_reading; + rb->post_handler(r); + + return NGX_OK; } - rb->bufs->buf = buf; - rb->bufs->next = NULL; + if (size == 0) { + return NGX_OK; + } - rb->rest = 0; + 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; } -ngx_int_t -ngx_http_v2_read_request_body(ngx_http_request_t *r, - ngx_http_client_body_handler_pt post_handler) +static ngx_int_t +ngx_http_v2_filter_request_body(ngx_http_request_t *r) { - ngx_http_v2_stream_t *stream; + 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; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http2 read request body"); + rb = r->request_body; + buf = rb->buf; - stream = r->stream; + cl = ngx_chain_get_free_buf(r->pool, &rb->free); + if (cl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - switch (stream->skip_data) { + b = cl->buf; - case NGX_HTTP_V2_DATA_DISCARD: - post_handler(r); - return NGX_OK; + 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; + } - case NGX_HTTP_V2_DATA_ERROR: - if (r->headers_in.content_length_n == -1) { - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } else { - return NGX_HTTP_BAD_REQUEST; + 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; + } } - case NGX_HTTP_V2_DATA_INTERNAL_ERROR: - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + b->temporary = 1; + b->pos = buf->pos; + b->last = buf->last; + b->start = b->pos; + b->end = b->last; - 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; + buf->pos = buf->last; } - if (stream->in_closed) { - post_handler(r); - return NGX_OK; + 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; + } + + b->last_buf = 1; } - r->request_body->post_handler = post_handler; + b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body; - r->read_event_handler = ngx_http_v2_read_client_request_body_handler; - r->write_event_handler = ngx_http_request_empty_handler; + rc = ngx_http_top_request_body_filter(r, cl); - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ngx_add_timer(r->connection->read, clcf->client_body_timeout); + ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl, + (ngx_buf_tag_t) &ngx_http_v2_filter_request_body); - return NGX_AGAIN; + return rc; } @@ -3678,7 +3658,7 @@ ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r) 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_handler(ngx_http_request_t *r) 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 --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index 4d1f2c785..07458878f 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -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; }; |