浏览代码

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

Matt Robenolt 9 年之前
父节点
当前提交
4dcfbd7923
共有 6 个文件被更改,包括 67 次插入1 次删除
  1. 1 0
      django/conf/global_settings.py
  2. 9 1
      django/http/request.py
  3. 8 0
      docs/ref/request-response.txt
  4. 14 0
      docs/ref/settings.txt
  5. 3 0
      docs/releases/1.9.txt
  6. 32 0
      tests/requests/tests.py

+ 1 - 0
django/conf/global_settings.py

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

+ 9 - 1
django/http/request.py

@@ -79,7 +79,7 @@ class HttpRequest(object):
         else:
             # Reconstruct the host using the algorithm from PEP 333.
             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'):
                 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."
             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):
         # RFC 3986 requires query string arguments to be in the ASCII range.
         # Rather than crash if this doesn't happen, we encode defensively.

+ 8 - 0
docs/ref/request-response.txt

@@ -254,6 +254,14 @@ Methods
         :class:`~django.middleware.common.CommonMiddleware` or
         :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()
 
     Returns the ``path``, plus an appended query string, if applicable.

+ 14 - 0
docs/ref/settings.txt

@@ -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
 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
 
 WSGI_APPLICATION
@@ -3329,6 +3342,7 @@ HTTP
 * :setting:`SIGNING_BACKEND`
 * :setting:`USE_ETAGS`
 * :setting:`USE_X_FORWARDED_HOST`
+* :setting:`USE_X_FORWARDED_PORT`
 * :setting:`WSGI_APPLICATION`
 
 Logging

+ 3 - 0
docs/releases/1.9.txt

@@ -538,6 +538,9 @@ Requests and Responses
   returning an :class:`~django.http.HttpResponseForbidden` so that
   :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
 ^^^^^
 

+ 32 - 0
tests/requests/tests.py

@@ -651,6 +651,38 @@ class HostValidationTests(SimpleTestCase):
                 }
                 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=[])
     def test_host_validation_disabled_in_debug_mode(self):
         """If ALLOWED_HOSTS is empty and DEBUG is True, all hosts pass."""