Mercurial > hg > nginx-quic
view src/http/v3/ngx_http_v3_request.c @ 8240:43f3574b3e6f quic
QUIC: fixed handling of clients connected to wildcard address.
The patch replaces c->send() occurences with c->send_chain(), because the
latter accounts for the local address, which may be different if the wildcard
listener is used.
Previously, server sent response to client using address different from
one client connected to.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Mon, 07 Dec 2020 14:06:00 +0300 |
parents | 1efee5e4194c |
children | 96eb6915d244 |
line wrap: on
line source
/* * Copyright (C) Roman Arutyunyan * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value); struct { ngx_str_t name; ngx_uint_t method; } ngx_http_v3_methods[] = { { ngx_string("GET"), NGX_HTTP_GET }, { ngx_string("POST"), NGX_HTTP_POST }, { ngx_string("HEAD"), NGX_HTTP_HEAD }, { ngx_string("OPTIONS"), NGX_HTTP_OPTIONS }, { ngx_string("PROPFIND"), NGX_HTTP_PROPFIND }, { ngx_string("PUT"), NGX_HTTP_PUT }, { ngx_string("MKCOL"), NGX_HTTP_MKCOL }, { ngx_string("DELETE"), NGX_HTTP_DELETE }, { ngx_string("COPY"), NGX_HTTP_COPY }, { ngx_string("MOVE"), NGX_HTTP_MOVE }, { ngx_string("PROPPATCH"), NGX_HTTP_PROPPATCH }, { ngx_string("LOCK"), NGX_HTTP_LOCK }, { ngx_string("UNLOCK"), NGX_HTTP_UNLOCK }, { ngx_string("PATCH"), NGX_HTTP_PATCH }, { ngx_string("TRACE"), NGX_HTTP_TRACE } }; ngx_int_t ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) { size_t len; u_char *p; ngx_int_t rc, n; ngx_str_t *name, *value; ngx_connection_t *c; ngx_http_v3_parse_headers_t *st; c = r->connection; 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; } r->h3_parse = st; r->parse_start = b->pos; r->state = 1; } 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"); goto failed; } if (rc == NGX_ERROR) { goto failed; } if (rc == NGX_BUSY) { return NGX_BUSY; } b->pos++; 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; } 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; } r->request_start = p; 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; } return NGX_AGAIN; failed: return NGX_HTTP_PARSE_INVALID_REQUEST; } ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t allow_underscores) { 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; 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; } /* 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; } 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; } } 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]; 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; } if (ch == '_' && !allow_underscores) { r->invalid_header = 1; continue; } 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); } r->header_hash = hash; r->lowcase_index = i; return NGX_OK; } 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; if (name->len == 0 || name->data[0] != ':') { return NGX_DONE; } c = r->connection; 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) { r->method = ngx_http_v3_methods[i].method; break; } } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->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_debug1(NGX_LOG_DEBUG_HTTP, c->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; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->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, "http3 authority \"%V\"", value); return NGX_OK; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 unknown pseudo header \"%V\" \"%V\"", name, value); return NGX_OK; } 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; enum { sw_start = 0, sw_skip }; 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; } ctx->h3_parse = st; } while (b->pos < b->last && ctx->size == 0) { rc = ngx_http_v3_parse_data(c, st, *b->pos++); if (rc > 0) { ngx_http_v3_finalize_connection(c, rc, "could not parse request body"); goto failed; } if (rc == NGX_ERROR) { goto failed; } if (rc == NGX_AGAIN) { ctx->state = sw_skip; continue; } if (rc == NGX_DONE) { return NGX_DONE; } /* rc == NGX_OK */ ctx->size = st->length; ctx->state = sw_start; } if (ctx->state == sw_skip) { ctx->length = 1; return NGX_AGAIN; } if (b->pos == b->last) { ctx->length = ctx->size; return NGX_AGAIN; } return NGX_OK; failed: return NGX_ERROR; }