Only construct HTTP_HOST header from SERVER_NAME and _PORT if it's unset
If HTTP_HOST is set, following PEP333 it takes precedence over SERVER_NAME and _PORT, so we set those from it.
This commit is contained in:
parent
a7bd770180
commit
53b74f9caa
2 changed files with 177 additions and 83 deletions
|
|
@ -321,6 +321,14 @@ class ReverseProxiedEnvironment(object):
|
|||
environ["HTTP_HOST"] = host
|
||||
environ["SERVER_NAME"] = server
|
||||
environ["SERVER_PORT"] = port
|
||||
|
||||
elif environ.get("HTTP_HOST", None) is not None:
|
||||
# if we have a Host header, we use that and make sure our server name and port properties match it
|
||||
host = environ["HTTP_HOST"]
|
||||
server, port = host_to_server_and_port(host, url_scheme)
|
||||
environ["SERVER_NAME"] = server
|
||||
environ["SERVER_PORT"] = port
|
||||
|
||||
else:
|
||||
# else we take a look at the server and port headers and if we have
|
||||
# something there we derive the host from it
|
||||
|
|
@ -335,15 +343,12 @@ class ReverseProxiedEnvironment(object):
|
|||
if port is not None:
|
||||
environ["SERVER_PORT"] = port
|
||||
|
||||
# make sure HTTP_HOST matches SERVER_NAME and SERVER_PORT
|
||||
expected_server, expected_port = host_to_server_and_port(environ.get("HTTP_HOST", None), url_scheme)
|
||||
if expected_server != environ["SERVER_NAME"] or expected_port != environ["SERVER_PORT"]:
|
||||
# there's a difference, fix it!
|
||||
if url_scheme == "http" and environ["SERVER_PORT"] == "80" or url_scheme == "https" and environ["SERVER_PORT"] == "443":
|
||||
# default port for scheme, can be skipped
|
||||
environ["HTTP_HOST"] = environ["SERVER_NAME"]
|
||||
else:
|
||||
environ["HTTP_HOST"] = environ["SERVER_NAME"] + ":" + environ["SERVER_PORT"]
|
||||
# reconstruct host header
|
||||
if url_scheme == "http" and environ["SERVER_PORT"] == "80" or url_scheme == "https" and environ["SERVER_PORT"] == "443":
|
||||
# default port for scheme, can be skipped
|
||||
environ["HTTP_HOST"] = environ["SERVER_NAME"]
|
||||
else:
|
||||
environ["HTTP_HOST"] = environ["SERVER_NAME"] + ":" + environ["SERVER_PORT"]
|
||||
|
||||
# call wrapped app with rewritten environment
|
||||
return environ
|
||||
|
|
|
|||
|
|
@ -103,27 +103,29 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
"wsgi.url_scheme": "https"
|
||||
}),
|
||||
|
||||
# server and port headers set -> host derived with port
|
||||
# host set, server and port differ -> updated, standard port
|
||||
({
|
||||
"HTTP_X_FORWARDED_SERVER": "example2.com",
|
||||
"HTTP_X_FORWARDED_PORT": "444",
|
||||
"HTTP_X_FORWARDED_PROTO": "https"
|
||||
}, {
|
||||
"HTTP_HOST": "example2.com:444",
|
||||
"SERVER_NAME": "example2.com",
|
||||
"SERVER_PORT": "444",
|
||||
"wsgi.url_scheme": "https"
|
||||
}),
|
||||
"HTTP_HOST": "example.com",
|
||||
"wsgi.url_scheme": "https",
|
||||
"SERVER_NAME": "localhost",
|
||||
"SERVER_PORT": "80"
|
||||
}, {
|
||||
"HTTP_HOST": "example.com",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "443",
|
||||
}),
|
||||
|
||||
# server and port headers set, standard port -> host derived, no port
|
||||
# host set, server and port differ -> updated, non standard port
|
||||
({
|
||||
"HTTP_X_FORWARDED_SERVER": "example.com",
|
||||
"HTTP_X_FORWARDED_PORT": "80",
|
||||
}, {
|
||||
"HTTP_HOST": "example.com",
|
||||
"HTTP_HOST": "example.com:444",
|
||||
"wsgi.url_scheme": "https",
|
||||
"SERVER_NAME": "localhost",
|
||||
"SERVER_PORT": "80"
|
||||
}, {
|
||||
"HTTP_HOST": "example.com:444",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "80",
|
||||
}),
|
||||
"SERVER_PORT": "444",
|
||||
}),
|
||||
|
||||
# multiple scheme entries -> only use first one
|
||||
({
|
||||
|
|
@ -132,7 +134,7 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
"wsgi.url_scheme": "https"
|
||||
}),
|
||||
|
||||
# host = none -> should never happen but you never know...
|
||||
# host = none (should never happen but you never know) -> server & port used for reconstruction
|
||||
({
|
||||
"HTTP_HOST": None,
|
||||
"HTTP_X_FORWARDED_SERVER": "example.com",
|
||||
|
|
@ -158,6 +160,67 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
|
||||
self.assertDictEqual(merged_expected, actual)
|
||||
|
||||
@data(
|
||||
# server and port headers set -> host derived with port
|
||||
({
|
||||
"SERVER_NAME": "example2.com",
|
||||
"SERVER_PORT": "444",
|
||||
"HTTP_X_FORWARDED_PROTO": "https"
|
||||
}, {
|
||||
"HTTP_HOST": "example2.com:444",
|
||||
"SERVER_NAME": "example2.com",
|
||||
"SERVER_PORT": "444",
|
||||
"wsgi.url_scheme": "https"
|
||||
}),
|
||||
|
||||
# server and port headers set, standard port -> host derived, no port
|
||||
({
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "80",
|
||||
}, {
|
||||
"HTTP_HOST": "example.com",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "80",
|
||||
}),
|
||||
|
||||
# server and port forwarded headers set -> host derived with port
|
||||
({
|
||||
"HTTP_X_FORWARDED_SERVER": "example2.com",
|
||||
"HTTP_X_FORWARDED_PORT": "444",
|
||||
"HTTP_X_FORWARDED_PROTO": "https"
|
||||
}, {
|
||||
"HTTP_HOST": "example2.com:444",
|
||||
"SERVER_NAME": "example2.com",
|
||||
"SERVER_PORT": "444",
|
||||
"wsgi.url_scheme": "https"
|
||||
}),
|
||||
|
||||
# server and port forwarded headers set, standard port -> host derived, no port
|
||||
({
|
||||
"HTTP_X_FORWARDED_SERVER": "example.com",
|
||||
"HTTP_X_FORWARDED_PORT": "80",
|
||||
}, {
|
||||
"HTTP_HOST": "example.com",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "80",
|
||||
}),
|
||||
)
|
||||
@unpack
|
||||
def test_nohost(self, environ, expected):
|
||||
reverse_proxied = ReverseProxiedEnvironment()
|
||||
|
||||
merged_environ = dict(standard_environ)
|
||||
merged_environ.update(environ)
|
||||
del merged_environ["HTTP_HOST"]
|
||||
|
||||
actual = reverse_proxied(merged_environ)
|
||||
|
||||
merged_expected = dict(standard_environ)
|
||||
merged_expected.update(environ)
|
||||
merged_expected.update(expected)
|
||||
|
||||
self.assertDictEqual(merged_expected, actual)
|
||||
|
||||
@data(
|
||||
# prefix overridden
|
||||
({
|
||||
|
|
@ -195,57 +258,7 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
"SERVER_PORT": "81"
|
||||
}),
|
||||
|
||||
# server overridden
|
||||
({
|
||||
"server": "example.com"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "example.com:5000",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "5000"
|
||||
}),
|
||||
|
||||
# port overridden, standard port
|
||||
({
|
||||
"port": "80"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "localhost",
|
||||
"SERVER_PORT": "80"
|
||||
}),
|
||||
|
||||
# port overridden, non standard port
|
||||
({
|
||||
"port": "81"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "localhost:81",
|
||||
"SERVER_PORT": "81"
|
||||
}),
|
||||
|
||||
# server and port overridden, default port
|
||||
({
|
||||
"server": "example.com",
|
||||
"port": "80"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "example.com",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "80"
|
||||
}),
|
||||
|
||||
# server and port overridden, non default port
|
||||
({
|
||||
"server": "example.com",
|
||||
"port": "81"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "example.com:81",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "81"
|
||||
}),
|
||||
|
||||
# prefix not really overridden
|
||||
# prefix not really overridden, forwarded headers take precedence
|
||||
({
|
||||
"prefix": "/octoprint"
|
||||
}, {
|
||||
|
|
@ -253,7 +266,7 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
}, {
|
||||
}),
|
||||
|
||||
# scheme not really overridden
|
||||
# scheme not really overridden, forwarded headers take precedence
|
||||
({
|
||||
"scheme": "https"
|
||||
}, {
|
||||
|
|
@ -261,7 +274,7 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
}, {
|
||||
}),
|
||||
|
||||
# scheme 2 not really overridden
|
||||
# scheme 2 not really overridden, forwarded headers take precedence
|
||||
({
|
||||
"scheme": "https"
|
||||
}, {
|
||||
|
|
@ -269,7 +282,7 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
}, {
|
||||
}),
|
||||
|
||||
# host not really overridden
|
||||
# host not really overridden, forwarded headers take precedence
|
||||
({
|
||||
"host": "example.com:444"
|
||||
}, {
|
||||
|
|
@ -277,7 +290,7 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
}, {
|
||||
}),
|
||||
|
||||
# server not really overridden
|
||||
# server not really overridden, forwarded headers take precedence
|
||||
({
|
||||
"server": "example.com"
|
||||
}, {
|
||||
|
|
@ -285,12 +298,20 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
}, {
|
||||
}),
|
||||
|
||||
# port not really overridden
|
||||
# port not really overridden, forwarded headers take precedence
|
||||
({
|
||||
"port": "444"
|
||||
}, {
|
||||
"HTTP_X_FORWARDED_PORT": "5000"
|
||||
}, {
|
||||
}),
|
||||
|
||||
# server and port not really overridden, Host header wins
|
||||
({
|
||||
"server": "example.com",
|
||||
"port": "80"
|
||||
}, {
|
||||
}, {
|
||||
})
|
||||
)
|
||||
@unpack
|
||||
|
|
@ -308,6 +329,74 @@ class ReverseProxiedEnvironmentTest(unittest.TestCase):
|
|||
|
||||
self.assertDictEqual(merged_expected, actual)
|
||||
|
||||
@data(
|
||||
# server overridden
|
||||
({
|
||||
"server": "example.com"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "example.com:5000",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "5000"
|
||||
}),
|
||||
|
||||
# port overridden, standard port
|
||||
({
|
||||
"port": "80"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "localhost",
|
||||
"SERVER_PORT": "80"
|
||||
}),
|
||||
|
||||
# port overridden, non standard port
|
||||
({
|
||||
"port": "81"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "localhost:81",
|
||||
"SERVER_PORT": "81"
|
||||
}),
|
||||
|
||||
# server and port overridden, default port
|
||||
({
|
||||
"server": "example.com",
|
||||
"port": "80"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "example.com",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "80"
|
||||
}),
|
||||
|
||||
# server and port overridden, non default port
|
||||
({
|
||||
"server": "example.com",
|
||||
"port": "81"
|
||||
}, {
|
||||
}, {
|
||||
"HTTP_HOST": "example.com:81",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": "81"
|
||||
}),
|
||||
|
||||
)
|
||||
@unpack
|
||||
def test_fallbacks_nohost(self, fallbacks, environ, expected):
|
||||
reverse_proxied = ReverseProxiedEnvironment(**fallbacks)
|
||||
|
||||
merged_environ = dict(standard_environ)
|
||||
merged_environ.update(environ)
|
||||
del merged_environ["HTTP_HOST"]
|
||||
|
||||
actual = reverse_proxied(merged_environ)
|
||||
|
||||
merged_expected = dict(standard_environ)
|
||||
merged_expected.update(environ)
|
||||
merged_expected.update(expected)
|
||||
|
||||
self.assertDictEqual(merged_expected, actual)
|
||||
|
||||
def test_header_config_ok(self):
|
||||
result = ReverseProxiedEnvironment.to_header_candidates(["prefix-header1", "prefix-header2"])
|
||||
self.assertEquals(result, ["HTTP_PREFIX_HEADER1", "HTTP_PREFIX_HEADER2"])
|
||||
|
|
|
|||
Loading…
Reference in a new issue