From: Stefan Eissing Date: Thu, 17 Feb 2022 10:06:31 +0000 (+0000) Subject: *) mod_http2: preserve the port number given in a HTTP/1.1 X-Git-Tag: 2.5.0-alpha2-ci-test-only~491 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6137cd7b74344f78b47d454749877c3e4839f0fc;p=thirdparty%2Fapache%2Fhttpd.git *) 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/trunk@1898146 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/changes-entries/pr65881.txt b/changes-entries/pr65881.txt new file mode 100644 index 00000000000..816158a2f36 --- /dev/null +++ b/changes-entries/pr65881.txt @@ -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 diff --git a/modules/http2/h2_request.c b/modules/http2/h2_request.c index e759c2e4558..7899f282d46 100644 --- a/modules/http2/h2_request.c +++ b/modules/http2/h2_request.c @@ -90,12 +90,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_psprintf(pool, "%s:%s", authority, r->parsed_uri.port_str); + } + 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); + } } } diff --git a/test/modules/http2/htdocs/cgi/hello.py b/test/modules/http2/htdocs/cgi/hello.py index 191acb2fedd..9fb2eb689d8 100644 --- a/test/modules/http2/htdocs/cgi/hello.py +++ b/test/modules/http2/htdocs/cgi/hello.py @@ -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 index 00000000000..472f625ad5f --- /dev/null +++ b/test/modules/http2/test_502_proxy_port.py @@ -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}'