]> git.feebdaed.xyz Git - 0xmirror/nginx.git/commitdiff
CONNECT method support for HTTP/1.1.
authorRoman Arutyunyan <arut@nginx.com>
Tue, 23 Sep 2025 11:03:52 +0000 (15:03 +0400)
committerRoman Arutyunyan <arutyunyan.roman@gmail.com>
Thu, 23 Oct 2025 14:40:05 +0000 (18:40 +0400)
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.

src/http/modules/ngx_http_chunked_filter_module.c
src/http/ngx_http_core_module.h
src/http/ngx_http_parse.c
src/http/ngx_http_request.c

index 4d6fd3eed0b3ce16c9792f0f542a8bef7c8ef8e9..ea5cbe6b3414caa757173a32390c9fe334fa08d6 100644 (file)
@@ -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);
     }
index a794144aa3089c936d5c4233ef31dd475e96bd0e..9be565373a68e2732d0ad592729e07d7dd2b2be6 100644 (file)
@@ -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;
index 68f604e10e56a7656f76cdace98bd9ca3d231c8a..4dfeb4bcfc46855b4b056eccffe43c95e6b975dc 100644 (file)
@@ -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:
index 7f2d04783b76afdc5570ae4c3cd72c44c2662048..533af452fca6a94d420ecc9241586f5830d4fcc8 100644 (file)
@@ -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);