This commit is contained in:
Zihao FU 2026-05-12 14:46:18 -04:00 committed by GitHub
commit a5a2b5dacd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 261 additions and 43 deletions

View file

@ -427,6 +427,7 @@ struct ngx_http_request_s {
ngx_str_t method_name;
ngx_str_t http_protocol;
ngx_str_t schema;
ngx_str_t connect_protocol;
ngx_chain_t *out;
ngx_http_request_t *main;

View file

@ -79,7 +79,17 @@ ngx_http_read_client_request_body(ngx_http_request_t *r,
r->request_body = rb;
if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
if (r->headers_in.content_length_n < 0
&& !r->headers_in.chunked
#if (NGX_HTTP_V2)
&& !(r->stream && r->method == NGX_HTTP_CONNECT)
#endif
#if (NGX_HTTP_V3)
&& !(r->http_version == NGX_HTTP_VERSION_30
&& r->method == NGX_HTTP_CONNECT)
#endif
)
{
r->request_body_no_buffering = 0;
post_handler(r);
return NGX_OK;

View file

@ -154,6 +154,8 @@ static ngx_int_t ngx_http_v2_parse_scheme(ngx_http_request_t *r,
ngx_str_t *value);
static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r,
ngx_str_t *value);
static ngx_int_t ngx_http_v2_parse_protocol(ngx_http_request_t *r,
ngx_str_t *value);
static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r,
ngx_http_v2_header_t *header);
@ -3325,6 +3327,16 @@ ngx_http_v2_pseudo_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)
break;
case 8:
if (ngx_memcmp(header->name.data, "protocol",
sizeof("protocol") - 1)
== 0)
{
return ngx_http_v2_parse_protocol(r, &header->value);
}
break;
case 9:
if (ngx_memcmp(header->name.data, "authority", sizeof("authority") - 1)
== 0)
@ -3567,37 +3579,133 @@ ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value)
}
static ngx_int_t
ngx_http_v2_parse_protocol(ngx_http_request_t *r, ngx_str_t *value)
{
if (r->connect_protocol.len) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent duplicate :protocol header");
return NGX_DECLINED;
}
if (value->len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent empty :protocol header");
return NGX_DECLINED;
}
r->connect_protocol = *value;
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_construct_request_line(ngx_http_request_t *r)
{
u_char *p;
size_t len;
u_char *p;
ngx_uint_t classic_connect;
ngx_http_core_srv_conf_t *cscf;
static const u_char ending[] = " HTTP/2.0";
if (r->method_name.len == 0
|| r->schema.len == 0
|| r->unparsed_uri.len == 0)
{
if (r->method_name.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :method header");
} else if (r->schema.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :scheme header");
} else {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :path header");
}
if (r->method_name.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :method header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
r->request_line.len = r->method_name.len + 1
+ r->unparsed_uri.len
+ sizeof(ending) - 1;
if (r->method == NGX_HTTP_CONNECT) {
goto method_connect;
}
if (r->connect_protocol.len) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent :protocol header with non-CONNECT method");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
classic_connect = 0;
if (r->schema.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :scheme header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
if (r->unparsed_uri.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :path header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
len = r->unparsed_uri.len;
goto construct_request_line;
method_connect:
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
if (!cscf->allow_connect) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent CONNECT method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
return NGX_ERROR;
}
if (r->schema.len == 0 && r->unparsed_uri.len == 0
&& r->connect_protocol.len == 0)
{
classic_connect = 1;
if (r->headers_in.server.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :authority header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
len = r->headers_in.server.len;
goto construct_request_line;
}
classic_connect = 0;
if (r->schema.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :scheme header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
if (r->connect_protocol.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent extended CONNECT without :protocol header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
if (r->unparsed_uri.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :path header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
len = r->unparsed_uri.len;
construct_request_line:
r->request_line.len = r->method_name.len + 1 + len + sizeof(ending) - 1;
p = ngx_pnalloc(r->pool, r->request_line.len + 1);
if (p == NULL) {
@ -3611,7 +3719,18 @@ ngx_http_v2_construct_request_line(ngx_http_request_t *r)
*p++ = ' ';
p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);
if (classic_connect) {
p = ngx_cpymem(p, r->headers_in.server.data, r->headers_in.server.len);
r->uri_start = (u_char *) "/";
r->uri_end = r->uri_start + 1;
ngx_str_set(&r->uri, "/");
ngx_str_set(&r->unparsed_uri, "/");
r->valid_unparsed_uri = 1;
} else {
p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);
}
ngx_memcpy(p, ending, sizeof(ending));
@ -3920,12 +4039,6 @@ ngx_http_v2_run_request(ngx_http_request_t *r)
r->headers_in.chunked = 1;
}
if (r->method == NGX_HTTP_CONNECT) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0, "client sent CONNECT method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
goto failed;
}
if (r->method == NGX_HTTP_TRACE) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0, "client sent TRACE method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);

View file

@ -896,6 +896,27 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name,
return NGX_OK;
}
if (name->len == 9 && ngx_strncmp(name->data, ":protocol", 9) == 0) {
if (r->connect_protocol.len) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent duplicate \":protocol\" header");
goto failed;
}
if (value->len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent empty \":protocol\" header");
goto failed;
}
r->connect_protocol = *value;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http3 protocol \"%V\"", value);
return NGX_OK;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent unknown pseudo-header \"%V\"", name);
@ -909,10 +930,12 @@ failed:
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;
size_t len, target_len;
u_char *p;
ngx_int_t rc;
ngx_uint_t classic_connect;
ngx_str_t host;
ngx_http_core_srv_conf_t *cscf;
in_port_t port;
if (r->request_line.len) {
@ -925,6 +948,19 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r)
goto failed;
}
if (r->method == NGX_HTTP_CONNECT) {
goto method_connect;
}
if (r->connect_protocol.len) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent \":protocol\" header with non-CONNECT "
"method");
goto failed;
}
classic_connect = 0;
if (r->schema.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no \":scheme\" header");
@ -937,9 +973,66 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r)
goto failed;
}
len = r->method_name.len + 1
+ (r->uri_end - r->uri_start) + 1
+ sizeof("HTTP/3.0") - 1;
target_len = (size_t) (r->uri_end - r->uri_start);
goto construct_request_line;
method_connect:
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
if (!cscf->allow_connect) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent CONNECT method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
return NGX_ERROR;
}
if (r->schema.len == 0 && r->uri_start == NULL
&& r->connect_protocol.len == 0)
{
classic_connect = 1;
if (r->host_start == NULL) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no \":authority\" header");
goto failed;
}
r->uri_start = (u_char *) "/";
r->uri_end = r->uri_start + 1;
target_len = (size_t) (r->host_end - r->host_start);
goto construct_request_line;
}
classic_connect = 0;
if (r->schema.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no \":scheme\" header");
goto failed;
}
if (r->connect_protocol.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent extended CONNECT without "
"\":protocol\" header");
goto failed;
}
if (r->uri_start == NULL) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no \":path\" header");
goto failed;
}
target_len = (size_t) (r->uri_end - r->uri_start);
construct_request_line:
len = r->method_name.len + 1 + target_len + 1 + sizeof("HTTP/3.0") - 1;
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
@ -951,7 +1044,14 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r)
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);
if (classic_connect) {
p = ngx_cpymem(p, r->host_start, target_len);
} else {
p = ngx_cpymem(p, r->uri_start, target_len);
}
*p++ = ' ';
p = ngx_cpymem(p, "HTTP/3.0", sizeof("HTTP/3.0") - 1);
@ -1136,12 +1236,6 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r)
}
}
if (r->method == NGX_HTTP_CONNECT) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent CONNECT method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
return NGX_ERROR;
}
if (r->method == NGX_HTTP_TRACE) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent TRACE method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);