Skip to content

Commit

Permalink
*) mod_proxy_http2: use only the ':authority' header to forward 'Host'
Browse files Browse the repository at this point in the history
     information to a backend. Deduce ':authority' from what the client
     sent when 'ProxyPreserveHost' is on.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1904164 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
icing committed Sep 20, 2022
1 parent db3bb19 commit 4ef081e
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 14 deletions.
4 changes: 4 additions & 0 deletions changes-entries/h2_proxy_host.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*) mod_proxy_http2: use only the ':authority' header to forward 'Host'
information to a backend. Deduce ':authority' from what the client
sent when 'ProxyPreserveHost' is on.
[Stefan Eissing]
11 changes: 9 additions & 2 deletions modules/http2/h2_proxy_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,10 @@ static apr_status_t open_stream(h2_proxy_session *session, const char *url,

dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
if (dconf->preserve_host) {
authority = r->hostname;
authority = apr_table_get(r->headers_in, "Host");
if (authority == NULL) {
authority = r->hostname;
}
}
else {
authority = puri.hostname;
Expand All @@ -847,6 +850,9 @@ static apr_status_t open_stream(h2_proxy_session *session, const char *url,
/* port info missing and port is not default for scheme: append */
authority = apr_psprintf(stream->pool, "%s:%d", authority, puri.port);
}
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
"authority=%s from uri.hostname=%s and uri.port=%d",
authority, puri.hostname, puri.port);
}

/* we need this for mapping relative uris in headers ("Link") back
Expand Down Expand Up @@ -884,7 +890,8 @@ static apr_status_t open_stream(h2_proxy_session *session, const char *url,
r->server->server_hostname);
}
}

apr_table_unset(r->headers_in, "Host");

/* Tuck away all already existing cookies */
stream->saves = apr_table_make(r->pool, 2);
apr_table_do(add_header, stream->saves, r->headers_out, "Set-Cookie", NULL);
Expand Down
1 change: 1 addition & 0 deletions test/modules/http2/htdocs/cgi/hello.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
print()
print("{")
print(" \"https\" : \"%s\"," % (os.getenv('HTTPS', '')))
print(" \"x_host\" : \"%s\"," % (os.getenv('X_HOST', '')))
print(" \"host\" : \"%s\"," % (os.getenv('SERVER_NAME', '')))
print(" \"port\" : \"%s\"," % (os.getenv('SERVER_PORT', '')))
print(" \"protocol\" : \"%s\"," % (os.getenv('SERVER_PROTOCOL', '')))
Expand Down
9 changes: 6 additions & 3 deletions test/modules/http2/htdocs/cgi/mnot164.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
except KeyError:
text="a"
count=77784



count = int(count)

print("Status: 200 OK")
print("Content-Type: text/html")
print()
sys.stdout.write(text*int(count))
sys.stdout.flush()
for _ in range(count):
sys.stdout.write(text)

2 changes: 1 addition & 1 deletion test/modules/http2/test_100_conn_reuse.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_h2_100_01(self, env):
def test_h2_100_02(self, env):
url = env.mkurl("https", "cgi", "/hello.py")
hostname = ("cgi-alias.%s" % env.http_tld)
r = env.curl_get(url, 5, options=[ "-H", "Host:%s" % hostname ])
r = env.curl_get(url, 5, options=["-H", f"Host: {hostname}"])
assert r.response["status"] == 200
assert "HTTP/2" == r.response["protocol"]
assert hostname == r.response["json"]["host"]
Expand Down
53 changes: 45 additions & 8 deletions test/modules/http2/test_600_h2proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
@pytest.mark.skipif(condition=H2TestEnv.is_unsupported, reason="mod_http2 not supported here")
class TestH2Proxy:

@pytest.fixture(autouse=True, scope='class')
def _class_scope(self, env):
conf = H2Conf(env)
def test_h2_600_01(self, env):
conf = H2Conf(env, extras={
f'cgi.{env.http_tld}': [
"SetEnvIf Host (.+) X_HOST=$1",
]
})
conf.add_vhost_cgi(h2proxy_self=True)
if env.verbosity > 1:
conf.add("LogLevel proxy:trace2 proxy_http2:trace2")
conf.install()
assert env.apache_restart() == 0

def test_h2_600_01(self, env):
url = env.mkurl("https", "cgi", "/h2proxy/hello.py")
r = env.curl_get(url, 5)
assert r.response["status"] == 200
Expand All @@ -24,4 +23,42 @@ def test_h2_600_01(self, env):
assert r.response["json"]["ssl_protocol"] != ""
assert r.response["json"]["h2"] == "on"
assert r.response["json"]["h2push"] == "off"
assert r.response["json"]["host"] == f"cgi.{env.http_tld}"
assert r.response["json"]["x_host"] == f"cgi.{env.http_tld}:{env.https_port}"

def test_h2_600_02(self, env):
conf = H2Conf(env, extras={
f'cgi.{env.http_tld}': [
"SetEnvIf Host (.+) X_HOST=$1",
f"ProxyPreserveHost on",
f"ProxyPass /h2c/ h2c://127.0.0.1:{env.http_port}/",
]
})
conf.add_vhost_cgi()
conf.install()
assert env.apache_restart() == 0
url = env.mkurl("https", "cgi", "/h2c/hello.py")
r = env.curl_get(url, 5)
assert r.response["status"] == 200
assert r.response["json"]["protocol"] == "HTTP/2.0"
assert r.response["json"]["https"] == ""
# the proxied backend sees Host header as passed on front
assert r.response["json"]["x_host"] == f"cgi.{env.http_tld}:{env.https_port}"

def test_h2_600_03(self, env):
conf = H2Conf(env, extras={
f'cgi.{env.http_tld}': [
"SetEnvIf Host (.+) X_HOST=$1",
f"ProxyPreserveHost off",
f"ProxyPass /h2c/ h2c://127.0.0.1:{env.http_port}/",
]
})
conf.add_vhost_cgi()
conf.install()
assert env.apache_restart() == 0
url = env.mkurl("https", "cgi", "/h2c/hello.py")
r = env.curl_get(url, 5)
assert r.response["status"] == 200
assert r.response["json"]["protocol"] == "HTTP/2.0"
assert r.response["json"]["https"] == ""
# the proxied backend sees Host as using in connecting to it
assert r.response["json"]["x_host"] == f"127.0.0.1:{env.http_port}"

0 comments on commit 4ef081e

Please sign in to comment.