From 42ca3a4576a32d0a912b0bba4088b8169f55ab2d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 23 Sep 2025 15:03:52 +0400 Subject: [PATCH] CONNECT method support for HTTP/1.1. The change allows modules to use the CONNECT method with HTTP/1.1 requests. To do so, they need to set the "allow_connect" flag in the core server configuration. --- .../modules/ngx_http_chunked_filter_module.c | 4 +- src/http/ngx_http_core_module.h | 1 + src/http/ngx_http_parse.c | 40 ++++++++++++++++++- src/http/ngx_http_request.c | 8 +++- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c index 4d6fd3eed..ea5cbe6b3 100644 --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -66,7 +66,9 @@ ngx_http_chunked_header_filter(ngx_http_request_t *r) || r->headers_out.status == NGX_HTTP_NO_CONTENT || r->headers_out.status < NGX_HTTP_OK || r != r->main - || r->method == NGX_HTTP_HEAD) + || r->method == NGX_HTTP_HEAD + || (r->method == NGX_HTTP_CONNECT + && r->headers_out.status < NGX_HTTP_SPECIAL_RESPONSE)) { return ngx_http_next_header_filter(r); } diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index a794144aa..9be565373 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -206,6 +206,7 @@ typedef struct { #if (NGX_PCRE) unsigned captures:1; #endif + unsigned allow_connect:1; ngx_http_core_loc_conf_t **named_locations; } ngx_http_core_srv_conf_t; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 68f604e10..4dfeb4bcf 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -111,6 +111,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) sw_schema, sw_schema_slash, sw_schema_slash_slash, + sw_spaces_before_host, sw_host_start, sw_host, sw_host_end, @@ -158,6 +159,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) if (ch == ' ') { r->method_end = p - 1; m = r->request_start; + state = sw_spaces_before_uri; switch (p - m) { @@ -247,6 +249,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) if (ngx_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', ' ')) { r->method = NGX_HTTP_CONNECT; + state = sw_spaces_before_host; } break; @@ -269,7 +272,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) break; } - state = sw_spaces_before_uri; break; } @@ -345,6 +347,14 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) } break; + case sw_spaces_before_host: + + if (ch == ' ') { + break; + } + + /* fall through */ + case sw_host_start: r->host_start = p; @@ -375,6 +385,15 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) r->host_end = p; + if (r->method == NGX_HTTP_CONNECT) { + if (ch == ':') { + state = sw_port; + break; + } + + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + switch (ch) { case ':': state = sw_port; @@ -454,6 +473,15 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) break; } + if (r->method == NGX_HTTP_CONNECT) { + if (ch == ' ') { + state = sw_http_09; + break; + } + + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + switch (ch) { case '/': r->uri_start = p; @@ -689,6 +717,16 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) case sw_http_HTTP: switch (ch) { case '/': + + /* + * use single "/" from request line to preserve pointers, + * if request line will be copied to large client buffer + */ + if (r->method == NGX_HTTP_CONNECT) { + r->uri_start = p; + r->uri_end = p + 1; + } + state = sw_first_major_digit; break; default: diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 7f2d04783..533af452f 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1997,6 +1997,8 @@ ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r) { + ngx_http_core_srv_conf_t *cscf; + if (r->headers_in.server.len == 0 && ngx_http_set_virtual_server(r, &r->headers_in.server) == NGX_ERROR) @@ -2065,7 +2067,11 @@ ngx_http_process_request_header(ngx_http_request_t *r) } } - if (r->method == NGX_HTTP_CONNECT) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (r->method == NGX_HTTP_CONNECT + && (r->http_version != NGX_HTTP_VERSION_11 || !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);