summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorSantiago Basulto <santiago.basulto@gmail.com>2018-05-04 20:37:01 -0300
committerTim Graham <timograham@gmail.com>2019-01-16 13:38:47 -0500
commit4fc35a9c3efdc9154efce28cb23cb84f8834517e (patch)
treea4f7b10244cb933d827cf72ef57dc11e68c1a6ca /django
parentaa5d0a5a90a690dc6f8fdbbffba143e32c86e40a (diff)
Fixed #20147 -- Added HttpRequest.headers.
Diffstat (limited to 'django')
-rw-r--r--django/http/request.py30
-rw-r--r--django/utils/datastructures.py59
2 files changed, 88 insertions, 1 deletions
diff --git a/django/http/request.py b/django/http/request.py
index 7dc758d268..02a127d664 100644
--- a/django/http/request.py
+++ b/django/http/request.py
@@ -12,7 +12,9 @@ from django.core.exceptions import (
)
from django.core.files import uploadhandler
from django.http.multipartparser import MultiPartParser, MultiPartParserError
-from django.utils.datastructures import ImmutableList, MultiValueDict
+from django.utils.datastructures import (
+ CaseInsensitiveMapping, ImmutableList, MultiValueDict,
+)
from django.utils.deprecation import RemovedInDjango30Warning
from django.utils.encoding import escape_uri_path, iri_to_uri
from django.utils.functional import cached_property
@@ -65,6 +67,10 @@ class HttpRequest:
return '<%s>' % self.__class__.__name__
return '<%s: %s %r>' % (self.__class__.__name__, self.method, self.get_full_path())
+ @cached_property
+ def headers(self):
+ return HttpHeaders(self.META)
+
def _get_raw_host(self):
"""
Return the HTTP host using the environment or request headers. Skip
@@ -359,6 +365,28 @@ class HttpRequest:
return list(self)
+class HttpHeaders(CaseInsensitiveMapping):
+ HTTP_PREFIX = 'HTTP_'
+ # PEP 333 gives two headers which aren't prepended with HTTP_.
+ UNPREFIXED_HEADERS = {'CONTENT_TYPE', 'CONTENT_LENGTH'}
+
+ def __init__(self, environ):
+ headers = {}
+ for header, value in environ.items():
+ name = self.parse_header_name(header)
+ if name:
+ headers[name] = value
+ super().__init__(headers)
+
+ @classmethod
+ def parse_header_name(cls, header):
+ if header.startswith(cls.HTTP_PREFIX):
+ header = header[len(cls.HTTP_PREFIX):]
+ elif header not in cls.UNPREFIXED_HEADERS:
+ return None
+ return header.replace('_', '-').title()
+
+
class QueryDict(MultiValueDict):
"""
A specialized MultiValueDict which represents a query string.
diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py
index c5bda0056a..191c2348f6 100644
--- a/django/utils/datastructures.py
+++ b/django/utils/datastructures.py
@@ -1,5 +1,6 @@
import copy
from collections import OrderedDict
+from collections.abc import Mapping
class OrderedSet:
@@ -280,3 +281,61 @@ class DictWrapper(dict):
if use_func:
return self.func(value)
return value
+
+
+def _destruct_iterable_mapping_values(data):
+ for i, elem in enumerate(data):
+ if len(elem) != 2:
+ raise ValueError(
+ 'dictionary update sequence element #{} has '
+ 'length {}; 2 is required.'.format(i, len(elem))
+ )
+ if not isinstance(elem[0], str):
+ raise ValueError('Element key %r invalid, only strings are allowed' % elem[0])
+ yield tuple(elem)
+
+
+class CaseInsensitiveMapping(Mapping):
+ """
+ Mapping allowing case-insensitive key lookups. Original case of keys is
+ preserved for iteration and string representation.
+
+ Example::
+
+ >>> ci_map = CaseInsensitiveMapping({'name': 'Jane'})
+ >>> ci_map['Name']
+ Jane
+ >>> ci_map['NAME']
+ Jane
+ >>> ci_map['name']
+ Jane
+ >>> ci_map # original case preserved
+ {'name': 'Jane'}
+ """
+
+ def __init__(self, data):
+ if not isinstance(data, Mapping):
+ data = {k: v for k, v in _destruct_iterable_mapping_values(data)}
+ self._store = {k.lower(): (k, v) for k, v in data.items()}
+
+ def __getitem__(self, key):
+ return self._store[key.lower()][1]
+
+ def __len__(self):
+ return len(self._store)
+
+ def __eq__(self, other):
+ return isinstance(other, Mapping) and {
+ k.lower(): v for k, v in self.items()
+ } == {
+ k.lower(): v for k, v in other.items()
+ }
+
+ def __iter__(self):
+ return (original_key for original_key, value in self._store.values())
+
+ def __repr__(self):
+ return repr({key: value for key, value in self._store.values()})
+
+ def copy(self):
+ return self