# HG changeset patch # User Roman Arutyunyan # Date 1611322446 -10800 # Node ID e1eb7f4ca9f10dd9ee2e7a0baa3b25e91aa87b0b # Parent 3443ee341cc176a86d898798ed30aa85f0aeb711 HTTP/3: refactored request parser. The change reduces diff to the default branch for src/http/ngx_http_request.c and src/http/ngx_http_parse.c. diff --git a/src/http/modules/ngx_http_quic_module.c b/src/http/modules/ngx_http_quic_module.c --- a/src/http/modules/ngx_http_quic_module.c +++ b/src/http/modules/ngx_http_quic_module.c @@ -175,6 +175,43 @@ static ngx_http_variable_t ngx_http_qui }; +ngx_int_t +ngx_http_quic_init(ngx_connection_t *c) +{ + ngx_quic_conf_t *qcf; + ngx_http_connection_t *hc, *phc; + ngx_http_core_loc_conf_t *clcf; + + hc = c->data; + + hc->ssl = 1; + + if (c->quic == NULL) { + c->log->connection = c->number; + + qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_quic_module); + + ngx_quic_run(c, qcf); + + return NGX_DONE; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http init quic stream"); + + phc = c->quic->parent->data; + + if (phc->ssl_servername) { + hc->ssl_servername = phc->ssl_servername; + hc->conf_ctx = phc->conf_ctx; + + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + ngx_set_connection_log(c, clcf->error_log); + } + + return NGX_OK; +} + + static ngx_int_t ngx_http_variable_quic(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) diff --git a/src/http/modules/ngx_http_quic_module.h b/src/http/modules/ngx_http_quic_module.h --- a/src/http/modules/ngx_http_quic_module.h +++ b/src/http/modules/ngx_http_quic_module.h @@ -18,7 +18,7 @@ #define NGX_HTTP_QUIC_ALPN_DRAFT_FMT "\x05hq-%02uD" -extern ngx_module_t ngx_http_quic_module; +ngx_int_t ngx_http_quic_init(ngx_connection_t *c); #endif /* _NGX_HTTP_QUIC_H_INCLUDED_ */ 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 @@ -134,6 +134,11 @@ void ngx_http_handler(ngx_http_request_t void ngx_http_run_posted_requests(ngx_connection_t *c); ngx_int_t ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr); +ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r, + ngx_str_t *host); +ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, + ngx_uint_t alloc); +void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc); 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 @@ -143,7 +143,6 @@ ngx_http_parse_request_line(ngx_http_req /* HTTP methods: GET, HEAD, POST */ case sw_start: - r->parse_start = p; r->request_start = p; if (ch == CR || ch == LF) { @@ -896,7 +895,6 @@ ngx_http_parse_header_line(ngx_http_requ /* first char */ case sw_start: - r->parse_start = p; r->header_name_start = p; r->invalid_header = 0; 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 @@ -31,10 +31,6 @@ static ngx_int_t ngx_http_process_connec static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); -static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, - ngx_uint_t alloc); -static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r, - ngx_str_t *host); static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c, ngx_http_virtual_names_t *virtual_names, ngx_str_t *host, ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp); @@ -52,7 +48,6 @@ static void ngx_http_keepalive_handler(n static void ngx_http_set_lingering_close(ngx_connection_t *c); static void ngx_http_lingering_close_handler(ngx_event_t *ev); static ngx_int_t ngx_http_post_action(ngx_http_request_t *r); -static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error); static void ngx_http_log_request(ngx_http_request_t *r); static u_char *ngx_http_log_error_handler(ngx_http_request_t *r, @@ -303,54 +298,11 @@ ngx_http_init_connection(ngx_connection_ hc->conf_ctx = hc->addr_conf->default_server->ctx; #if (NGX_HTTP_QUIC) - if (hc->addr_conf->quic) { - ngx_quic_conf_t *qcf; - ngx_http_connection_t *phc; - ngx_http_core_loc_conf_t *clcf; - - hc->ssl = 1; - -#if (NGX_HTTP_V3) - - if (hc->addr_conf->http3) { - ngx_int_t rc; - - rc = ngx_http_v3_init_connection(c); - - if (rc == NGX_ERROR) { - ngx_http_close_connection(c); - return; - } - - if (rc == NGX_DONE) { - return; - } - } - -#endif - - if (c->quic == NULL) { - c->log->connection = c->number; - - qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, - ngx_http_quic_module); - ngx_quic_run(c, qcf); + if (ngx_http_quic_init(c) == NGX_DONE) { return; } - - phc = c->quic->parent->data; - - if (phc->ssl_servername) { - hc->ssl_servername = phc->ssl_servername; - hc->conf_ctx = phc->conf_ctx; - - clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, - ngx_http_core_module); - ngx_set_connection_log(c, clcf->error_log); - } } - #endif ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); @@ -380,6 +332,13 @@ ngx_http_init_connection(ngx_connection_ } #endif +#if (NGX_HTTP_V3) + if (hc->addr_conf->http3) { + ngx_http_v3_init(c); + return; + } +#endif + #if (NGX_HTTP_SSL) { ngx_http_ssl_srv_conf_t *sscf; @@ -669,12 +628,6 @@ ngx_http_alloc_request(ngx_connection_t r->method = NGX_HTTP_UNKNOWN; r->http_version = NGX_HTTP_VERSION_10; -#if (NGX_HTTP_V3) - if (hc->addr_conf->http3) { - r->http_version = NGX_HTTP_VERSION_30; - } -#endif - r->headers_in.content_length_n = -1; r->headers_in.keep_alive_n = -1; r->headers_out.content_length_n = -1; @@ -1140,16 +1093,7 @@ ngx_http_process_request_line(ngx_event_ } } - switch (r->http_version) { -#if (NGX_HTTP_V3) - case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_request(r, r->header_in); - break; -#endif - - default: /* HTTP/1.x */ - rc = ngx_http_parse_request_line(r, r->header_in); - } + rc = ngx_http_parse_request_line(r, r->header_in); if (rc == NGX_OK) { @@ -1157,7 +1101,7 @@ ngx_http_process_request_line(ngx_event_ r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; - r->request_length = r->header_in->pos - r->parse_start; + r->request_length = r->header_in->pos - r->request_start; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request line: \"%V\"", &r->request_line); @@ -1234,15 +1178,6 @@ ngx_http_process_request_line(ngx_event_ break; } - if (rc == NGX_BUSY) { - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - break; - } - if (rc != NGX_AGAIN) { /* there was error while a request line parsing */ @@ -1272,8 +1207,8 @@ ngx_http_process_request_line(ngx_event_ } if (rv == NGX_DECLINED) { - r->request_line.len = r->header_in->end - r->parse_start; - r->request_line.data = r->parse_start; + r->request_line.len = r->header_in->end - r->request_start; + r->request_line.data = r->request_start; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long URI"); @@ -1437,7 +1372,7 @@ ngx_http_process_request_headers(ngx_eve cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - rc = NGX_OK; + rc = NGX_AGAIN; for ( ;; ) { @@ -1453,7 +1388,7 @@ ngx_http_process_request_headers(ngx_eve } if (rv == NGX_DECLINED) { - p = r->parse_start; + p = r->header_name_start; r->lingering_close = 1; @@ -1473,7 +1408,7 @@ ngx_http_process_request_headers(ngx_eve ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long header line: \"%*s...\"", - len, r->parse_start); + len, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); @@ -1491,32 +1426,21 @@ ngx_http_process_request_headers(ngx_eve /* the host header could change the server configuration context */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - switch (r->http_version) { -#if (NGX_HTTP_V3) - case NGX_HTTP_VERSION_30: - rc = ngx_http_v3_parse_header(r, r->header_in, - cscf->underscores_in_headers); - break; -#endif - - default: /* HTTP/1.x */ - rc = ngx_http_parse_header_line(r, r->header_in, - cscf->underscores_in_headers); - } + rc = ngx_http_parse_header_line(r, r->header_in, + cscf->underscores_in_headers); if (rc == NGX_OK) { - r->request_length += r->header_in->pos - r->parse_start; + r->request_length += r->header_in->pos - r->header_name_start; if (r->invalid_header && cscf->ignore_invalid_headers) { /* there was error while a header line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid header line: \"%*s: %*s\"", - r->header_name_end - r->header_name_start, - r->header_name_start, - r->header_end - r->header_start, r->header_start); + "client sent invalid header line: \"%*s\"", + r->header_end - r->header_name_start, + r->header_name_start); continue; } @@ -1532,17 +1456,11 @@ ngx_http_process_request_headers(ngx_eve h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; - - if (h->key.data[h->key.len]) { - h->key.data[h->key.len] = '\0'; - } + h->key.data[h->key.len] = '\0'; h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; - - if (h->value.data[h->value.len]) { - h->value.data[h->value.len] = '\0'; - } + h->value.data[h->value.len] = '\0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { @@ -1578,7 +1496,7 @@ ngx_http_process_request_headers(ngx_eve ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done"); - r->request_length += r->header_in->pos - r->parse_start; + r->request_length += r->header_in->pos - r->header_name_start; r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; @@ -1693,7 +1611,7 @@ ngx_http_alloc_large_header_buffer(ngx_h return NGX_OK; } - old = r->parse_start; + old = request_line ? r->request_start : r->header_name_start; cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); @@ -1771,14 +1689,6 @@ ngx_http_alloc_large_header_buffer(ngx_h b->pos = new + (r->header_in->pos - old); b->last = new + (r->header_in->pos - old); - r->parse_start = new; - - r->header_in = b; - - if (r->http_version > NGX_HTTP_VERSION_11) { - return NGX_OK; - } - if (request_line) { r->request_start = new; @@ -1827,6 +1737,8 @@ ngx_http_alloc_large_header_buffer(ngx_h r->header_end = new + (r->header_end - old); } + r->header_in = b; + return NGX_OK; } @@ -2047,46 +1959,13 @@ ngx_http_process_request_header(ngx_http return NGX_ERROR; } - if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) { + if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent HTTP/1.1 request without \"Host\" header"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return NGX_ERROR; } - if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_20) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent HTTP/2 request without " - "\":authority\" or \"Host\" header"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; - } - - if (r->http_version == NGX_HTTP_VERSION_30) { - if (r->headers_in.server.len == 0) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent HTTP/3 request without " - "\":authority\" or \"Host\" header"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; - } - - if (r->headers_in.host) { - if (r->headers_in.host->value.len != r->headers_in.server.len - || ngx_memcmp(r->headers_in.host->value.data, - r->headers_in.server.data, - r->headers_in.server.len) - != 0) - { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent HTTP/3 request with different " - "values of \":authority\" and \"Host\" headers"); - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); - return NGX_ERROR; - } - } - } - if (r->headers_in.content_length) { r->headers_in.content_length_n = ngx_atoof(r->headers_in.content_length->value.data, @@ -2125,12 +2004,6 @@ ngx_http_process_request_header(ngx_http } } -#if (NGX_HTTP_V3) - if (r->http_version == NGX_HTTP_VERSION_30) { - r->headers_in.chunked = 1; - } -#endif - if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) { if (r->headers_in.keep_alive) { r->headers_in.keep_alive_n = @@ -2235,7 +2108,7 @@ ngx_http_process_request(ngx_http_reques } -static ngx_int_t +ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc) { u_char *h, ch; @@ -2326,7 +2199,7 @@ ngx_http_validate_host(ngx_str_t *host, } -static ngx_int_t +ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host) { ngx_int_t rc; @@ -3744,7 +3617,7 @@ ngx_http_post_action(ngx_http_request_t } -static void +void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_connection_t *c; @@ -3965,15 +3838,15 @@ ngx_http_log_error_handler(ngx_http_requ len -= p - buf; buf = p; - if (r->request_line.data == NULL && r->parse_start) { - for (p = r->parse_start; p < r->header_in->last; p++) { + if (r->request_line.data == NULL && r->request_start) { + for (p = r->request_start; p < r->header_in->last; p++) { if (*p == CR || *p == LF) { break; } } - r->request_line.len = p - r->parse_start; - r->request_line.data = r->parse_start; + r->request_line.len = p - r->request_start; + r->request_line.data = r->request_start; } if (r->request_line.len) { diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -325,6 +325,7 @@ typedef struct { unsigned ssl:1; unsigned proxy_protocol:1; + unsigned http3:1; } ngx_http_connection_t; @@ -581,7 +582,6 @@ struct ngx_http_request_s { * via ngx_http_ephemeral_t */ - u_char *parse_start; u_char *uri_start; u_char *uri_end; u_char *uri_ext; 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 @@ -127,17 +127,11 @@ typedef struct { uint64_t next_push_id; uint64_t max_push_id; - ngx_uint_t settings_sent; - /* unsigned settings_sent:1; */ ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; } ngx_http_v3_connection_t; -ngx_int_t ngx_http_v3_init_connection(ngx_connection_t *c); - -ngx_int_t ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b); -ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, - ngx_uint_t allow_underscores); +void ngx_http_v3_init(ngx_connection_t *c); ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, ngx_http_chunked_t *ctx); @@ -157,6 +151,8 @@ uintptr_t ngx_http_v3_encode_header_pbi( uintptr_t ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, u_char *data, size_t len); +ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); +void ngx_http_v3_init_uni_stream(ngx_connection_t *c); ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id); ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, 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 @@ -10,8 +10,13 @@ #include +static void ngx_http_v3_process_request(ngx_event_t *rev); +static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, + ngx_str_t *name, ngx_str_t *value); static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); +static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r); static const struct { @@ -37,230 +42,256 @@ static const struct { }; -ngx_int_t -ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) +void +ngx_http_v3_init(ngx_connection_t *c) { - size_t len; - u_char *p; - ngx_int_t rc, n; - ngx_str_t *name, *value; + size_t size; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_http_request_t *r; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + + if (ngx_http_v3_init_session(c) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "internal error"); + ngx_http_close_connection(c); + return; + } + + if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { + ngx_http_v3_init_uni_stream(c); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init request stream"); + + hc = c->data; + + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); + + size = cscf->client_header_buffer_size; + + b = c->buffer; + + if (b == NULL) { + b = ngx_create_temp_buf(c->pool, size); + if (b == NULL) { + ngx_http_close_connection(c); + return; + } + + c->buffer = b; + + } else if (b->start == NULL) { + + b->start = ngx_palloc(c->pool, size); + if (b->start == NULL) { + ngx_http_close_connection(c); + return; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + } + + c->log->action = "reading client request"; + + r = ngx_http_create_request(c); + if (r == NULL) { + ngx_http_close_connection(c); + return; + } + + r->http_version = NGX_HTTP_VERSION_30; + + c->data = r; + + rev = c->read; + rev->handler = ngx_http_v3_process_request; + + ngx_http_v3_process_request(rev); +} + + +static void +ngx_http_v3_process_request(ngx_event_t *rev) +{ + ssize_t n; + ngx_buf_t *b; + ngx_int_t rc; ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_core_srv_conf_t *cscf; ngx_http_v3_parse_headers_t *st; - c = r->connection; + c = rev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http3 process request"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + st = r->h3_parse; if (st == NULL) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header"); - st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_headers_t)); if (st == NULL) { - goto failed; + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return; } r->h3_parse = st; - r->parse_start = b->pos; - r->state = 1; } - while (b->pos < b->last) { + b = r->header_in; + + for ( ;; ) { + + if (b->pos == b->last) { + + if (!rev->ready) { + break; + } + + n = c->recv(c, b->start, b->end - b->start); + + if (n == NGX_AGAIN) { + if (!rev->timer_set) { + cscf = ngx_http_get_module_srv_conf(r, + ngx_http_core_module); + ngx_add_timer(rev, cscf->client_header_timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + break; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client prematurely closed connection"); + } + + if (n == 0 || n == NGX_ERROR) { + c->error = 1; + c->log->action = "reading client request"; + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + break; + } + + b->pos = b->start; + b->last = b->start + n; + } + rc = ngx_http_v3_parse_headers(c, st, *b->pos); if (rc > 0) { ngx_http_v3_finalize_connection(c, rc, "could not parse request headers"); - goto failed; + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + break; } if (rc == NGX_ERROR) { - goto failed; + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "internal error"); + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + break; } if (rc == NGX_BUSY) { - return NGX_BUSY; + if (rev->error) { + ngx_http_close_request(r, NGX_HTTP_CLOSE); + break; + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + break; } b->pos++; + r->request_length++; if (rc == NGX_AGAIN) { continue; } - name = &st->header_rep.header.name; - value = &st->header_rep.header.value; - - n = ngx_http_v3_process_pseudo_header(r, name, value); - - if (n == NGX_ERROR) { - goto failed; - } + /* rc == NGX_OK || rc == NGX_DONE */ - if (n == NGX_OK && rc == NGX_OK) { - continue; - } - - len = r->method_name.len + 1 - + (r->uri_end - r->uri_start) + 1 - + sizeof("HTTP/3.0") - 1; - - p = ngx_pnalloc(c->pool, len); - if (p == NULL) { - goto failed; + if (ngx_http_v3_process_header(r, &st->header_rep.header.name, + &st->header_rep.header.value) + != NGX_OK) + { + break; } - r->request_start = p; + if (rc == NGX_DONE) { + if (ngx_http_v3_process_request_header(r) != NGX_OK) { + break; + } - p = ngx_cpymem(p, r->method_name.data, r->method_name.len); - r->method_end = p - 1; - *p++ = ' '; - p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start); - *p++ = ' '; - r->http_protocol.data = p; - p = ngx_cpymem(p, "HTTP/3.0", sizeof("HTTP/3.0") - 1); - - r->request_end = p; - r->state = 0; - - return NGX_OK; + ngx_http_process_request(r); + break; + } } - return NGX_AGAIN; + ngx_http_run_posted_requests(c); -failed: - - return NGX_HTTP_PARSE_INVALID_REQUEST; + return; } -ngx_int_t -ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, - ngx_uint_t allow_underscores) +static ngx_int_t +ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, + ngx_str_t *value) { - u_char ch; - ngx_int_t rc; - ngx_str_t *name, *value; - ngx_uint_t hash, i, n; - ngx_connection_t *c; - ngx_http_v3_parse_headers_t *st; - enum { - sw_start = 0, - sw_done, - sw_next, - sw_header - }; - - c = r->connection; - st = r->h3_parse; - - switch (r->state) { - - case sw_start: - r->parse_start = b->pos; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; - if (st->state) { - r->state = sw_next; - goto done; - } - - name = &st->header_rep.header.name; - - if (name->len && name->data[0] != ':') { - r->state = sw_done; - goto done; - } + if (name->len && name->data[0] == ':') { + return ngx_http_v3_process_pseudo_header(r, name, value); + } - /* fall through */ - - case sw_done: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 parse header done"); - return NGX_HTTP_PARSE_HEADER_DONE; - - case sw_next: - r->parse_start = b->pos; - r->invalid_header = 0; - break; - - case sw_header: - break; + if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { + return NGX_ERROR; } - while (b->pos < b->last) { - rc = ngx_http_v3_parse_headers(c, st, *b->pos++); - - if (rc > 0) { - ngx_http_v3_finalize_connection(c, rc, - "could not parse request headers"); - return NGX_HTTP_PARSE_INVALID_HEADER; - } - - if (rc == NGX_ERROR) { - return NGX_HTTP_PARSE_INVALID_HEADER; - } - - if (rc == NGX_DONE) { - r->state = sw_done; - goto done; - } - - if (rc == NGX_OK) { - r->state = sw_next; - goto done; - } + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; } - r->state = sw_header; - return NGX_AGAIN; - -done: - - name = &st->header_rep.header.name; - value = &st->header_rep.header.value; - - r->header_name_start = name->data; - r->header_name_end = name->data + name->len; - r->header_start = value->data; - r->header_end = value->data + value->len; - - hash = 0; - i = 0; - - for (n = 0; n < name->len; n++) { - ch = name->data[n]; + h->key = *name; + h->value = *value; + h->lowcase_key = h->key.data; + h->hash = ngx_hash_key(h->key.data, h->key.len); - if (ch >= 'A' && ch <= 'Z') { - /* - * A request or response containing uppercase - * header field names MUST be treated as malformed - */ - return NGX_HTTP_PARSE_INVALID_HEADER; - } - - if (ch == '\0') { - return NGX_HTTP_PARSE_INVALID_HEADER; - } + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - if (ch == '_' && !allow_underscores) { - r->invalid_header = 1; - continue; - } + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); - if ((ch < 'a' || ch > 'z') - && (ch < '0' || ch > '9') - && ch != '-' && ch != '_') - { - r->invalid_header = 1; - continue; - } - - hash = ngx_hash(hash, ch); - r->lowcase_header[i++] = ch; - i &= (NGX_HTTP_LC_HEADER_LEN - 1); + if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { + return NGX_ERROR; } - r->header_hash = hash; - r->lowcase_index = i; - + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 header: \"%V: %V\"", name, value); return NGX_OK; } @@ -269,75 +300,210 @@ static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value) { - ngx_uint_t i; - ngx_connection_t *c; + ngx_uint_t i; - if (name->len == 0 || name->data[0] != ':') { - return NGX_DONE; + if (r->request_line.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent out of order pseudo-headers"); + goto failed; } - c = r->connection; + if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) { - if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) { r->method_name = *value; for (i = 0; i < sizeof(ngx_http_v3_methods) / sizeof(ngx_http_v3_methods[0]); i++) { if (value->len == ngx_http_v3_methods[i].name.len - && ngx_strncmp(value->data, ngx_http_v3_methods[i].name.data, - value->len) == 0) + && ngx_strncmp(value->data, + ngx_http_v3_methods[i].name.data, value->len) + == 0) { r->method = ngx_http_v3_methods[i].method; break; } } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 method \"%V\" %ui", value, r->method); return NGX_OK; } if (name->len == 5 && ngx_strncmp(name->data, ":path", 5) == 0) { + r->uri_start = value->data; r->uri_end = value->data + value->len; if (ngx_http_parse_uri(r) != NGX_OK) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid :path header: \"%V\"", value); - return NGX_ERROR; + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \":path\" header: \"%V\"", + value); + goto failed; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 path \"%V\"", value); - return NGX_OK; } if (name->len == 7 && ngx_strncmp(name->data, ":scheme", 7) == 0) { - r->schema_start = value->data; - r->schema_end = value->data + value->len; + + r->schema = *value; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 schema \"%V\"", value); - return NGX_OK; } if (name->len == 10 && ngx_strncmp(name->data, ":authority", 10) == 0) { + r->host_start = value->data; r->host_end = value->data + value->len; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http3 authority \"%V\"", value); + return NGX_OK; + } + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent unknown pseudo-header \"%V\"", name); + +failed: + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r) +{ + size_t len; + u_char *p; + ngx_int_t rc; + ngx_str_t host; + + if (r->request_line.len) { return NGX_OK; } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 unknown pseudo header \"%V\" \"%V\"", name, value); + len = r->method_name.len + 1 + + (r->uri_end - r->uri_start) + 1 + + sizeof("HTTP/3.0") - 1; + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + r->request_line.data = p; + + p = ngx_cpymem(p, r->method_name.data, r->method_name.len); + *p++ = ' '; + p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start); + *p++ = ' '; + p = ngx_cpymem(p, "HTTP/3.0", sizeof("HTTP/3.0") - 1); + + r->request_line.len = p - r->request_line.data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 request line: \"%V\"", &r->request_line); + + ngx_str_set(&r->http_protocol, "HTTP/3.0"); + + if (ngx_http_process_request_uri(r) != NGX_OK) { + return NGX_ERROR; + } + + if (r->host_end) { + + host.len = r->host_end - r->host_start; + host.data = r->host_start; + + rc = ngx_http_validate_host(&host, r->pool, 0); + + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid host in request line"); + goto failed; + } + + if (rc == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) { + return NGX_ERROR; + } + + r->headers_in.server = host; + } + + if (ngx_list_init(&r->headers_in.headers, r->pool, 20, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } return NGX_OK; + +failed: + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_v3_process_request_header(ngx_http_request_t *r) +{ + if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { + return NGX_ERROR; + } + + if (r->headers_in.server.len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent neither \":authority\" nor \"Host\" header"); + goto failed; + } + + if (r->headers_in.host) { + if (r->headers_in.host->value.len != r->headers_in.server.len + || ngx_memcmp(r->headers_in.host->value.data, + r->headers_in.server.data, + r->headers_in.server.len) + != 0) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent \":authority\" and \"Host\" headers " + "with different values"); + goto failed; + } + } + + if (r->headers_in.content_length) { + r->headers_in.content_length_n = + ngx_atoof(r->headers_in.content_length->value.data, + r->headers_in.content_length->value.len); + + if (r->headers_in.content_length_n == NGX_ERROR) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \"Content-Length\" header"); + goto failed; + } + } + + return NGX_OK; + +failed: + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -40,44 +40,49 @@ static ngx_int_t ngx_http_v3_send_settin ngx_int_t -ngx_http_v3_init_connection(ngx_connection_t *c) +ngx_http_v3_init_session(ngx_connection_t *c) { - ngx_http_connection_t *hc; - ngx_http_v3_uni_stream_t *us; + ngx_connection_t *pc; + ngx_http_connection_t *phc; ngx_http_v3_connection_t *h3c; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init connection"); - - hc = c->data; + pc = c->quic->parent; + phc = pc->data; - if (c->quic == NULL) { - h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t)); - if (h3c == NULL) { - return NGX_ERROR; - } - - h3c->hc = *hc; - - ngx_queue_init(&h3c->blocked); - ngx_queue_init(&h3c->pushing); - - c->data = h3c; + if (phc->http3) { return NGX_OK; } - if (ngx_http_v3_send_settings(c) == NGX_ERROR) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init session"); + + h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_connection_t)); + if (h3c == NULL) { return NGX_ERROR; } - if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) { - return NGX_OK; - } + h3c->hc = *phc; + h3c->hc.http3 = 1; + + ngx_queue_init(&h3c->blocked); + ngx_queue_init(&h3c->pushing); + + pc->data = h3c; + + return ngx_http_v3_send_settings(c); +} + + +void +ngx_http_v3_init_uni_stream(ngx_connection_t *c) +{ + ngx_http_v3_uni_stream_t *us; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - NULL); - return NGX_ERROR; + ngx_http_close_connection(c); + return; } us->index = -1; @@ -88,8 +93,6 @@ ngx_http_v3_init_connection(ngx_connecti c->write->handler = ngx_http_v3_dummy_write_handler; ngx_http_v3_read_uni_stream_type(c->read); - - return NGX_DONE; } @@ -478,10 +481,6 @@ ngx_http_v3_send_settings(ngx_connection h3c = c->quic->parent->data; - if (h3c->settings_sent) { - return NGX_OK; - } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); @@ -512,17 +511,12 @@ ngx_http_v3_send_settings(ngx_connection goto failed; } - h3c->settings_sent = 1; - return NGX_OK; failed: ngx_http_v3_close_uni_stream(cc); - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, - "could not send settings"); - return NGX_ERROR; }