]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
merge of 1898146,1898173 from trunk:
authorStefan Eissing <icing@apache.org>
Fri, 4 Mar 2022 08:51:47 +0000 (08:51 +0000)
committerStefan Eissing <icing@apache.org>
Fri, 4 Mar 2022 08:51:47 +0000 (08:51 +0000)
  *) mod_http2: preserve the port number given in a HTTP/1.1
     request that was Upgraded to HTTP/2. Fixes PR65881.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1898586 13f79535-47bb-0310-9956-ffa450edef68

changes-entries/pr65881.txt [new file with mode: 0644]
modules/http2/h2_request.c
test/modules/http2/env.py
test/modules/http2/htdocs/cgi/hello.py
test/modules/http2/test_502_proxy_port.py [new file with mode: 0644]

diff --git a/changes-entries/pr65881.txt b/changes-entries/pr65881.txt
new file mode 100644 (file)
index 0000000..816158a
--- /dev/null
@@ -0,0 +1,3 @@
+  *) mod_http2: preserve the port number given in a HTTP/1.1
+     request that was Upgraded to HTTP/2. Fixes PR65881.
+     [Stefan Eissing]
\ No newline at end of file
index 9ff6feb675f4eb0ae33c1eb521854af05fdbb9d3..16c279c3771f9aeafc1ca20ed7993353f38f78a5 100644 (file)
@@ -69,12 +69,25 @@ apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool,
         return APR_EINVAL;
     }
 
-    if (!ap_strchr_c(authority, ':') && r->server && r->server->port) {
-        apr_port_t defport = apr_uri_port_of_scheme(scheme);
-        if (defport != r->server->port) {
-            /* port info missing and port is not default for scheme: append */
-            authority = apr_psprintf(pool, "%s:%d", authority,
-                                     (int)r->server->port);
+    /* The authority we carry in h2_request is the 'authority' part of
+     * the URL for the request. r->hostname has stripped any port info that
+     * might have been present. Do we need to add it?
+     */
+    if (!ap_strchr_c(authority, ':')) {
+        if (r->parsed_uri.port_str) {
+            /* Yes, it was there, add it again. */
+            authority = apr_pstrcat(pool, authority, ":", r->parsed_uri.port_str, NULL);
+        }
+        else if (!r->parsed_uri.hostname && r->server && r->server->port) {
+            /* If there was no hostname in the parsed URL, the URL was relative.
+             * In that case, we restore port from our server->port, if it
+             * is known and not the default port for the scheme. */
+            apr_port_t defport = apr_uri_port_of_scheme(scheme);
+            if (defport != r->server->port) {
+                /* port info missing and port is not default for scheme: append */
+                authority = apr_psprintf(pool, "%s:%d", authority,
+                                         (int)r->server->port);
+            }
         }
     }
     
index f70852dd2aefe681a6e4b9449b993aa22610a832..a839a040e6903aa7671e6400459a812fa08f3781 100644 (file)
@@ -86,6 +86,7 @@ class H2TestEnv(HttpdTestEnv):
             'AH00135',
             'AH02261',  # Re-negotiation handshake failed (our test_101)
             'AH03490',  # scoreboard full, happens on limit tests
+            'AH01247',  # cgi reports sometimes error on accept on cgid socket
         ])
         self.httpd_error_log.add_ignored_patterns([
             re.compile(r'.*malformed header from script \'hecho.py\': Bad header: x.*'),
index 191acb2fedde7b3f4a9a85a46b55a81d2c8d20e6..9fb2eb689d8f75c4a358035035ec731120a562d1 100644 (file)
@@ -7,6 +7,7 @@ print()
 print("{")
 print("  \"https\" : \"%s\"," % (os.getenv('HTTPS', '')))
 print("  \"host\" : \"%s\"," % (os.getenv('SERVER_NAME', '')))
+print("  \"port\" : \"%s\"," % (os.getenv('SERVER_PORT', '')))
 print("  \"protocol\" : \"%s\"," % (os.getenv('SERVER_PROTOCOL', '')))
 print("  \"ssl_protocol\" : \"%s\"," % (os.getenv('SSL_PROTOCOL', '')))
 print("  \"h2\" : \"%s\"," % (os.getenv('HTTP2', '')))
diff --git a/test/modules/http2/test_502_proxy_port.py b/test/modules/http2/test_502_proxy_port.py
new file mode 100644 (file)
index 0000000..472f625
--- /dev/null
@@ -0,0 +1,40 @@
+import pytest
+
+from .env import H2Conf
+
+
+class TestProxyPort:
+
+    @pytest.fixture(autouse=True, scope='class')
+    def _class_scope(self, env):
+        conf = H2Conf(env, extras={
+            'base': [
+                f'Listen {env.proxy_port}',
+                'Protocols h2c http/1.1',
+                'LogLevel proxy_http2:trace2 proxy:trace2',
+            ],
+            f'cgi.{env.http_tld}': [
+                "Header unset Server",
+                "Header always set Server cgi",
+            ]
+        })
+        conf.add_vhost_cgi(proxy_self=False, h2proxy_self=False)
+        conf.start_vhost(domains=[f"test1.{env.http_tld}"], port=env.proxy_port)
+        conf.add([
+            'Protocols h2c',
+            'RewriteEngine On',
+            'RewriteRule "^/(.*)$" "h2c://%{HTTP_HOST}/$1"[NC,P]',
+            'ProxyPassMatch / "h2c://$1/"',
+        ])
+        conf.end_vhost()
+        conf.install()
+        assert env.apache_restart() == 0
+
+    # Test PR 65881
+    # h2c upgraded request via a dynamic proxy onto another port
+    def test_h2_502_01(self, env):
+        url = f'http://localhost:{env.http_port}/hello.py'
+        r = env.curl_get(url, 5, options=['--http2',
+                                          '--proxy', f'localhost:{env.proxy_port}'])
+        assert r.response['status'] == 200
+        assert r.json['port'] == f'{env.http_port}'