Repository: thomasw/djproxy

Issue:
HTTP headers containing "http" are mangled
https://github.com/thomasw/djproxy/blob/ce40f540c2a9537a3e389ed1604cf8b80adbf746/djproxy/headers.py#L13

I was trying to proxy third-party software that sent a header named "x-http-method", and it got changed to "method".

Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 98353ce..491f002 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,19 +13,22 @@ env:
     - DJANGO_VERSION=2.1
     - DJANGO_VERSION=2.2
     - DJANGO_VERSION=3.0
+    - DJANGO_VERSION=3.1
     - DJANGO_VERSION='latest'
 matrix:
     include:
         # 1.11 is the last Django version to support python 2.7
         - python: 2.7
           env: DJANGO_VERSION=1.11
-        - python: pypy
-          env: DJANGO_VERSION=1.11
     exclude:
         - python: 3.5
           env: DJANGO_VERSION=3.0
+        - python: 3.5
+          env: DJANGO_VERSION=3.1
         - python: pypy3.5
           env: DJANGO_VERSION=3.0
+        - python: pypy3.5
+          env: DJANGO_VERSION=3.1
 install:
     - pip install -r requirements.txt
     - if [[ $DJANGO_VERSION != 'latest' ]]; then pip install django==$DJANGO_VERSION; fi
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1a94fb2..3130072 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,13 @@
 Changelog
 =========
 
+2.3.6
+-----
+
+- Declares Django 3.1 as supported versions
+- Fixes a bug that changed the names of HTTP headers that contained http. For
+  example, X-Http-Method was incorrectly changed to Method.
+
 2.3.5
 -----
 
diff --git a/djproxy/__init__.py b/djproxy/__init__.py
index 25b226b..ea9066a 100644
--- a/djproxy/__init__.py
+++ b/djproxy/__init__.py
@@ -15,4 +15,4 @@ Performance can be further increased by aggressively caching upstream
 responses.
 """
 __author__ = "Thomas Welfley"
-__version__ = "2.3.5"
+__version__ = "2.3.6"
diff --git a/djproxy/headers.py b/djproxy/headers.py
index 967be89..e213ec7 100644
--- a/djproxy/headers.py
+++ b/djproxy/headers.py
@@ -3,14 +3,18 @@ from six import iteritems
 
 
 class HeaderDict(dict):
-
     """A dict containing header, value pairings."""
 
     @staticmethod
     def _normalize_django_header_name(header):
         """Unmunge header names modified by Django."""
-        # Remove HTTP_ prefix.
-        new_header = header.rpartition('HTTP_')[2]
+        new_header = header
+        # HTTP header keys in Django's HttpRequest.META dict (except
+        # "CONTENT_TYPE" and "CONTENT_LENGTH") are prefixed with "HTTP_", so it
+        # needs to be removed if present.
+        prefix = 'HTTP_'
+        if new_header.startswith(prefix):
+            new_header = new_header[len(prefix):]
         # Camel case and replace _ with -
         new_header = '-'.join(
             x.capitalize() for x in new_header.split('_'))
diff --git a/setup.py b/setup.py
index 12f0157..4453953 100644
--- a/setup.py
+++ b/setup.py
@@ -32,6 +32,7 @@ setup(
         'Framework :: Django :: 2.1',
         'Framework :: Django :: 2.2',
         'Framework :: Django :: 3.0',
+        'Framework :: Django :: 3.1',
         'Programming Language :: Python',
         'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3.5',


Test patch:
diff --git a/tests/header_tests.py b/tests/header_tests.py
index d8dd7f9..4ebb900 100644
--- a/tests/header_tests.py
+++ b/tests/header_tests.py
@@ -29,6 +29,7 @@ class HeaderDictFromRequestMethod(TestCase):
         self.request.META['HTTP_Fake_Header'] = 'header_value'
         self.request.META['HTTP_X_Forwarded_For'] = 'ipaddr 1'
         self.request.META['HTTP_UNNORMALIZED_HEADER'] = 'header value'
+        self.request.META['HTTP_X_HTTP_METHOD'] = 'MERGE'
         self.request.META['CONTENT_TYPE'] = 'header value'
 
         self.headers = HeaderDict.from_request(self.request)
@@ -39,6 +40,9 @@ class HeaderDictFromRequestMethod(TestCase):
     def test_contains_http_prefixed_headers(self):
         self.assertIn('Fake-Header', self.headers)
 
+    def test_contains_http_prefixed_header_containing_http(self):
+        self.assertIn('X-Http-Method', self.headers)
+
     def test_contains_normalized_header_names(self):
         self.assertIn('Unnormalized-Header', self.headers)