# HG changeset patch # User Roman Arutyunyan # Date 1585327266 -10800 # Node ID 5649079a41f4c5bf7b1a1bbf663bbc223d51240d # Parent 32db41d603cd29261b0c65ad85a5a5470f092c84 Parsing HTTP/3 request body. diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -63,6 +63,9 @@ struct ngx_http_chunked_s { ngx_uint_t state; off_t size; off_t length; +#if (NGX_HTTP_V3) + void *h3_parse; +#endif }; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -2185,6 +2185,12 @@ ngx_http_parse_chunked(ngx_http_request_ sw_trailer_header_almost_done } state; +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + return ngx_http_v3_parse_request_body(r, b, ctx); + } +#endif + state = ctx->state; if (state == sw_chunk_data && ctx->size == 0) { @@ -2371,6 +2377,11 @@ ngx_http_parse_chunked(ngx_http_request_ } } + if (b->last_buf) { + /* XXX client prematurely closed connection */ + return NGX_ERROR; + } + data: ctx->state = state; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -709,6 +709,7 @@ ngx_http_alloc_request(ngx_connection_t #if (NGX_HTTP_V3) if (hc->quic) { r->http_version = NGX_HTTP_VERSION_30; + r->headers_in.chunked = 1; } #endif diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -343,11 +343,10 @@ ngx_http_do_read_client_request_body(ngx } if (n == 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client prematurely closed connection"); + rb->buf->last_buf = 1; } - if (n == 0 || n == NGX_ERROR) { + if (n == NGX_ERROR) { c->error = 1; return NGX_HTTP_BAD_REQUEST; } @@ -355,7 +354,7 @@ ngx_http_do_read_client_request_body(ngx rb->buf->last += n; r->request_length += n; - if (n == rest) { + if (n == rest || n == 0) { /* pass buffer to request body filter chain */ out.buf = rb->buf; @@ -805,11 +804,7 @@ ngx_http_test_expect(ngx_http_request_t if (r->expect_tested || r->headers_in.expect == NULL - || r->http_version < NGX_HTTP_VERSION_11 -#if (NGX_HTTP_V2) - || r->stream != NULL -#endif - ) + || r->http_version != NGX_HTTP_VERSION_11) { return NGX_OK; } @@ -914,6 +909,11 @@ ngx_http_request_body_length_filter(ngx_ b->last_buf = 1; } + if (cl->buf->last_buf && rb->rest > 0) { + /* XXX client prematurely closed connection */ + return NGX_ERROR; + } + *ll = tl; ll = &tl->next; } @@ -950,7 +950,16 @@ ngx_http_request_body_chunked_filter(ngx } r->headers_in.content_length_n = 0; - rb->rest = 3; + +#if (NGX_HTTP_V3) + if (r->http_version == NGX_HTTP_VERSION_30) { + rb->rest = 1; + + } else +#endif + { + rb->rest = 3; + } } out = NULL; diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -68,6 +68,8 @@ typedef struct { ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b); +ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_chunked_t *ctx); ngx_chain_t *ngx_http_v3_create_header(ngx_http_request_t *r); ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r); diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -1421,3 +1421,61 @@ done: st->state = sw_start; return NGX_DONE; } + + +ngx_int_t +ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, + u_char ch) +{ + enum { + sw_start = 0, + sw_type, + sw_length + }; + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data"); + + st->state = sw_type; + + /* fall through */ + + case sw_type: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + if (st->vlint.value != NGX_HTTP_V3_FRAME_DATA) { + return NGX_ERROR; + } + + st->state = sw_length; + break; + + case sw_length: + + if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { + break; + } + + st->length = st->vlint.value; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse data frame len:%ui", st->length); + + goto done; + } + + return NGX_AGAIN; + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data done"); + + st->state = sw_start; + return NGX_DONE; +} diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -105,6 +105,13 @@ typedef struct { } ngx_http_v3_parse_control_t; +typedef struct { + ngx_uint_t state; + ngx_uint_t length; + ngx_http_v3_parse_varlen_int_t vlint; +} ngx_http_v3_parse_data_t; + + ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, ngx_http_v3_parse_varlen_int_t *st, u_char ch); ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, @@ -141,5 +148,8 @@ ngx_int_t ngx_http_v3_parse_header_iwnr( ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch); +ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, + ngx_http_v3_parse_data_t *st, u_char ch); + #endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */ diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -241,6 +241,69 @@ ngx_http_v3_process_pseudo_header(ngx_ht } +ngx_int_t +ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_chunked_t *ctx) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_v3_parse_data_t *st; + + c = r->connection; + st = ctx->h3_parse; + + if (st == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse request body"); + + st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_data_t)); + if (st == NULL) { + goto failed; + } + + r->h3_parse = st; + } + + if (ctx->size) { + return NGX_OK; + } + + while (b->pos < b->last) { + rc = ngx_http_v3_parse_data(c, st, *b->pos++); + + if (rc == NGX_ERROR) { + goto failed; + } + + if (rc == NGX_AGAIN) { + continue; + } + + /* rc == NGX_DONE */ + + ctx->size = st->length; + return NGX_OK; + } + + if (!b->last_buf) { + ctx->length = 1; + return NGX_AGAIN; + } + + if (st->state) { + goto failed; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header done"); + + return NGX_DONE; + +failed: + + return NGX_ERROR; +} + + ngx_chain_t * ngx_http_v3_create_header(ngx_http_request_t *r) {