aboutsummaryrefslogtreecommitdiff
path: root/src/http/v2/ngx_http_v2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/http/v2/ngx_http_v2.c')
-rw-r--r--src/http/v2/ngx_http_v2.c152
1 files changed, 109 insertions, 43 deletions
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index 278c9abf6..cd0243cfc 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -48,11 +48,6 @@
#define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14)
-#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
@@ -879,8 +874,6 @@ 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);
@@ -891,10 +884,12 @@ static u_char *
ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
- size_t size;
- ngx_int_t rc;
- ngx_uint_t last;
- ngx_http_v2_stream_t *stream;
+ size_t size;
+ ngx_buf_t *buf;
+ ngx_int_t rc;
+ ngx_http_request_t *r;
+ ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
stream = h2c->state.stream;
@@ -913,17 +908,42 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
if (size >= h2c->state.length) {
size = h2c->state.length;
- last = stream->in_closed;
-
- } else {
- last = 0;
+ stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
}
- rc = ngx_http_v2_process_request_body(stream->request, pos, size, last);
+ r = stream->request;
- if (rc != NGX_OK) {
- stream->skip_data = 1;
- ngx_http_finalize_request(stream->request, rc);
+ if (r->request_body) {
+ rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed);
+
+ if (rc != NGX_OK) {
+ stream->skip_data = 1;
+ ngx_http_finalize_request(r, rc);
+ }
+
+ } else if (size) {
+ buf = stream->preread;
+
+ if (buf == NULL) {
+ h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
+
+ buf = ngx_create_temp_buf(r->pool, h2scf->preread_size);
+ if (buf == NULL) {
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ stream->preread = buf;
+ }
+
+ if (size > (size_t) (buf->end - buf->last)) {
+ ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
+ "http2 preread buffer overflow");
+ return ngx_http_v2_connection_error(h2c,
+ NGX_HTTP_V2_INTERNAL_ERROR);
+ }
+
+ buf->last = ngx_cpymem(buf->last, pos, size);
}
pos += size;
@@ -1058,7 +1078,9 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
goto rst_stream;
}
- if (!h2c->settings_ack && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG))
+ if (!h2c->settings_ack
+ && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)
+ && h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW)
{
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent stream with data "
@@ -2434,8 +2456,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_INITIAL_WINDOW);
+ buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size);
buf->last = ngx_http_v2_write_uint16(buf->last,
NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING);
@@ -2643,6 +2664,7 @@ ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c)
ngx_http_log_ctx_t *ctx;
ngx_http_request_t *r;
ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
ngx_http_core_srv_conf_t *cscf;
fc = h2c->free_fake_connections;
@@ -2756,8 +2778,10 @@ ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c)
stream->request = r;
stream->connection = h2c;
+ h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
+
stream->send_window = h2c->init_window;
- stream->recv_window = NGX_HTTP_V2_INITIAL_WINDOW;
+ stream->recv_window = h2scf->preread_size;
h2c->processing++;
@@ -3411,7 +3435,11 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r,
ngx_http_client_body_handler_pt post_handler)
{
off_t len;
+ size_t size;
+ ngx_buf_t *buf;
+ ngx_int_t rc;
ngx_http_v2_stream_t *stream;
+ ngx_http_v2_srv_conf_t *h2scf;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
ngx_http_v2_connection_t *h2c;
@@ -3444,24 +3472,34 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r,
r->request_body = rb;
+ h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
len = r->headers_in.content_length_n;
if (r->request_body_no_buffering && !stream->in_closed) {
- r->request_body_in_file_only = 0;
if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
len = clcf->client_body_buffer_size;
}
+ /*
+ * We need a room to store data up to the stream's initial window size,
+ * at least until this window will be exhausted.
+ */
+
+ if (len < (off_t) h2scf->preread_size) {
+ len = h2scf->preread_size;
+ }
+
if (len > NGX_HTTP_V2_MAX_WINDOW) {
len = NGX_HTTP_V2_MAX_WINDOW;
}
- }
- 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 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);
@@ -3478,22 +3516,44 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r,
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
+ buf = stream->preread;
+
if (stream->in_closed) {
r->request_body_no_buffering = 0;
+
+ if (buf) {
+ rc = ngx_http_v2_process_request_body(r, buf->pos,
+ buf->last - buf->pos, 1);
+ ngx_pfree(r->pool, buf->start);
+ return rc;
+ }
+
return ngx_http_v2_process_request_body(r, NULL, 0, 1);
}
- if (len) {
- if (r->request_body_no_buffering) {
- stream->recv_window = (size_t) len;
+ if (buf) {
+ rc = ngx_http_v2_process_request_body(r, buf->pos,
+ buf->last - buf->pos, 0);
- } else {
- stream->no_flow_control = 1;
- stream->recv_window = NGX_HTTP_V2_MAX_WINDOW;
+ ngx_pfree(r->pool, buf->start);
+
+ if (rc != NGX_OK) {
+ stream->skip_data = 1;
+ return rc;
}
+ }
- if (ngx_http_v2_send_window_update(stream->connection, stream->node->id,
- stream->recv_window)
+ if (r->request_body_no_buffering) {
+ size = len - h2scf->preread_size;
+
+ } else {
+ stream->no_flow_control = 1;
+ size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window;
+ }
+
+ if (size) {
+ if (ngx_http_v2_send_window_update(stream->connection,
+ stream->node->id, size)
== NGX_ERROR)
{
stream->skip_data = 1;
@@ -3508,9 +3568,13 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r,
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
+
+ stream->recv_window += size;
}
- ngx_add_timer(r->connection->read, clcf->client_body_timeout);
+ if (!buf) {
+ 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;
@@ -3529,13 +3593,8 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,
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;
+ rb = r->request_body;
buf = rb->buf;
if (size) {
@@ -3789,7 +3848,14 @@ ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r)
window -= h2c->state.length;
}
- if (window == stream->recv_window) {
+ if (window <= stream->recv_window) {
+ if (window < stream->recv_window) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "http2 negative window update");
+ stream->skip_data = 1;
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
return NGX_AGAIN;
}