Fixed #25211 -- Added HttpRequest.get_port() and USE_X_FORWARDED_PORT setting.

This commit is contained in:
Matt Robenolt 2015-08-02 16:56:54 -07:00 committed by Tim Graham
parent f6259ce776
commit 4dcfbd7923
6 changed files with 67 additions and 1 deletions

View File

@ -428,6 +428,7 @@ DEFAULT_INDEX_TABLESPACE = ''
X_FRAME_OPTIONS = 'SAMEORIGIN' X_FRAME_OPTIONS = 'SAMEORIGIN'
USE_X_FORWARDED_HOST = False USE_X_FORWARDED_HOST = False
USE_X_FORWARDED_PORT = False
# The Python dotted path to the WSGI application that Django's internal server # The Python dotted path to the WSGI application that Django's internal server
# (runserver) will use. If `None`, the return value of # (runserver) will use. If `None`, the return value of

View File

@ -79,7 +79,7 @@ class HttpRequest(object):
else: else:
# Reconstruct the host using the algorithm from PEP 333. # Reconstruct the host using the algorithm from PEP 333.
host = self.META['SERVER_NAME'] host = self.META['SERVER_NAME']
server_port = str(self.META['SERVER_PORT']) server_port = self.get_port()
if server_port != ('443' if self.is_secure() else '80'): if server_port != ('443' if self.is_secure() else '80'):
host = '%s:%s' % (host, server_port) host = '%s:%s' % (host, server_port)
@ -98,6 +98,14 @@ class HttpRequest(object):
msg += " The domain name provided is not valid according to RFC 1034/1035." msg += " The domain name provided is not valid according to RFC 1034/1035."
raise DisallowedHost(msg) raise DisallowedHost(msg)
def get_port(self):
"""Return the port number for the request as a string."""
if settings.USE_X_FORWARDED_PORT and 'HTTP_X_FORWARDED_PORT' in self.META:
port = self.META['HTTP_X_FORWARDED_PORT']
else:
port = self.META['SERVER_PORT']
return str(port)
def get_full_path(self, force_append_slash=False): def get_full_path(self, force_append_slash=False):
# RFC 3986 requires query string arguments to be in the ASCII range. # RFC 3986 requires query string arguments to be in the ASCII range.
# Rather than crash if this doesn't happen, we encode defensively. # Rather than crash if this doesn't happen, we encode defensively.

View File

@ -254,6 +254,14 @@ Methods
:class:`~django.middleware.common.CommonMiddleware` or :class:`~django.middleware.common.CommonMiddleware` or
:class:`~django.middleware.csrf.CsrfViewMiddleware`. :class:`~django.middleware.csrf.CsrfViewMiddleware`.
.. method:: HttpRequest.get_port()
.. versionadded:: 1.9
Returns the originating port of the request using information from the
``HTTP_X_FORWARDED_PORT`` (if :setting:`USE_X_FORWARDED_PORT` is enabled)
and ``SERVER_PORT`` ``META`` variables, in that order.
.. method:: HttpRequest.get_full_path() .. method:: HttpRequest.get_full_path()
Returns the ``path``, plus an appended query string, if applicable. Returns the ``path``, plus an appended query string, if applicable.

View File

@ -2621,6 +2621,19 @@ A boolean that specifies whether to use the X-Forwarded-Host header in
preference to the Host header. This should only be enabled if a proxy preference to the Host header. This should only be enabled if a proxy
which sets this header is in use. which sets this header is in use.
.. setting:: USE_X_FORWARDED_PORT
USE_X_FORWARDED_PORT
--------------------
.. versionadded:: 1.9
Default: ``False``
A boolean that specifies whether to use the X-Forwarded-Port header in
preference to the ``SERVER_PORT`` ``META`` variable. This should only be
enabled if a proxy which sets this header is in use.
.. setting:: WSGI_APPLICATION .. setting:: WSGI_APPLICATION
WSGI_APPLICATION WSGI_APPLICATION
@ -3329,6 +3342,7 @@ HTTP
* :setting:`SIGNING_BACKEND` * :setting:`SIGNING_BACKEND`
* :setting:`USE_ETAGS` * :setting:`USE_ETAGS`
* :setting:`USE_X_FORWARDED_HOST` * :setting:`USE_X_FORWARDED_HOST`
* :setting:`USE_X_FORWARDED_PORT`
* :setting:`WSGI_APPLICATION` * :setting:`WSGI_APPLICATION`
Logging Logging

View File

@ -538,6 +538,9 @@ Requests and Responses
returning an :class:`~django.http.HttpResponseForbidden` so that returning an :class:`~django.http.HttpResponseForbidden` so that
:data:`~django.conf.urls.handler403` is invoked. :data:`~django.conf.urls.handler403` is invoked.
* Added :meth:`HttpRequest.get_port() <django.http.HttpRequest.get_port>` to
fetch the originating port of the request.
Tests Tests
^^^^^ ^^^^^

View File

@ -651,6 +651,38 @@ class HostValidationTests(SimpleTestCase):
} }
request.get_host() request.get_host()
@override_settings(USE_X_FORWARDED_PORT=False)
def test_get_port(self):
request = HttpRequest()
request.META = {
'SERVER_PORT': '8080',
'HTTP_X_FORWARDED_PORT': '80',
}
# Shouldn't use the X-Forwarded-Port header
self.assertEqual(request.get_port(), '8080')
request = HttpRequest()
request.META = {
'SERVER_PORT': '8080',
}
self.assertEqual(request.get_port(), '8080')
@override_settings(USE_X_FORWARDED_PORT=True)
def test_get_port_with_x_forwarded_port(self):
request = HttpRequest()
request.META = {
'SERVER_PORT': '8080',
'HTTP_X_FORWARDED_PORT': '80',
}
# Should use the X-Forwarded-Port header
self.assertEqual(request.get_port(), '80')
request = HttpRequest()
request.META = {
'SERVER_PORT': '8080',
}
self.assertEqual(request.get_port(), '8080')
@override_settings(DEBUG=True, ALLOWED_HOSTS=[]) @override_settings(DEBUG=True, ALLOWED_HOSTS=[])
def test_host_validation_disabled_in_debug_mode(self): def test_host_validation_disabled_in_debug_mode(self):
"""If ALLOWED_HOSTS is empty and DEBUG is True, all hosts pass.""" """If ALLOWED_HOSTS is empty and DEBUG is True, all hosts pass."""