Parsing HTTP/3 request body.

This commit is contained in:
Roman Arutyunyan 2020-03-27 19:41:06 +03:00
parent 732e383dd1
commit 48a1eeb5c2
8 changed files with 167 additions and 10 deletions

View file

@ -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
};

View file

@ -2185,6 +2185,12 @@ ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
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_t *r, ngx_buf_t *b,
}
}
if (b->last_buf) {
/* XXX client prematurely closed connection */
return NGX_ERROR;
}
data:
ctx->state = state;

View file

@ -709,6 +709,7 @@ ngx_http_alloc_request(ngx_connection_t *c)
#if (NGX_HTTP_V3)
if (hc->quic) {
r->http_version = NGX_HTTP_VERSION_30;
r->headers_in.chunked = 1;
}
#endif

View file

@ -343,11 +343,10 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
}
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_http_request_t *r)
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 *r)
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_http_request_t *r, ngx_chain_t *in)
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_http_request_t *r, ngx_chain_t *in)
}
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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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_connection_t *c,
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_ */

View file

@ -241,6 +241,69 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name,
}
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)
{