dont' override SERVER_* by the Host header

Gunicorn shouln't override the SERVER_* by the Host header. The client
will take care about it during the URL reconstruction if needed.

Since the spec don't support unix sockets, Gunicorn is using the HOST
heeader when available to create viable SERVER_* if possible so the
application and framworks will be happy. When the Host Header is not
available (SocketPath, '') is returned.

fix #628
This commit is contained in:
benoitc 2013-12-27 17:15:46 +01:00
parent c487368129
commit 5fb4b49a91

View File

@ -78,17 +78,22 @@ def proxy_environ(req):
def create(req, sock, client, server, cfg): def create(req, sock, client, server, cfg):
resp = Response(req, sock, cfg) resp = Response(req, sock, cfg)
# set initial environ
environ = default_environ(req, sock, cfg) environ = default_environ(req, sock, cfg)
# default variables
host = None
url_scheme = "https" if cfg.is_ssl else "http" url_scheme = "https" if cfg.is_ssl else "http"
script_name = os.environ.get("SCRIPT_NAME", "") script_name = os.environ.get("SCRIPT_NAME", "")
# set secure_headers
secure_headers = cfg.secure_scheme_headers secure_headers = cfg.secure_scheme_headers
if client and not isinstance(client, string_types): if client and not isinstance(client, string_types):
if ('*' not in cfg.forwarded_allow_ips if ('*' not in cfg.forwarded_allow_ips
and client[0] not in cfg.forwarded_allow_ips): and client[0] not in cfg.forwarded_allow_ips):
secure_headers = {} secure_headers = {}
# add the headers tot the environ
for hdr_name, hdr_value in req.headers: for hdr_name, hdr_value in req.headers:
if hdr_name == "EXPECT": if hdr_name == "EXPECT":
# handle expect # handle expect
@ -97,8 +102,8 @@ def create(req, sock, client, server, cfg):
elif secure_headers and (hdr_name in secure_headers and elif secure_headers and (hdr_name in secure_headers and
hdr_value == secure_headers[hdr_name]): hdr_value == secure_headers[hdr_name]):
url_scheme = "https" url_scheme = "https"
elif hdr_name == "HOST": elif hdr_name == 'HOST':
server = hdr_value host = hdr_value
elif hdr_name == "SCRIPT_NAME": elif hdr_name == "SCRIPT_NAME":
script_name = hdr_value script_name = hdr_value
elif hdr_name == "CONTENT-TYPE": elif hdr_name == "CONTENT-TYPE":
@ -113,8 +118,10 @@ def create(req, sock, client, server, cfg):
hdr_value = "%s,%s" % (environ[key], hdr_value) hdr_value = "%s,%s" % (environ[key], hdr_value)
environ[key] = hdr_value environ[key] = hdr_value
# set the url schejeme
environ['wsgi.url_scheme'] = url_scheme environ['wsgi.url_scheme'] = url_scheme
# set the REMOTE_* keys in environ
# authors should be aware that REMOTE_HOST and REMOTE_ADDR # authors should be aware that REMOTE_HOST and REMOTE_ADDR
# may not qualify the remote addr: # may not qualify the remote addr:
# http://www.ietf.org/rfc/rfc3875 # http://www.ietf.org/rfc/rfc3875
@ -124,26 +131,42 @@ def create(req, sock, client, server, cfg):
environ['REMOTE_ADDR'] = client[0] environ['REMOTE_ADDR'] = client[0]
environ['REMOTE_PORT'] = str(client[1]) environ['REMOTE_PORT'] = str(client[1])
# handle the SERVER_*
# Normally only the application should use the Host header but since the
# WSGI spec doesn't support unix sockets, we are using it to create
# viable SERVER_* if possible.
if isinstance(server, string_types): if isinstance(server, string_types):
server = server.split(":") server = server.split(":")
if len(server) == 1: if len(server) == 1:
if url_scheme == "http": # unix socket
server.append("80") if host and host is not None:
elif url_scheme == "https": server = host.split(':')
server.append("443") if len(server) == 1:
if url_scheme == "http":
server.append(80),
elif url_scheme == "https":
server.append(443)
else:
server.append('')
else: else:
# no host header given which means that we are not behind a
# proxy, so append an empty port.
server.append('') server.append('')
environ['SERVER_NAME'] = server[0] environ['SERVER_NAME'] = server[0]
environ['SERVER_PORT'] = str(server[1]) environ['SERVER_PORT'] = str(server[1])
# set the path and script name
path_info = req.path path_info = req.path
if script_name: if script_name:
path_info = path_info.split(script_name, 1)[1] path_info = path_info.split(script_name, 1)[1]
environ['PATH_INFO'] = unquote_to_wsgi_str(path_info) environ['PATH_INFO'] = unquote_to_wsgi_str(path_info)
environ['SCRIPT_NAME'] = script_name environ['SCRIPT_NAME'] = script_name
# override the environ with the correct remote and server address if
# we are behind a proxy using the proxy protocol.
environ.update(proxy_environ(req)) environ.update(proxy_environ(req))
print(environ)
return resp, environ return resp, environ