Fixed #31789 -- Added a new headers interface to HttpResponse.

This commit is contained in:
Tom Carrick 2020-07-14 13:32:24 +02:00 committed by Mariusz Felisiak
parent 71ae1ab012
commit bcc2befd0e
47 changed files with 385 additions and 256 deletions

View File

@ -10,7 +10,7 @@ from django.utils.translation import gettext as _
class CSPMiddleware(MiddlewareMixin): class CSPMiddleware(MiddlewareMixin):
"""The admin's JavaScript should be compatible with CSP.""" """The admin's JavaScript should be compatible with CSP."""
def process_response(self, request, response): def process_response(self, request, response):
response['Content-Security-Policy'] = "default-src 'self'" response.headers['Content-Security-Policy'] = "default-src 'self'"
return response return response

View File

@ -24,5 +24,5 @@ class XViewMiddleware(MiddlewareMixin):
if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or
(request.user.is_active and request.user.is_staff)): (request.user.is_active and request.user.is_staff)):
response = HttpResponse() response = HttpResponse()
response['X-View'] = get_view_name(view_func) response.headers['X-View'] = get_view_name(view_func)
return response return response

View File

@ -14,7 +14,7 @@ def x_robots_tag(func):
@wraps(func) @wraps(func)
def inner(request, *args, **kwargs): def inner(request, *args, **kwargs):
response = func(request, *args, **kwargs) response = func(request, *args, **kwargs)
response['X-Robots-Tag'] = 'noindex, noodp, noarchive' response.headers['X-Robots-Tag'] = 'noindex, noodp, noarchive'
return response return response
return inner return inner
@ -88,5 +88,5 @@ def sitemap(request, sitemaps, section=None,
if all_sites_lastmod and lastmod is not None: if all_sites_lastmod and lastmod is not None:
# if lastmod is defined for all sites, set header so as # if lastmod is defined for all sites, set header so as
# ConditionalGetMiddleware is able to send 304 NOT MODIFIED # ConditionalGetMiddleware is able to send 304 NOT MODIFIED
response['Last-Modified'] = http_date(timegm(lastmod)) response.headers['Last-Modified'] = http_date(timegm(lastmod))
return response return response

View File

@ -42,7 +42,7 @@ class Feed:
if hasattr(self, 'item_pubdate') or hasattr(self, 'item_updateddate'): if hasattr(self, 'item_pubdate') or hasattr(self, 'item_updateddate'):
# if item_pubdate or item_updateddate is defined for the feed, set # if item_pubdate or item_updateddate is defined for the feed, set
# header so as ConditionalGetMiddleware is able to send 304 NOT MODIFIED # header so as ConditionalGetMiddleware is able to send 304 NOT MODIFIED
response['Last-Modified'] = http_date( response.headers['Last-Modified'] = http_date(
timegm(feedgen.latest_post_date().utctimetuple())) timegm(feedgen.latest_post_date().utctimetuple()))
feedgen.write(response, 'utf-8') feedgen.write(response, 'utf-8')
return response return response

View File

@ -5,6 +5,7 @@ import os
import re import re
import sys import sys
import time import time
from collections.abc import Mapping
from email.header import Header from email.header import Header
from http.client import responses from http.client import responses
from urllib.parse import quote, urlparse from urllib.parse import quote, urlparse
@ -15,6 +16,7 @@ from django.core.exceptions import DisallowedRedirect
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.http.cookie import SimpleCookie from django.http.cookie import SimpleCookie
from django.utils import timezone from django.utils import timezone
from django.utils.datastructures import CaseInsensitiveMapping
from django.utils.encoding import iri_to_uri from django.utils.encoding import iri_to_uri
from django.utils.http import http_date from django.utils.http import http_date
from django.utils.regex_helper import _lazy_re_compile from django.utils.regex_helper import _lazy_re_compile
@ -22,6 +24,65 @@ from django.utils.regex_helper import _lazy_re_compile
_charset_from_content_type_re = _lazy_re_compile(r';\s*charset=(?P<charset>[^\s;]+)', re.I) _charset_from_content_type_re = _lazy_re_compile(r';\s*charset=(?P<charset>[^\s;]+)', re.I)
class ResponseHeaders(CaseInsensitiveMapping):
def __init__(self, data):
"""
Populate the initial data using __setitem__ to ensure values are
correctly encoded.
"""
if not isinstance(data, Mapping):
data = {
k: v
for k, v in CaseInsensitiveMapping._destruct_iterable_mapping_values(data)
}
self._store = {}
for header, value in data.items():
self[header] = value
def _convert_to_charset(self, value, charset, mime_encode=False):
"""
Convert headers key/value to ascii/latin-1 native strings.
`charset` must be 'ascii' or 'latin-1'. If `mime_encode` is True and
`value` can't be represented in the given charset, apply MIME-encoding.
"""
if not isinstance(value, (bytes, str)):
value = str(value)
if (
(isinstance(value, bytes) and (b'\n' in value or b'\r' in value)) or
(isinstance(value, str) and ('\n' in value or '\r' in value))
):
raise BadHeaderError("Header values can't contain newlines (got %r)" % value)
try:
if isinstance(value, str):
# Ensure string is valid in given charset
value.encode(charset)
else:
# Convert bytestring using given charset
value = value.decode(charset)
except UnicodeError as e:
if mime_encode:
value = Header(value, 'utf-8', maxlinelen=sys.maxsize).encode()
else:
e.reason += ', HTTP response headers must be in %s format' % charset
raise
return value
def __delitem__(self, key):
self.pop(key)
def __setitem__(self, key, value):
key = self._convert_to_charset(key, 'ascii')
value = self._convert_to_charset(value, 'latin-1', mime_encode=True)
self._store[key.lower()] = (key, value)
def pop(self, key, default=None):
return self._store.pop(key.lower(), default)
def setdefault(self, key, value):
if key not in self:
self[key] = value
class BadHeaderError(ValueError): class BadHeaderError(ValueError):
pass pass
@ -37,10 +98,7 @@ class HttpResponseBase:
status_code = 200 status_code = 200
def __init__(self, content_type=None, status=None, reason=None, charset=None): def __init__(self, content_type=None, status=None, reason=None, charset=None):
# _headers is a mapping of the lowercase name to the original case of self.headers = ResponseHeaders({})
# the header (required for working with legacy systems) and the header
# value. Both the name of the header and its value are ASCII strings.
self._headers = {}
self._resource_closers = [] self._resource_closers = []
# This parameter is set by the handler. It's necessary to preserve the # This parameter is set by the handler. It's necessary to preserve the
# historical behavior of request_finished. # historical behavior of request_finished.
@ -95,7 +153,7 @@ class HttpResponseBase:
headers = [ headers = [
(to_bytes(key, 'ascii') + b': ' + to_bytes(value, 'latin-1')) (to_bytes(key, 'ascii') + b': ' + to_bytes(value, 'latin-1'))
for key, value in self._headers.values() for key, value in self.headers.items()
] ]
return b'\r\n'.join(headers) return b'\r\n'.join(headers)
@ -103,57 +161,28 @@ class HttpResponseBase:
@property @property
def _content_type_for_repr(self): def _content_type_for_repr(self):
return ', "%s"' % self['Content-Type'] if 'Content-Type' in self else '' return ', "%s"' % self.headers['Content-Type'] if 'Content-Type' in self.headers else ''
def _convert_to_charset(self, value, charset, mime_encode=False):
"""
Convert headers key/value to ascii/latin-1 native strings.
`charset` must be 'ascii' or 'latin-1'. If `mime_encode` is True and
`value` can't be represented in the given charset, apply MIME-encoding.
"""
if not isinstance(value, (bytes, str)):
value = str(value)
if ((isinstance(value, bytes) and (b'\n' in value or b'\r' in value)) or
isinstance(value, str) and ('\n' in value or '\r' in value)):
raise BadHeaderError("Header values can't contain newlines (got %r)" % value)
try:
if isinstance(value, str):
# Ensure string is valid in given charset
value.encode(charset)
else:
# Convert bytestring using given charset
value = value.decode(charset)
except UnicodeError as e:
if mime_encode:
value = Header(value, 'utf-8', maxlinelen=sys.maxsize).encode()
else:
e.reason += ', HTTP response headers must be in %s format' % charset
raise
return value
def __setitem__(self, header, value): def __setitem__(self, header, value):
header = self._convert_to_charset(header, 'ascii') self.headers[header] = value
value = self._convert_to_charset(value, 'latin-1', mime_encode=True)
self._headers[header.lower()] = (header, value)
def __delitem__(self, header): def __delitem__(self, header):
self._headers.pop(header.lower(), False) del self.headers[header]
def __getitem__(self, header): def __getitem__(self, header):
return self._headers[header.lower()][1] return self.headers[header]
def has_header(self, header): def has_header(self, header):
"""Case-insensitive check for a header.""" """Case-insensitive check for a header."""
return header.lower() in self._headers return header in self.headers
__contains__ = has_header __contains__ = has_header
def items(self): def items(self):
return self._headers.values() return self.headers.items()
def get(self, header, alternate=None): def get(self, header, alternate=None):
return self._headers.get(header.lower(), (None, alternate))[1] return self.headers.get(header, alternate)
def set_cookie(self, key, value='', max_age=None, expires=None, path='/', def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
domain=None, secure=False, httponly=False, samesite=None): domain=None, secure=False, httponly=False, samesite=None):
@ -203,8 +232,7 @@ class HttpResponseBase:
def setdefault(self, key, value): def setdefault(self, key, value):
"""Set a header unless it has already been set.""" """Set a header unless it has already been set."""
if key not in self: self.headers.setdefault(key, value)
self[key] = value
def set_signed_cookie(self, key, value, salt='', **kwargs): def set_signed_cookie(self, key, value, salt='', **kwargs):
value = signing.get_cookie_signer(salt=key + salt).sign(value) value = signing.get_cookie_signer(salt=key + salt).sign(value)
@ -430,19 +458,19 @@ class FileResponse(StreamingHttpResponse):
filename = getattr(filelike, 'name', None) filename = getattr(filelike, 'name', None)
filename = filename if (isinstance(filename, str) and filename) else self.filename filename = filename if (isinstance(filename, str) and filename) else self.filename
if os.path.isabs(filename): if os.path.isabs(filename):
self['Content-Length'] = os.path.getsize(filelike.name) self.headers['Content-Length'] = os.path.getsize(filelike.name)
elif hasattr(filelike, 'getbuffer'): elif hasattr(filelike, 'getbuffer'):
self['Content-Length'] = filelike.getbuffer().nbytes self.headers['Content-Length'] = filelike.getbuffer().nbytes
if self.get('Content-Type', '').startswith('text/html'): if self.headers.get('Content-Type', '').startswith('text/html'):
if filename: if filename:
content_type, encoding = mimetypes.guess_type(filename) content_type, encoding = mimetypes.guess_type(filename)
# Encoding isn't set to prevent browsers from automatically # Encoding isn't set to prevent browsers from automatically
# uncompressing files. # uncompressing files.
content_type = encoding_map.get(encoding, content_type) content_type = encoding_map.get(encoding, content_type)
self['Content-Type'] = content_type or 'application/octet-stream' self.headers['Content-Type'] = content_type or 'application/octet-stream'
else: else:
self['Content-Type'] = 'application/octet-stream' self.headers['Content-Type'] = 'application/octet-stream'
filename = self.filename or os.path.basename(filename) filename = self.filename or os.path.basename(filename)
if filename: if filename:
@ -452,9 +480,9 @@ class FileResponse(StreamingHttpResponse):
file_expr = 'filename="{}"'.format(filename) file_expr = 'filename="{}"'.format(filename)
except UnicodeEncodeError: except UnicodeEncodeError:
file_expr = "filename*=utf-8''{}".format(quote(filename)) file_expr = "filename*=utf-8''{}".format(quote(filename))
self['Content-Disposition'] = '{}; {}'.format(disposition, file_expr) self.headers['Content-Disposition'] = '{}; {}'.format(disposition, file_expr)
elif self.as_attachment: elif self.as_attachment:
self['Content-Disposition'] = 'attachment' self.headers['Content-Disposition'] = 'attachment'
class HttpResponseRedirectBase(HttpResponse): class HttpResponseRedirectBase(HttpResponse):

View File

@ -30,8 +30,10 @@ class XFrameOptionsMiddleware(MiddlewareMixin):
if getattr(response, 'xframe_options_exempt', False): if getattr(response, 'xframe_options_exempt', False):
return response return response
response['X-Frame-Options'] = self.get_xframe_options_value(request, response.headers['X-Frame-Options'] = self.get_xframe_options_value(
response) request,
response,
)
return response return response
def get_xframe_options_value(self, request, response): def get_xframe_options_value(self, request, response):

View File

@ -110,7 +110,7 @@ class CommonMiddleware(MiddlewareMixin):
# Add the Content-Length header to non-streaming responses if not # Add the Content-Length header to non-streaming responses if not
# already set. # already set.
if not response.streaming and not response.has_header('Content-Length'): if not response.streaming and not response.has_header('Content-Length'):
response['Content-Length'] = str(len(response.content)) response.headers['Content-Length'] = str(len(response.content))
return response return response

View File

@ -31,21 +31,21 @@ class GZipMiddleware(MiddlewareMixin):
# Delete the `Content-Length` header for streaming content, because # Delete the `Content-Length` header for streaming content, because
# we won't know the compressed size until we stream it. # we won't know the compressed size until we stream it.
response.streaming_content = compress_sequence(response.streaming_content) response.streaming_content = compress_sequence(response.streaming_content)
del response['Content-Length'] del response.headers['Content-Length']
else: else:
# Return the compressed content only if it's actually shorter. # Return the compressed content only if it's actually shorter.
compressed_content = compress_string(response.content) compressed_content = compress_string(response.content)
if len(compressed_content) >= len(response.content): if len(compressed_content) >= len(response.content):
return response return response
response.content = compressed_content response.content = compressed_content
response['Content-Length'] = str(len(response.content)) response.headers['Content-Length'] = str(len(response.content))
# If there is a strong ETag, make it weak to fulfill the requirements # If there is a strong ETag, make it weak to fulfill the requirements
# of RFC 7232 section-2.1 while also allowing conditional request # of RFC 7232 section-2.1 while also allowing conditional request
# matches on ETags. # matches on ETags.
etag = response.get('ETag') etag = response.get('ETag')
if etag and etag.startswith('"'): if etag and etag.startswith('"'):
response['ETag'] = 'W/' + etag response.headers['ETag'] = 'W/' + etag
response['Content-Encoding'] = 'gzip' response.headers['Content-Encoding'] = 'gzip'
return response return response

View File

@ -57,5 +57,5 @@ class LocaleMiddleware(MiddlewareMixin):
if not (i18n_patterns_used and language_from_path): if not (i18n_patterns_used and language_from_path):
patch_vary_headers(response, ('Accept-Language',)) patch_vary_headers(response, ('Accept-Language',))
response.setdefault('Content-Language', language) response.headers.setdefault('Content-Language', language)
return response return response

View File

@ -38,18 +38,18 @@ class SecurityMiddleware(MiddlewareMixin):
sts_header = sts_header + "; includeSubDomains" sts_header = sts_header + "; includeSubDomains"
if self.sts_preload: if self.sts_preload:
sts_header = sts_header + "; preload" sts_header = sts_header + "; preload"
response['Strict-Transport-Security'] = sts_header response.headers['Strict-Transport-Security'] = sts_header
if self.content_type_nosniff: if self.content_type_nosniff:
response.setdefault('X-Content-Type-Options', 'nosniff') response.headers.setdefault('X-Content-Type-Options', 'nosniff')
if self.xss_filter: if self.xss_filter:
response.setdefault('X-XSS-Protection', '1; mode=block') response.headers.setdefault('X-XSS-Protection', '1; mode=block')
if self.referrer_policy: if self.referrer_policy:
# Support a comma-separated string or iterable of values to allow # Support a comma-separated string or iterable of values to allow
# fallback. # fallback.
response.setdefault('Referrer-Policy', ','.join( response.headers.setdefault('Referrer-Policy', ','.join(
[v.strip() for v in self.referrer_policy.split(',')] [v.strip() for v in self.referrer_policy.split(',')]
if isinstance(self.referrer_policy, str) else self.referrer_policy if isinstance(self.referrer_policy, str) else self.referrer_policy
)) ))

View File

@ -62,7 +62,7 @@ def patch_cache_control(response, **kwargs):
cc = defaultdict(set) cc = defaultdict(set)
if response.get('Cache-Control'): if response.get('Cache-Control'):
for field in cc_delim_re.split(response['Cache-Control']): for field in cc_delim_re.split(response.headers['Cache-Control']):
directive, value = dictitem(field) directive, value = dictitem(field)
if directive == 'no-cache': if directive == 'no-cache':
# no-cache supports multiple field names. # no-cache supports multiple field names.
@ -100,7 +100,7 @@ def patch_cache_control(response, **kwargs):
else: else:
directives.append(dictvalue(directive, values)) directives.append(dictvalue(directive, values))
cc = ', '.join(directives) cc = ', '.join(directives)
response['Cache-Control'] = cc response.headers['Cache-Control'] = cc
def get_max_age(response): def get_max_age(response):
@ -110,7 +110,7 @@ def get_max_age(response):
""" """
if not response.has_header('Cache-Control'): if not response.has_header('Cache-Control'):
return return
cc = dict(_to_tuple(el) for el in cc_delim_re.split(response['Cache-Control'])) cc = dict(_to_tuple(el) for el in cc_delim_re.split(response.headers['Cache-Control']))
try: try:
return int(cc['max-age']) return int(cc['max-age'])
except (ValueError, TypeError, KeyError): except (ValueError, TypeError, KeyError):
@ -119,7 +119,7 @@ def get_max_age(response):
def set_response_etag(response): def set_response_etag(response):
if not response.streaming and response.content: if not response.streaming and response.content:
response['ETag'] = quote_etag(hashlib.md5(response.content).hexdigest()) response.headers['ETag'] = quote_etag(hashlib.md5(response.content).hexdigest())
return response return response
@ -140,7 +140,7 @@ def _not_modified(request, response=None):
# Last-Modified. # Last-Modified.
for header in ('Cache-Control', 'Content-Location', 'Date', 'ETag', 'Expires', 'Last-Modified', 'Vary'): for header in ('Cache-Control', 'Content-Location', 'Date', 'ETag', 'Expires', 'Last-Modified', 'Vary'):
if header in response: if header in response:
new_response[header] = response[header] new_response.headers[header] = response.headers[header]
# Preserve cookies as per the cookie specification: "If a proxy server # Preserve cookies as per the cookie specification: "If a proxy server
# receives a response which contains a Set-cookie header, it should # receives a response which contains a Set-cookie header, it should
@ -261,7 +261,7 @@ def patch_response_headers(response, cache_timeout=None):
if cache_timeout < 0: if cache_timeout < 0:
cache_timeout = 0 # Can't have max-age negative cache_timeout = 0 # Can't have max-age negative
if not response.has_header('Expires'): if not response.has_header('Expires'):
response['Expires'] = http_date(time.time() + cache_timeout) response.headers['Expires'] = http_date(time.time() + cache_timeout)
patch_cache_control(response, max_age=cache_timeout) patch_cache_control(response, max_age=cache_timeout)
@ -284,7 +284,7 @@ def patch_vary_headers(response, newheaders):
# implementations may rely on the order of the Vary contents in, say, # implementations may rely on the order of the Vary contents in, say,
# computing an MD5 hash. # computing an MD5 hash.
if response.has_header('Vary'): if response.has_header('Vary'):
vary_headers = cc_delim_re.split(response['Vary']) vary_headers = cc_delim_re.split(response.headers['Vary'])
else: else:
vary_headers = [] vary_headers = []
# Use .lower() here so we treat headers as case-insensitive. # Use .lower() here so we treat headers as case-insensitive.
@ -293,9 +293,9 @@ def patch_vary_headers(response, newheaders):
if newheader.lower() not in existing_headers] if newheader.lower() not in existing_headers]
vary_headers += additional_headers vary_headers += additional_headers
if '*' in vary_headers: if '*' in vary_headers:
response['Vary'] = '*' response.headers['Vary'] = '*'
else: else:
response['Vary'] = ', '.join(vary_headers) response.headers['Vary'] = ', '.join(vary_headers)
def has_vary_header(response, header_query): def has_vary_header(response, header_query):
@ -304,7 +304,7 @@ def has_vary_header(response, header_query):
""" """
if not response.has_header('Vary'): if not response.has_header('Vary'):
return False return False
vary_headers = cc_delim_re.split(response['Vary']) vary_headers = cc_delim_re.split(response.headers['Vary'])
existing_headers = {header.lower() for header in vary_headers} existing_headers = {header.lower() for header in vary_headers}
return header_query.lower() in existing_headers return header_query.lower() in existing_headers
@ -391,7 +391,7 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cach
# in that case and would result in storing the same content under # in that case and would result in storing the same content under
# multiple keys in the cache. See #18191 for details. # multiple keys in the cache. See #18191 for details.
headerlist = [] headerlist = []
for header in cc_delim_re.split(response['Vary']): for header in cc_delim_re.split(response.headers['Vary']):
header = header.upper().replace('-', '_') header = header.upper().replace('-', '_')
if header != 'ACCEPT_LANGUAGE' or not is_accept_language_redundant: if header != 'ACCEPT_LANGUAGE' or not is_accept_language_redundant:
headerlist.append('HTTP_' + header) headerlist.append('HTTP_' + header)

View File

@ -102,9 +102,9 @@ def condition(etag_func=None, last_modified_func=None):
# and if the request method is safe. # and if the request method is safe.
if request.method in ('GET', 'HEAD'): if request.method in ('GET', 'HEAD'):
if res_last_modified and not response.has_header('Last-Modified'): if res_last_modified and not response.has_header('Last-Modified'):
response['Last-Modified'] = http_date(res_last_modified) response.headers['Last-Modified'] = http_date(res_last_modified)
if res_etag: if res_etag:
response.setdefault('ETag', res_etag) response.headers.setdefault('ETag', res_etag)
return response return response

View File

@ -107,8 +107,8 @@ class View:
def options(self, request, *args, **kwargs): def options(self, request, *args, **kwargs):
"""Handle responding to requests for the OPTIONS HTTP verb.""" """Handle responding to requests for the OPTIONS HTTP verb."""
response = HttpResponse() response = HttpResponse()
response['Allow'] = ', '.join(self._allowed_methods()) response.headers['Allow'] = ', '.join(self._allowed_methods())
response['Content-Length'] = '0' response.headers['Content-Length'] = '0'
return response return response
def _allowed_methods(self): def _allowed_methods(self):

View File

@ -48,9 +48,9 @@ def serve(request, path, document_root=None, show_indexes=False):
content_type, encoding = mimetypes.guess_type(str(fullpath)) content_type, encoding = mimetypes.guess_type(str(fullpath))
content_type = content_type or 'application/octet-stream' content_type = content_type or 'application/octet-stream'
response = FileResponse(fullpath.open('rb'), content_type=content_type) response = FileResponse(fullpath.open('rb'), content_type=content_type)
response["Last-Modified"] = http_date(statobj.st_mtime) response.headers["Last-Modified"] = http_date(statobj.st_mtime)
if encoding: if encoding:
response["Content-Encoding"] = encoding response.headers["Content-Encoding"] = encoding
return response return response

View File

@ -21,7 +21,7 @@ Here's an example::
def some_view(request): def some_view(request):
# Create the HttpResponse object with the appropriate CSV header. # Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(content_type='text/csv') response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' response.headers['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
writer = csv.writer(response) writer = csv.writer(response)
writer.writerow(['First row', 'Foo', 'Bar', 'Baz']) writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
@ -88,7 +88,7 @@ the assembly and transmission of a large CSV file::
writer = csv.writer(pseudo_buffer) writer = csv.writer(pseudo_buffer)
response = StreamingHttpResponse((writer.writerow(row) for row in rows), response = StreamingHttpResponse((writer.writerow(row) for row in rows),
content_type="text/csv") content_type="text/csv")
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' response.headers['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
return response return response
Using the template system Using the template system
@ -109,7 +109,7 @@ Here's an example, which generates the same CSV file as above::
def some_view(request): def some_view(request):
# Create the HttpResponse object with the appropriate CSV header. # Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(content_type='text/csv') response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' response.headers['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
# The data is hard-coded here, but you could load it from a database or # The data is hard-coded here, but you could load it from a database or
# some other source. # some other source.

View File

@ -700,17 +700,29 @@ generators are immediately closed.
If you need the response to be streamed from the iterator to the client, you If you need the response to be streamed from the iterator to the client, you
must use the :class:`StreamingHttpResponse` class instead. must use the :class:`StreamingHttpResponse` class instead.
.. _setting-header-fields:
Setting header fields Setting header fields
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
To set or remove a header field in your response, treat it like a dictionary:: To set or remove a header field in your response, use
:attr:`HttpResponse.headers`::
>>> response = HttpResponse()
>>> response.headers['Age'] = 120
>>> del response.headers['Age']
You can also manipulate headers by treating your response like a dictionary::
>>> response = HttpResponse() >>> response = HttpResponse()
>>> response['Age'] = 120 >>> response['Age'] = 120
>>> del response['Age'] >>> del response['Age']
Note that unlike a dictionary, ``del`` doesn't raise ``KeyError`` if the header This proxies to ``HttpResponse.headers``, and is the original interface offered
field doesn't exist. by ``HttpResponse``.
When using this interface, unlike a dictionary, ``del`` doesn't raise
``KeyError`` if the header field doesn't exist.
For setting the ``Cache-Control`` and ``Vary`` header fields, it is recommended For setting the ``Cache-Control`` and ``Vary`` header fields, it is recommended
to use the :func:`~django.utils.cache.patch_cache_control` and to use the :func:`~django.utils.cache.patch_cache_control` and
@ -722,6 +734,10 @@ middleware, are not removed.
HTTP header fields cannot contain newlines. An attempt to set a header field HTTP header fields cannot contain newlines. An attempt to set a header field
containing a newline character (CR or LF) will raise ``BadHeaderError`` containing a newline character (CR or LF) will raise ``BadHeaderError``
.. versionchanged:: 3.2
The :attr:`HttpResponse.headers` interface was added.
Telling the browser to treat the response as a file attachment Telling the browser to treat the response as a file attachment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -730,7 +746,7 @@ To tell the browser to treat the response as a file attachment, use the
this is how you might return a Microsoft Excel spreadsheet:: this is how you might return a Microsoft Excel spreadsheet::
>>> response = HttpResponse(my_data, content_type='application/vnd.ms-excel') >>> response = HttpResponse(my_data, content_type='application/vnd.ms-excel')
>>> response['Content-Disposition'] = 'attachment; filename="foo.xls"' >>> response.headers['Content-Disposition'] = 'attachment; filename="foo.xls"'
There's nothing Django-specific about the ``Content-Disposition`` header, but There's nothing Django-specific about the ``Content-Disposition`` header, but
it's easy to forget the syntax, so we've included it here. it's easy to forget the syntax, so we've included it here.
@ -742,6 +758,13 @@ Attributes
A bytestring representing the content, encoded from a string if necessary. A bytestring representing the content, encoded from a string if necessary.
.. attribute:: HttpResponse.headers
.. versionadded:: 3.2
A case insensitive, dict-like object that provides an interface to all
HTTP headers on the response. See :ref:`setting-header-fields`.
.. attribute:: HttpResponse.charset .. attribute:: HttpResponse.charset
A string denoting the charset in which the response will be encoded. If not A string denoting the charset in which the response will be encoded. If not

View File

@ -309,7 +309,10 @@ Pagination
Requests and Responses Requests and Responses
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
* ... * Response headers are now stored in :attr:`.HttpResponse.headers`. This can be
used instead of the original dict-like interface of ``HttpResponse`` objects.
Both interfaces will continue to be supported. See
:ref:`setting-header-fields` for details.
Security Security
~~~~~~~~ ~~~~~~~~

View File

@ -1159,10 +1159,10 @@ In this case, a caching mechanism (such as Django's own cache middleware) will
cache a separate version of the page for each unique user-agent. cache a separate version of the page for each unique user-agent.
The advantage to using the ``vary_on_headers`` decorator rather than manually The advantage to using the ``vary_on_headers`` decorator rather than manually
setting the ``Vary`` header (using something like setting the ``Vary`` header (using something like ``response.headers['Vary'] =
``response['Vary'] = 'user-agent'``) is that the decorator *adds* to the 'user-agent'``) is that the decorator *adds* to the ``Vary`` header (which may
``Vary`` header (which may already exist), rather than setting it from scratch already exist), rather than setting it from scratch and potentially overriding
and potentially overriding anything that was already in there. anything that was already in there.
You can pass multiple headers to ``vary_on_headers()``:: You can pass multiple headers to ``vary_on_headers()``::

View File

@ -119,7 +119,7 @@ And the view::
last_book = self.get_queryset().latest('publication_date') last_book = self.get_queryset().latest('publication_date')
response = HttpResponse() response = HttpResponse()
# RFC 1123 date format # RFC 1123 date format
response['Last-Modified'] = last_book.publication_date.strftime('%a, %d %b %Y %H:%M:%S GMT') response.headers['Last-Modified'] = last_book.publication_date.strftime('%a, %d %b %Y %H:%M:%S GMT')
return response return response
If the view is accessed from a ``GET`` request, an object list is returned in If the view is accessed from a ``GET`` request, an object list is returned in

View File

@ -551,9 +551,9 @@ Specifically, a ``Response`` object has the following attributes:
If the given URL is not found, accessing this attribute will raise a If the given URL is not found, accessing this attribute will raise a
:exc:`~django.urls.Resolver404` exception. :exc:`~django.urls.Resolver404` exception.
You can also use dictionary syntax on the response object to query the value As with a normal response, you can also access the headers through
of any settings in the HTTP headers. For example, you could determine the :attr:`.HttpResponse.headers`. For example, you could determine the content
content type of a response using ``response['Content-Type']``. type of a response using ``response.headers['Content-Type']``.
Exceptions Exceptions
---------- ----------

View File

@ -13,7 +13,7 @@ class XViewMiddlewareTest(TestDataMixin, AdminDocsTestCase):
self.client.force_login(self.superuser) self.client.force_login(self.superuser)
response = self.client.head('/xview/func/') response = self.client.head('/xview/func/')
self.assertIn('X-View', response) self.assertIn('X-View', response)
self.assertEqual(response['X-View'], 'admin_docs.views.xview') self.assertEqual(response.headers['X-View'], 'admin_docs.views.xview')
user.is_staff = False user.is_staff = False
user.save() user.save()
response = self.client.head('/xview/func/') response = self.client.head('/xview/func/')
@ -31,7 +31,7 @@ class XViewMiddlewareTest(TestDataMixin, AdminDocsTestCase):
self.client.force_login(self.superuser) self.client.force_login(self.superuser)
response = self.client.head('/xview/class/') response = self.client.head('/xview/class/')
self.assertIn('X-View', response) self.assertIn('X-View', response)
self.assertEqual(response['X-View'], 'admin_docs.views.XViewClass') self.assertEqual(response.headers['X-View'], 'admin_docs.views.XViewClass')
user.is_staff = False user.is_staff = False
user.save() user.save()
response = self.client.head('/xview/class/') response = self.client.head('/xview/class/')
@ -45,7 +45,7 @@ class XViewMiddlewareTest(TestDataMixin, AdminDocsTestCase):
def test_callable_object_view(self): def test_callable_object_view(self):
self.client.force_login(self.superuser) self.client.force_login(self.superuser)
response = self.client.head('/xview/callable_object/') response = self.client.head('/xview/callable_object/')
self.assertEqual(response['X-View'], 'admin_docs.views.XViewCallableObject') self.assertEqual(response.headers['X-View'], 'admin_docs.views.XViewCallableObject')
@override_settings(MIDDLEWARE=[]) @override_settings(MIDDLEWARE=[])
def test_no_auth_middleware(self): def test_no_auth_middleware(self):

View File

@ -2964,7 +2964,7 @@ class AdminViewStringPrimaryKeyTest(TestCase):
) )
self.assertEqual(response.status_code, 302) # temporary redirect self.assertEqual(response.status_code, 302) # temporary redirect
self.assertIn('/123_2Fhistory/', response['location']) # PK is quoted self.assertIn('/123_2Fhistory/', response.headers['location']) # PK is quoted
@override_settings(ROOT_URLCONF='admin_views.urls') @override_settings(ROOT_URLCONF='admin_views.urls')

View File

@ -993,7 +993,7 @@ class LogoutTest(AuthViewsTestCase):
in #25490. in #25490.
""" """
response = self.client.get('/logout/') response = self.client.get('/logout/')
self.assertIn('no-store', response['Cache-Control']) self.assertIn('no-store', response.headers['Cache-Control'])
def test_logout_with_overridden_redirect_url(self): def test_logout_with_overridden_redirect_url(self):
# Bug 11223 # Bug 11223

18
tests/cache/tests.py vendored
View File

@ -1700,9 +1700,9 @@ class CacheUtils(SimpleTestCase):
with self.subTest(initial_vary=initial_vary, newheaders=newheaders): with self.subTest(initial_vary=initial_vary, newheaders=newheaders):
response = HttpResponse() response = HttpResponse()
if initial_vary is not None: if initial_vary is not None:
response['Vary'] = initial_vary response.headers['Vary'] = initial_vary
patch_vary_headers(response, newheaders) patch_vary_headers(response, newheaders)
self.assertEqual(response['Vary'], resulting_vary) self.assertEqual(response.headers['Vary'], resulting_vary)
def test_get_cache_key(self): def test_get_cache_key(self):
request = self.factory.get(self.path) request = self.factory.get(self.path)
@ -1753,7 +1753,7 @@ class CacheUtils(SimpleTestCase):
def test_learn_cache_key(self): def test_learn_cache_key(self):
request = self.factory.head(self.path) request = self.factory.head(self.path)
response = HttpResponse() response = HttpResponse()
response['Vary'] = 'Pony' response.headers['Vary'] = 'Pony'
# Make sure that the Vary header is added to the key hash # Make sure that the Vary header is added to the key hash
learn_cache_key(request, response) learn_cache_key(request, response)
@ -1795,9 +1795,9 @@ class CacheUtils(SimpleTestCase):
with self.subTest(initial_cc=initial_cc, newheaders=newheaders): with self.subTest(initial_cc=initial_cc, newheaders=newheaders):
response = HttpResponse() response = HttpResponse()
if initial_cc is not None: if initial_cc is not None:
response['Cache-Control'] = initial_cc response.headers['Cache-Control'] = initial_cc
patch_cache_control(response, **newheaders) patch_cache_control(response, **newheaders)
parts = set(cc_delim_re.split(response['Cache-Control'])) parts = set(cc_delim_re.split(response.headers['Cache-Control']))
self.assertEqual(parts, expected_cc) self.assertEqual(parts, expected_cc)
@ -1892,7 +1892,7 @@ class CacheI18nTest(SimpleTestCase):
request.META['HTTP_ACCEPT_LANGUAGE'] = accept_language request.META['HTTP_ACCEPT_LANGUAGE'] = accept_language
request.META['HTTP_ACCEPT_ENCODING'] = 'gzip;q=1.0, identity; q=0.5, *;q=0' request.META['HTTP_ACCEPT_ENCODING'] = 'gzip;q=1.0, identity; q=0.5, *;q=0'
response = HttpResponse() response = HttpResponse()
response['Vary'] = vary response.headers['Vary'] = vary
key = learn_cache_key(request, response) key = learn_cache_key(request, response)
key2 = get_cache_key(request) key2 = get_cache_key(request)
self.assertEqual(key, reference_key) self.assertEqual(key, reference_key)
@ -1905,7 +1905,7 @@ class CacheI18nTest(SimpleTestCase):
request = self.factory.get(self.path) request = self.factory.get(self.path)
request.META['HTTP_ACCEPT_ENCODING'] = 'gzip;q=1.0, identity; q=0.5, *;q=0' request.META['HTTP_ACCEPT_ENCODING'] = 'gzip;q=1.0, identity; q=0.5, *;q=0'
response = HttpResponse() response = HttpResponse()
response['Vary'] = 'accept-encoding' response.headers['Vary'] = 'accept-encoding'
key = learn_cache_key(request, response) key = learn_cache_key(request, response)
self.assertIn(lang, key, "Cache keys should include the language name when translation is active") self.assertIn(lang, key, "Cache keys should include the language name when translation is active")
self.check_accept_language_vary( self.check_accept_language_vary(
@ -2364,9 +2364,9 @@ class TestWithTemplateResponse(SimpleTestCase):
template = engines['django'].from_string("This is a test") template = engines['django'].from_string("This is a test")
response = TemplateResponse(HttpRequest(), template) response = TemplateResponse(HttpRequest(), template)
if initial_vary is not None: if initial_vary is not None:
response['Vary'] = initial_vary response.headers['Vary'] = initial_vary
patch_vary_headers(response, newheaders) patch_vary_headers(response, newheaders)
self.assertEqual(response['Vary'], resulting_vary) self.assertEqual(response.headers['Vary'], resulting_vary)
def test_get_cache_key(self): def test_get_cache_key(self):
request = self.factory.get(self.path) request = self.factory.get(self.path)

View File

@ -21,12 +21,12 @@ class ConditionalGet(SimpleTestCase):
self.assertEqual(response.content, FULL_RESPONSE.encode()) self.assertEqual(response.content, FULL_RESPONSE.encode())
if response.request['REQUEST_METHOD'] in ('GET', 'HEAD'): if response.request['REQUEST_METHOD'] in ('GET', 'HEAD'):
if check_last_modified: if check_last_modified:
self.assertEqual(response['Last-Modified'], LAST_MODIFIED_STR) self.assertEqual(response.headers['Last-Modified'], LAST_MODIFIED_STR)
if check_etag: if check_etag:
self.assertEqual(response['ETag'], ETAG) self.assertEqual(response.headers['ETag'], ETAG)
else: else:
self.assertNotIn('Last-Modified', response) self.assertNotIn('Last-Modified', response.headers)
self.assertNotIn('ETag', response) self.assertNotIn('ETag', response.headers)
def assertNotModified(self, response): def assertNotModified(self, response):
self.assertEqual(response.status_code, 304) self.assertEqual(response.status_code, 304)

View File

@ -184,11 +184,11 @@ class ShortcutViewTests(TestCase):
response = shortcut(self.request, user_ct.id, obj.id) response = shortcut(self.request, user_ct.id, obj.id)
self.assertEqual( self.assertEqual(
'http://%s/users/john/' % get_current_site(self.request).domain, 'http://%s/users/john/' % get_current_site(self.request).domain,
response._headers.get('location')[1] response.headers.get('location')
) )
with self.modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}): with self.modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}):
response = shortcut(self.request, user_ct.id, obj.id) response = shortcut(self.request, user_ct.id, obj.id)
self.assertEqual('http://Example.com/users/john/', response._headers.get('location')[1]) self.assertEqual('http://Example.com/users/john/', response.headers.get('location'))
def test_model_without_get_absolute_url(self): def test_model_without_get_absolute_url(self):
"""The view returns 404 when Model.get_absolute_url() isn't defined.""" """The view returns 404 when Model.get_absolute_url() isn't defined."""

View File

@ -438,7 +438,7 @@ class XFrameOptionsDecoratorsTests(TestCase):
def a_view(request): def a_view(request):
return HttpResponse() return HttpResponse()
r = a_view(HttpRequest()) r = a_view(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'DENY') self.assertEqual(r.headers['X-Frame-Options'], 'DENY')
def test_sameorigin_decorator(self): def test_sameorigin_decorator(self):
""" """
@ -449,7 +449,7 @@ class XFrameOptionsDecoratorsTests(TestCase):
def a_view(request): def a_view(request):
return HttpResponse() return HttpResponse()
r = a_view(HttpRequest()) r = a_view(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'SAMEORIGIN') self.assertEqual(r.headers['X-Frame-Options'], 'SAMEORIGIN')
def test_exempt_decorator(self): def test_exempt_decorator(self):
""" """
@ -477,6 +477,6 @@ class NeverCacheDecoratorTest(TestCase):
return HttpResponse() return HttpResponse()
r = a_view(HttpRequest()) r = a_view(HttpRequest())
self.assertEqual( self.assertEqual(
set(r['Cache-Control'].split(', ')), set(r.headers['Cache-Control'].split(', ')),
{'max-age=0', 'no-cache', 'no-store', 'must-revalidate', 'private'}, {'max-age=0', 'no-cache', 'no-store', 'must-revalidate', 'private'},
) )

View File

@ -195,7 +195,7 @@ class ViewTest(SimpleTestCase):
view = SimpleView.as_view() view = SimpleView.as_view()
response = view(request) response = view(request)
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
self.assertTrue(response['Allow']) self.assertTrue(response.headers['Allow'])
def test_options_for_get_view(self): def test_options_for_get_view(self):
""" """
@ -226,7 +226,7 @@ class ViewTest(SimpleTestCase):
def _assert_allows(self, response, *expected_methods): def _assert_allows(self, response, *expected_methods):
"Assert allowed HTTP methods reported in the Allow response header" "Assert allowed HTTP methods reported in the Allow response header"
response_allows = set(response['Allow'].split(', ')) response_allows = set(response.headers['Allow'].split(', '))
self.assertEqual(set(expected_methods + ('OPTIONS',)), response_allows) self.assertEqual(set(expected_methods + ('OPTIONS',)), response_allows)
def test_args_kwargs_request_on_self(self): def test_args_kwargs_request_on_self(self):
@ -390,7 +390,7 @@ class TemplateViewTest(SimpleTestCase):
def test_content_type(self): def test_content_type(self):
response = self.client.get('/template/content_type/') response = self.client.get('/template/content_type/')
self.assertEqual(response['Content-Type'], 'text/plain') self.assertEqual(response.headers['Content-Type'], 'text/plain')
def test_resolve_view(self): def test_resolve_view(self):
match = resolve('/template/content_type/') match = resolve('/template/content_type/')
@ -461,12 +461,12 @@ class RedirectViewTest(SimpleTestCase):
"Named pattern parameter should reverse to the matching pattern" "Named pattern parameter should reverse to the matching pattern"
response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), pk=1) response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), pk=1)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], '/detail/artist/1/') self.assertEqual(response.headers['Location'], '/detail/artist/1/')
def test_named_url_pattern_using_args(self): def test_named_url_pattern_using_args(self):
response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), 1) response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), 1)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], '/detail/artist/1/') self.assertEqual(response.headers['Location'], '/detail/artist/1/')
def test_redirect_POST(self): def test_redirect_POST(self):
"Default is a temporary redirect" "Default is a temporary redirect"

View File

@ -292,44 +292,44 @@ class HttpResponseTests(unittest.TestCase):
r = HttpResponse() r = HttpResponse()
# ASCII strings or bytes values are converted to strings. # ASCII strings or bytes values are converted to strings.
r['key'] = 'test' r.headers['key'] = 'test'
self.assertEqual(r['key'], 'test') self.assertEqual(r.headers['key'], 'test')
r['key'] = b'test' r.headers['key'] = b'test'
self.assertEqual(r['key'], 'test') self.assertEqual(r.headers['key'], 'test')
self.assertIn(b'test', r.serialize_headers()) self.assertIn(b'test', r.serialize_headers())
# Non-ASCII values are serialized to Latin-1. # Non-ASCII values are serialized to Latin-1.
r['key'] = 'café' r.headers['key'] = 'café'
self.assertIn('café'.encode('latin-1'), r.serialize_headers()) self.assertIn('café'.encode('latin-1'), r.serialize_headers())
# Other Unicode values are MIME-encoded (there's no way to pass them as # Other Unicode values are MIME-encoded (there's no way to pass them as
# bytes). # bytes).
r['key'] = '' r.headers['key'] = ''
self.assertEqual(r['key'], '=?utf-8?b?4oCg?=') self.assertEqual(r.headers['key'], '=?utf-8?b?4oCg?=')
self.assertIn(b'=?utf-8?b?4oCg?=', r.serialize_headers()) self.assertIn(b'=?utf-8?b?4oCg?=', r.serialize_headers())
# The response also converts string or bytes keys to strings, but requires # The response also converts string or bytes keys to strings, but requires
# them to contain ASCII # them to contain ASCII
r = HttpResponse() r = HttpResponse()
del r['Content-Type'] del r.headers['Content-Type']
r['foo'] = 'bar' r.headers['foo'] = 'bar'
headers = list(r.items()) headers = list(r.headers.items())
self.assertEqual(len(headers), 1) self.assertEqual(len(headers), 1)
self.assertEqual(headers[0], ('foo', 'bar')) self.assertEqual(headers[0], ('foo', 'bar'))
r = HttpResponse() r = HttpResponse()
del r['Content-Type'] del r.headers['Content-Type']
r[b'foo'] = 'bar' r.headers[b'foo'] = 'bar'
headers = list(r.items()) headers = list(r.headers.items())
self.assertEqual(len(headers), 1) self.assertEqual(len(headers), 1)
self.assertEqual(headers[0], ('foo', 'bar')) self.assertEqual(headers[0], ('foo', 'bar'))
self.assertIsInstance(headers[0][0], str) self.assertIsInstance(headers[0][0], str)
r = HttpResponse() r = HttpResponse()
with self.assertRaises(UnicodeError): with self.assertRaises(UnicodeError):
r.__setitem__('føø', 'bar') r.headers.__setitem__('føø', 'bar')
with self.assertRaises(UnicodeError): with self.assertRaises(UnicodeError):
r.__setitem__('føø'.encode(), 'bar') r.headers.__setitem__('føø'.encode(), 'bar')
def test_long_line(self): def test_long_line(self):
# Bug #20889: long lines trigger newlines to be added to headers # Bug #20889: long lines trigger newlines to be added to headers
@ -337,18 +337,18 @@ class HttpResponseTests(unittest.TestCase):
h = HttpResponse() h = HttpResponse()
f = b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz a\xcc\x88' f = b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz a\xcc\x88'
f = f.decode('utf-8') f = f.decode('utf-8')
h['Content-Disposition'] = 'attachment; filename="%s"' % f h.headers['Content-Disposition'] = 'attachment; filename="%s"' % f
# This one is triggering https://bugs.python.org/issue20747, that is Python # This one is triggering https://bugs.python.org/issue20747, that is Python
# will itself insert a newline in the header # will itself insert a newline in the header
h['Content-Disposition'] = 'attachment; filename="EdelRot_Blu\u0308te (3)-0.JPG"' h.headers['Content-Disposition'] = 'attachment; filename="EdelRot_Blu\u0308te (3)-0.JPG"'
def test_newlines_in_headers(self): def test_newlines_in_headers(self):
# Bug #10188: Do not allow newlines in headers (CR or LF) # Bug #10188: Do not allow newlines in headers (CR or LF)
r = HttpResponse() r = HttpResponse()
with self.assertRaises(BadHeaderError): with self.assertRaises(BadHeaderError):
r.__setitem__('test\rstr', 'test') r.headers.__setitem__('test\rstr', 'test')
with self.assertRaises(BadHeaderError): with self.assertRaises(BadHeaderError):
r.__setitem__('test\nstr', 'test') r.headers.__setitem__('test\nstr', 'test')
def test_dict_behavior(self): def test_dict_behavior(self):
""" """
@ -436,7 +436,7 @@ class HttpResponseTests(unittest.TestCase):
# with Content-Encoding header # with Content-Encoding header
r = HttpResponse() r = HttpResponse()
r['Content-Encoding'] = 'winning' r.headers['Content-Encoding'] = 'winning'
r.write(b'abc') r.write(b'abc')
r.write(b'def') r.write(b'def')
self.assertEqual(r.content, b'abcdef') self.assertEqual(r.content, b'abcdef')
@ -462,6 +462,14 @@ class HttpResponseTests(unittest.TestCase):
with self.assertRaises(DisallowedRedirect): with self.assertRaises(DisallowedRedirect):
HttpResponsePermanentRedirect(url) HttpResponsePermanentRedirect(url)
def test_header_deletion(self):
r = HttpResponse('hello')
r.headers['X-Foo'] = 'foo'
del r.headers['X-Foo']
self.assertNotIn('X-Foo', r.headers)
# del doesn't raise a KeyError on nonexistent headers.
del r.headers['X-Foo']
class HttpResponseSubclassesTests(SimpleTestCase): class HttpResponseSubclassesTests(SimpleTestCase):
def test_redirect(self): def test_redirect(self):
@ -474,7 +482,7 @@ class HttpResponseSubclassesTests(SimpleTestCase):
content_type='text/html', content_type='text/html',
) )
self.assertContains(response, 'The resource has temporarily moved', status_code=302) self.assertContains(response, 'The resource has temporarily moved', status_code=302)
self.assertEqual(response.url, response['Location']) self.assertEqual(response.url, response.headers['Location'])
def test_redirect_lazy(self): def test_redirect_lazy(self):
"""Make sure HttpResponseRedirect works with lazy strings.""" """Make sure HttpResponseRedirect works with lazy strings."""
@ -523,7 +531,7 @@ class HttpResponseSubclassesTests(SimpleTestCase):
def test_not_allowed_repr_no_content_type(self): def test_not_allowed_repr_no_content_type(self):
response = HttpResponseNotAllowed(('GET', 'POST')) response = HttpResponseNotAllowed(('GET', 'POST'))
del response['Content-Type'] del response.headers['Content-Type']
self.assertEqual(repr(response), '<HttpResponseNotAllowed [GET, POST] status_code=405>') self.assertEqual(repr(response), '<HttpResponseNotAllowed [GET, POST] status_code=405>')
@ -785,3 +793,32 @@ class CookieTests(unittest.TestCase):
for proto in range(pickle.HIGHEST_PROTOCOL + 1): for proto in range(pickle.HIGHEST_PROTOCOL + 1):
C1 = pickle.loads(pickle.dumps(C, protocol=proto)) C1 = pickle.loads(pickle.dumps(C, protocol=proto))
self.assertEqual(C1.output(), expected_output) self.assertEqual(C1.output(), expected_output)
class HttpResponseHeadersTestCase(SimpleTestCase):
"""Headers by treating HttpResponse like a dictionary."""
def test_headers(self):
response = HttpResponse()
response['X-Foo'] = 'bar'
self.assertEqual(response['X-Foo'], 'bar')
self.assertEqual(response.headers['X-Foo'], 'bar')
self.assertIn('X-Foo', response)
self.assertIs(response.has_header('X-Foo'), True)
del response['X-Foo']
self.assertNotIn('X-Foo', response)
self.assertNotIn('X-Foo', response.headers)
# del doesn't raise a KeyError on nonexistent headers.
del response['X-Foo']
def test_headers_bytestring(self):
response = HttpResponse()
response['X-Foo'] = b'bar'
self.assertEqual(response['X-Foo'], 'bar')
self.assertEqual(response.headers['X-Foo'], 'bar')
def test_newlines_in_headers(self):
response = HttpResponse()
with self.assertRaises(BadHeaderError):
response['test\rstr'] = 'test'
with self.assertRaises(BadHeaderError):
response['test\nstr'] = 'test'

View File

@ -116,7 +116,7 @@ class PathUnusedTests(URLTestCaseBase):
def test_no_lang_activate(self): def test_no_lang_activate(self):
response = self.client.get('/nl/foo/') response = self.client.get('/nl/foo/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response['content-language'], 'en') self.assertEqual(response.headers['content-language'], 'en')
self.assertEqual(response.context['LANGUAGE_CODE'], 'en') self.assertEqual(response.context['LANGUAGE_CODE'], 'en')
@ -200,7 +200,7 @@ class URLRedirectTests(URLTestCaseBase):
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en') response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en')
self.assertRedirects(response, '/en/account/register/') self.assertRedirects(response, '/en/account/register/')
response = self.client.get(response['location']) response = self.client.get(response.headers['location'])
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_en_redirect_wrong_url(self): def test_en_redirect_wrong_url(self):
@ -211,7 +211,7 @@ class URLRedirectTests(URLTestCaseBase):
response = self.client.get('/profiel/registreren/', HTTP_ACCEPT_LANGUAGE='nl') response = self.client.get('/profiel/registreren/', HTTP_ACCEPT_LANGUAGE='nl')
self.assertRedirects(response, '/nl/profiel/registreren/') self.assertRedirects(response, '/nl/profiel/registreren/')
response = self.client.get(response['location']) response = self.client.get(response.headers['location'])
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_nl_redirect_wrong_url(self): def test_nl_redirect_wrong_url(self):
@ -222,7 +222,7 @@ class URLRedirectTests(URLTestCaseBase):
response = self.client.get('/conta/registre-se/', HTTP_ACCEPT_LANGUAGE='pt-br') response = self.client.get('/conta/registre-se/', HTTP_ACCEPT_LANGUAGE='pt-br')
self.assertRedirects(response, '/pt-br/conta/registre-se/') self.assertRedirects(response, '/pt-br/conta/registre-se/')
response = self.client.get(response['location']) response = self.client.get(response.headers['location'])
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_pl_pl_redirect(self): def test_pl_pl_redirect(self):
@ -230,7 +230,7 @@ class URLRedirectTests(URLTestCaseBase):
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='pl-pl') response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='pl-pl')
self.assertRedirects(response, '/en/account/register/') self.assertRedirects(response, '/en/account/register/')
response = self.client.get(response['location']) response = self.client.get(response.headers['location'])
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@override_settings( @override_settings(
@ -258,7 +258,7 @@ class URLVaryAcceptLanguageTests(URLTestCaseBase):
self.assertRedirects(response, '/en/account/register/') self.assertRedirects(response, '/en/account/register/')
self.assertFalse(response.get('Vary')) self.assertFalse(response.get('Vary'))
response = self.client.get(response['location']) response = self.client.get(response.headers['location'])
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertFalse(response.get('Vary')) self.assertFalse(response.get('Vary'))
@ -297,7 +297,7 @@ class URLRedirectWithoutTrailingSlashSettingTests(URLTestCaseBase):
response = self.client.get('/account/register-without-slash', HTTP_ACCEPT_LANGUAGE='en') response = self.client.get('/account/register-without-slash', HTTP_ACCEPT_LANGUAGE='en')
self.assertRedirects(response, '/en/account/register-without-slash', 302) self.assertRedirects(response, '/en/account/register-without-slash', 302)
response = self.client.get(response['location']) response = self.client.get(response.headers['location'])
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@ -310,13 +310,13 @@ class URLResponseTests(URLTestCaseBase):
def test_en_url(self): def test_en_url(self):
response = self.client.get('/en/account/register/') response = self.client.get('/en/account/register/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response['content-language'], 'en') self.assertEqual(response.headers['content-language'], 'en')
self.assertEqual(response.context['LANGUAGE_CODE'], 'en') self.assertEqual(response.context['LANGUAGE_CODE'], 'en')
def test_nl_url(self): def test_nl_url(self):
response = self.client.get('/nl/profiel/registreren/') response = self.client.get('/nl/profiel/registreren/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response['content-language'], 'nl') self.assertEqual(response.headers['content-language'], 'nl')
self.assertEqual(response.context['LANGUAGE_CODE'], 'nl') self.assertEqual(response.context['LANGUAGE_CODE'], 'nl')
def test_wrong_en_prefix(self): def test_wrong_en_prefix(self):
@ -330,19 +330,19 @@ class URLResponseTests(URLTestCaseBase):
def test_pt_br_url(self): def test_pt_br_url(self):
response = self.client.get('/pt-br/conta/registre-se/') response = self.client.get('/pt-br/conta/registre-se/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response['content-language'], 'pt-br') self.assertEqual(response.headers['content-language'], 'pt-br')
self.assertEqual(response.context['LANGUAGE_CODE'], 'pt-br') self.assertEqual(response.context['LANGUAGE_CODE'], 'pt-br')
def test_en_path(self): def test_en_path(self):
response = self.client.get('/en/account/register-as-path/') response = self.client.get('/en/account/register-as-path/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response['content-language'], 'en') self.assertEqual(response.headers['content-language'], 'en')
self.assertEqual(response.context['LANGUAGE_CODE'], 'en') self.assertEqual(response.context['LANGUAGE_CODE'], 'en')
def test_nl_path(self): def test_nl_path(self):
response = self.client.get('/nl/profiel/registreren-als-pad/') response = self.client.get('/nl/profiel/registreren-als-pad/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response['content-language'], 'nl') self.assertEqual(response.headers['content-language'], 'nl')
self.assertEqual(response.context['LANGUAGE_CODE'], 'nl') self.assertEqual(response.context['LANGUAGE_CODE'], 'nl')

View File

@ -17,7 +17,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
response = HttpResponse(*args, **kwargs) response = HttpResponse(*args, **kwargs)
if headers: if headers:
for k, v in headers.items(): for k, v in headers.items():
response[k] = v response.headers[k] = v
return response return response
return get_response return get_response
@ -47,7 +47,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
"Strict-Transport-Security: max-age=3600" to the response. "Strict-Transport-Security: max-age=3600" to the response.
""" """
self.assertEqual( self.assertEqual(
self.process_response(secure=True)["Strict-Transport-Security"], self.process_response(secure=True).headers['Strict-Transport-Security'],
'max-age=3600', 'max-age=3600',
) )
@ -60,7 +60,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
response = self.process_response( response = self.process_response(
secure=True, secure=True,
headers={"Strict-Transport-Security": "max-age=7200"}) headers={"Strict-Transport-Security": "max-age=7200"})
self.assertEqual(response["Strict-Transport-Security"], "max-age=7200") self.assertEqual(response.headers["Strict-Transport-Security"], "max-age=7200")
@override_settings(SECURE_HSTS_SECONDS=3600) @override_settings(SECURE_HSTS_SECONDS=3600)
def test_sts_only_if_secure(self): def test_sts_only_if_secure(self):
@ -68,7 +68,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
The "Strict-Transport-Security" header is not added to responses going The "Strict-Transport-Security" header is not added to responses going
over an insecure connection. over an insecure connection.
""" """
self.assertNotIn("Strict-Transport-Security", self.process_response(secure=False)) self.assertNotIn(
'Strict-Transport-Security',
self.process_response(secure=False).headers,
)
@override_settings(SECURE_HSTS_SECONDS=0) @override_settings(SECURE_HSTS_SECONDS=0)
def test_sts_off(self): def test_sts_off(self):
@ -76,7 +79,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
With SECURE_HSTS_SECONDS=0, the middleware does not add a With SECURE_HSTS_SECONDS=0, the middleware does not add a
"Strict-Transport-Security" header to the response. "Strict-Transport-Security" header to the response.
""" """
self.assertNotIn("Strict-Transport-Security", self.process_response(secure=True)) self.assertNotIn(
'Strict-Transport-Security',
self.process_response(secure=True).headers,
)
@override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=True) @override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=True)
def test_sts_include_subdomains(self): def test_sts_include_subdomains(self):
@ -86,7 +92,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
"includeSubDomains" directive to the response. "includeSubDomains" directive to the response.
""" """
response = self.process_response(secure=True) response = self.process_response(secure=True)
self.assertEqual(response["Strict-Transport-Security"], "max-age=600; includeSubDomains") self.assertEqual(
response.headers['Strict-Transport-Security'],
'max-age=600; includeSubDomains',
)
@override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=False) @override_settings(SECURE_HSTS_SECONDS=600, SECURE_HSTS_INCLUDE_SUBDOMAINS=False)
def test_sts_no_include_subdomains(self): def test_sts_no_include_subdomains(self):
@ -96,7 +105,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
the "includeSubDomains" directive to the response. the "includeSubDomains" directive to the response.
""" """
response = self.process_response(secure=True) response = self.process_response(secure=True)
self.assertEqual(response["Strict-Transport-Security"], "max-age=600") self.assertEqual(response.headers["Strict-Transport-Security"], "max-age=600")
@override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=True) @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=True)
def test_sts_preload(self): def test_sts_preload(self):
@ -106,7 +115,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
directive to the response. directive to the response.
""" """
response = self.process_response(secure=True) response = self.process_response(secure=True)
self.assertEqual(response["Strict-Transport-Security"], "max-age=10886400; preload") self.assertEqual(
response.headers['Strict-Transport-Security'],
'max-age=10886400; preload',
)
@override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_INCLUDE_SUBDOMAINS=True, SECURE_HSTS_PRELOAD=True) @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_INCLUDE_SUBDOMAINS=True, SECURE_HSTS_PRELOAD=True)
def test_sts_subdomains_and_preload(self): def test_sts_subdomains_and_preload(self):
@ -117,7 +129,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
to the response. to the response.
""" """
response = self.process_response(secure=True) response = self.process_response(secure=True)
self.assertEqual(response["Strict-Transport-Security"], "max-age=10886400; includeSubDomains; preload") self.assertEqual(
response.headers['Strict-Transport-Security'],
'max-age=10886400; includeSubDomains; preload',
)
@override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=False) @override_settings(SECURE_HSTS_SECONDS=10886400, SECURE_HSTS_PRELOAD=False)
def test_sts_no_preload(self): def test_sts_no_preload(self):
@ -127,7 +142,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
the "preload" directive to the response. the "preload" directive to the response.
""" """
response = self.process_response(secure=True) response = self.process_response(secure=True)
self.assertEqual(response["Strict-Transport-Security"], "max-age=10886400") self.assertEqual(
response.headers['Strict-Transport-Security'],
'max-age=10886400',
)
@override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True) @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
def test_content_type_on(self): def test_content_type_on(self):
@ -135,7 +153,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
With SECURE_CONTENT_TYPE_NOSNIFF set to True, the middleware adds With SECURE_CONTENT_TYPE_NOSNIFF set to True, the middleware adds
"X-Content-Type-Options: nosniff" header to the response. "X-Content-Type-Options: nosniff" header to the response.
""" """
self.assertEqual(self.process_response()["X-Content-Type-Options"], "nosniff") self.assertEqual(
self.process_response().headers['X-Content-Type-Options'],
'nosniff',
)
@override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True) @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=True)
def test_content_type_already_present(self): def test_content_type_already_present(self):
@ -144,7 +165,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
already present in the response. already present in the response.
""" """
response = self.process_response(secure=True, headers={"X-Content-Type-Options": "foo"}) response = self.process_response(secure=True, headers={"X-Content-Type-Options": "foo"})
self.assertEqual(response["X-Content-Type-Options"], "foo") self.assertEqual(response.headers["X-Content-Type-Options"], "foo")
@override_settings(SECURE_CONTENT_TYPE_NOSNIFF=False) @override_settings(SECURE_CONTENT_TYPE_NOSNIFF=False)
def test_content_type_off(self): def test_content_type_off(self):
@ -152,7 +173,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
With SECURE_CONTENT_TYPE_NOSNIFF False, the middleware does not add an With SECURE_CONTENT_TYPE_NOSNIFF False, the middleware does not add an
"X-Content-Type-Options" header to the response. "X-Content-Type-Options" header to the response.
""" """
self.assertNotIn("X-Content-Type-Options", self.process_response()) self.assertNotIn('X-Content-Type-Options', self.process_response().headers)
@override_settings(SECURE_BROWSER_XSS_FILTER=True) @override_settings(SECURE_BROWSER_XSS_FILTER=True)
def test_xss_filter_on(self): def test_xss_filter_on(self):
@ -160,7 +181,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
With SECURE_BROWSER_XSS_FILTER set to True, the middleware adds With SECURE_BROWSER_XSS_FILTER set to True, the middleware adds
"s-xss-protection: 1; mode=block" header to the response. "s-xss-protection: 1; mode=block" header to the response.
""" """
self.assertEqual(self.process_response()["X-XSS-Protection"], "1; mode=block") self.assertEqual(
self.process_response().headers['X-XSS-Protection'],
'1; mode=block',
)
@override_settings(SECURE_BROWSER_XSS_FILTER=True) @override_settings(SECURE_BROWSER_XSS_FILTER=True)
def test_xss_filter_already_present(self): def test_xss_filter_already_present(self):
@ -169,7 +193,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
already present in the response. already present in the response.
""" """
response = self.process_response(secure=True, headers={"X-XSS-Protection": "foo"}) response = self.process_response(secure=True, headers={"X-XSS-Protection": "foo"})
self.assertEqual(response["X-XSS-Protection"], "foo") self.assertEqual(response.headers["X-XSS-Protection"], "foo")
@override_settings(SECURE_BROWSER_XSS_FILTER=False) @override_settings(SECURE_BROWSER_XSS_FILTER=False)
def test_xss_filter_off(self): def test_xss_filter_off(self):
@ -177,7 +201,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
With SECURE_BROWSER_XSS_FILTER set to False, the middleware does not With SECURE_BROWSER_XSS_FILTER set to False, the middleware does not
add an "X-XSS-Protection" header to the response. add an "X-XSS-Protection" header to the response.
""" """
self.assertNotIn("X-XSS-Protection", self.process_response()) self.assertNotIn('X-XSS-Protection', self.process_response().headers)
@override_settings(SECURE_SSL_REDIRECT=True) @override_settings(SECURE_SSL_REDIRECT=True)
def test_ssl_redirect_on(self): def test_ssl_redirect_on(self):
@ -229,7 +253,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
With SECURE_REFERRER_POLICY set to None, the middleware does not add a With SECURE_REFERRER_POLICY set to None, the middleware does not add a
"Referrer-Policy" header to the response. "Referrer-Policy" header to the response.
""" """
self.assertNotIn('Referrer-Policy', self.process_response()) self.assertNotIn('Referrer-Policy', self.process_response().headers)
def test_referrer_policy_on(self): def test_referrer_policy_on(self):
""" """
@ -245,7 +269,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
) )
for value, expected in tests: for value, expected in tests:
with self.subTest(value=value), override_settings(SECURE_REFERRER_POLICY=value): with self.subTest(value=value), override_settings(SECURE_REFERRER_POLICY=value):
self.assertEqual(self.process_response()['Referrer-Policy'], expected) self.assertEqual(
self.process_response().headers['Referrer-Policy'],
expected,
)
@override_settings(SECURE_REFERRER_POLICY='strict-origin') @override_settings(SECURE_REFERRER_POLICY='strict-origin')
def test_referrer_policy_already_present(self): def test_referrer_policy_already_present(self):
@ -254,4 +281,4 @@ class SecurityMiddlewareTest(SimpleTestCase):
present in the response. present in the response.
""" """
response = self.process_response(headers={'Referrer-Policy': 'unsafe-url'}) response = self.process_response(headers={'Referrer-Policy': 'unsafe-url'})
self.assertEqual(response['Referrer-Policy'], 'unsafe-url') self.assertEqual(response.headers['Referrer-Policy'], 'unsafe-url')

View File

@ -292,7 +292,7 @@ class CommonMiddlewareTest(SimpleTestCase):
return response return response
response = CommonMiddleware(get_response)(self.rf.get('/')) response = CommonMiddleware(get_response)(self.rf.get('/'))
self.assertEqual(int(response['Content-Length']), len(response.content)) self.assertEqual(int(response.headers['Content-Length']), len(response.content))
def test_content_length_header_not_added_for_streaming_response(self): def test_content_length_header_not_added_for_streaming_response(self):
def get_response(req): def get_response(req):
@ -308,11 +308,11 @@ class CommonMiddlewareTest(SimpleTestCase):
def get_response(req): def get_response(req):
response = HttpResponse() response = HttpResponse()
response['Content-Length'] = bad_content_length response.headers['Content-Length'] = bad_content_length
return response return response
response = CommonMiddleware(get_response)(self.rf.get('/')) response = CommonMiddleware(get_response)(self.rf.get('/'))
self.assertEqual(int(response['Content-Length']), bad_content_length) self.assertEqual(int(response.headers['Content-Length']), bad_content_length)
# Other tests # Other tests
@ -607,7 +607,7 @@ class ConditionalGetMiddlewareTest(SimpleTestCase):
self.assertEqual(new_response.status_code, 304) self.assertEqual(new_response.status_code, 304)
base_response = get_response(self.req) base_response = get_response(self.req)
for header in ('Cache-Control', 'Content-Location', 'Date', 'ETag', 'Expires', 'Last-Modified', 'Vary'): for header in ('Cache-Control', 'Content-Location', 'Date', 'ETag', 'Expires', 'Last-Modified', 'Vary'):
self.assertEqual(new_response[header], base_response[header]) self.assertEqual(new_response.headers[header], base_response.headers[header])
self.assertEqual(new_response.cookies, base_response.cookies) self.assertEqual(new_response.cookies, base_response.cookies)
self.assertNotIn('Content-Language', new_response) self.assertNotIn('Content-Language', new_response)
@ -622,7 +622,7 @@ class ConditionalGetMiddlewareTest(SimpleTestCase):
return HttpResponse(status=200) return HttpResponse(status=200)
response = ConditionalGetMiddleware(self.get_response)(self.req) response = ConditionalGetMiddleware(self.get_response)(self.req)
etag = response['ETag'] etag = response.headers['ETag']
put_request = self.request_factory.put('/', HTTP_IF_MATCH=etag) put_request = self.request_factory.put('/', HTTP_IF_MATCH=etag)
conditional_get_response = ConditionalGetMiddleware(get_200_response)(put_request) conditional_get_response = ConditionalGetMiddleware(get_200_response)(put_request)
self.assertEqual(conditional_get_response.status_code, 200) # should never be a 412 self.assertEqual(conditional_get_response.status_code, 200) # should never be a 412
@ -653,11 +653,11 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
""" """
with override_settings(X_FRAME_OPTIONS='SAMEORIGIN'): with override_settings(X_FRAME_OPTIONS='SAMEORIGIN'):
r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest()) r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'SAMEORIGIN') self.assertEqual(r.headers['X-Frame-Options'], 'SAMEORIGIN')
with override_settings(X_FRAME_OPTIONS='sameorigin'): with override_settings(X_FRAME_OPTIONS='sameorigin'):
r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest()) r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'SAMEORIGIN') self.assertEqual(r.headers['X-Frame-Options'], 'SAMEORIGIN')
def test_deny(self): def test_deny(self):
""" """
@ -666,11 +666,11 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
""" """
with override_settings(X_FRAME_OPTIONS='DENY'): with override_settings(X_FRAME_OPTIONS='DENY'):
r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest()) r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'DENY') self.assertEqual(r.headers['X-Frame-Options'], 'DENY')
with override_settings(X_FRAME_OPTIONS='deny'): with override_settings(X_FRAME_OPTIONS='deny'):
r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest()) r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'DENY') self.assertEqual(r.headers['X-Frame-Options'], 'DENY')
def test_defaults_sameorigin(self): def test_defaults_sameorigin(self):
""" """
@ -680,7 +680,7 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
with override_settings(X_FRAME_OPTIONS=None): with override_settings(X_FRAME_OPTIONS=None):
del settings.X_FRAME_OPTIONS # restored by override_settings del settings.X_FRAME_OPTIONS # restored by override_settings
r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest()) r = XFrameOptionsMiddleware(get_response_empty)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'DENY') self.assertEqual(r.headers['X-Frame-Options'], 'DENY')
def test_dont_set_if_set(self): def test_dont_set_if_set(self):
""" """
@ -689,21 +689,21 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
""" """
def same_origin_response(request): def same_origin_response(request):
response = HttpResponse() response = HttpResponse()
response['X-Frame-Options'] = 'SAMEORIGIN' response.headers['X-Frame-Options'] = 'SAMEORIGIN'
return response return response
def deny_response(request): def deny_response(request):
response = HttpResponse() response = HttpResponse()
response['X-Frame-Options'] = 'DENY' response.headers['X-Frame-Options'] = 'DENY'
return response return response
with override_settings(X_FRAME_OPTIONS='DENY'): with override_settings(X_FRAME_OPTIONS='DENY'):
r = XFrameOptionsMiddleware(same_origin_response)(HttpRequest()) r = XFrameOptionsMiddleware(same_origin_response)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'SAMEORIGIN') self.assertEqual(r.headers['X-Frame-Options'], 'SAMEORIGIN')
with override_settings(X_FRAME_OPTIONS='SAMEORIGIN'): with override_settings(X_FRAME_OPTIONS='SAMEORIGIN'):
r = XFrameOptionsMiddleware(deny_response)(HttpRequest()) r = XFrameOptionsMiddleware(deny_response)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'DENY') self.assertEqual(r.headers['X-Frame-Options'], 'DENY')
def test_response_exempt(self): def test_response_exempt(self):
""" """
@ -722,10 +722,10 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
with override_settings(X_FRAME_OPTIONS='SAMEORIGIN'): with override_settings(X_FRAME_OPTIONS='SAMEORIGIN'):
r = XFrameOptionsMiddleware(xframe_not_exempt_response)(HttpRequest()) r = XFrameOptionsMiddleware(xframe_not_exempt_response)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'SAMEORIGIN') self.assertEqual(r.headers['X-Frame-Options'], 'SAMEORIGIN')
r = XFrameOptionsMiddleware(xframe_exempt_response)(HttpRequest()) r = XFrameOptionsMiddleware(xframe_exempt_response)(HttpRequest())
self.assertIsNone(r.get('X-Frame-Options')) self.assertIsNone(r.headers.get('X-Frame-Options'))
def test_is_extendable(self): def test_is_extendable(self):
""" """
@ -749,16 +749,16 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
with override_settings(X_FRAME_OPTIONS='DENY'): with override_settings(X_FRAME_OPTIONS='DENY'):
r = OtherXFrameOptionsMiddleware(same_origin_response)(HttpRequest()) r = OtherXFrameOptionsMiddleware(same_origin_response)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'SAMEORIGIN') self.assertEqual(r.headers['X-Frame-Options'], 'SAMEORIGIN')
request = HttpRequest() request = HttpRequest()
request.sameorigin = True request.sameorigin = True
r = OtherXFrameOptionsMiddleware(get_response_empty)(request) r = OtherXFrameOptionsMiddleware(get_response_empty)(request)
self.assertEqual(r['X-Frame-Options'], 'SAMEORIGIN') self.assertEqual(r.headers['X-Frame-Options'], 'SAMEORIGIN')
with override_settings(X_FRAME_OPTIONS='SAMEORIGIN'): with override_settings(X_FRAME_OPTIONS='SAMEORIGIN'):
r = OtherXFrameOptionsMiddleware(get_response_empty)(HttpRequest()) r = OtherXFrameOptionsMiddleware(get_response_empty)(HttpRequest())
self.assertEqual(r['X-Frame-Options'], 'DENY') self.assertEqual(r.headers['X-Frame-Options'], 'DENY')
class GZipMiddlewareTest(SimpleTestCase): class GZipMiddlewareTest(SimpleTestCase):
@ -916,12 +916,12 @@ class ETagGZipMiddlewareTest(SimpleTestCase):
""" """
def get_response(req): def get_response(req):
response = HttpResponse(self.compressible_string) response = HttpResponse(self.compressible_string)
response['ETag'] = '"eggs"' response.headers['ETag'] = '"eggs"'
return response return response
request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate') request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
gzip_response = GZipMiddleware(get_response)(request) gzip_response = GZipMiddleware(get_response)(request)
self.assertEqual(gzip_response['ETag'], 'W/"eggs"') self.assertEqual(gzip_response.headers['ETag'], 'W/"eggs"')
def test_weak_etag_not_modified(self): def test_weak_etag_not_modified(self):
""" """
@ -929,12 +929,12 @@ class ETagGZipMiddlewareTest(SimpleTestCase):
""" """
def get_response(req): def get_response(req):
response = HttpResponse(self.compressible_string) response = HttpResponse(self.compressible_string)
response['ETag'] = 'W/"eggs"' response.headers['ETag'] = 'W/"eggs"'
return response return response
request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate') request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
gzip_response = GZipMiddleware(get_response)(request) gzip_response = GZipMiddleware(get_response)(request)
self.assertEqual(gzip_response['ETag'], 'W/"eggs"') self.assertEqual(gzip_response.headers['ETag'], 'W/"eggs"')
def test_etag_match(self): def test_etag_match(self):
""" """
@ -949,7 +949,7 @@ class ETagGZipMiddlewareTest(SimpleTestCase):
request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate') request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
response = GZipMiddleware(get_cond_response)(request) response = GZipMiddleware(get_cond_response)(request)
gzip_etag = response['ETag'] gzip_etag = response.headers['ETag']
next_request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate', HTTP_IF_NONE_MATCH=gzip_etag) next_request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate', HTTP_IF_NONE_MATCH=gzip_etag)
next_response = ConditionalGetMiddleware(get_response)(next_request) next_response = ConditionalGetMiddleware(get_response)(next_request)
self.assertEqual(next_response.status_code, 304) self.assertEqual(next_response.status_code, 304)

View File

@ -12,23 +12,26 @@ from django.test import SimpleTestCase
class FileResponseTests(SimpleTestCase): class FileResponseTests(SimpleTestCase):
def test_file_from_disk_response(self): def test_file_from_disk_response(self):
response = FileResponse(open(__file__, 'rb')) response = FileResponse(open(__file__, 'rb'))
self.assertEqual(response['Content-Length'], str(os.path.getsize(__file__))) self.assertEqual(response.headers['Content-Length'], str(os.path.getsize(__file__)))
self.assertIn(response['Content-Type'], ['text/x-python', 'text/plain']) self.assertIn(response.headers['Content-Type'], ['text/x-python', 'text/plain'])
self.assertEqual(response['Content-Disposition'], 'inline; filename="test_fileresponse.py"') self.assertEqual(
response.headers['Content-Disposition'],
'inline; filename="test_fileresponse.py"',
)
response.close() response.close()
def test_file_from_buffer_response(self): def test_file_from_buffer_response(self):
response = FileResponse(io.BytesIO(b'binary content')) response = FileResponse(io.BytesIO(b'binary content'))
self.assertEqual(response['Content-Length'], '14') self.assertEqual(response.headers['Content-Length'], '14')
self.assertEqual(response['Content-Type'], 'application/octet-stream') self.assertEqual(response.headers['Content-Type'], 'application/octet-stream')
self.assertFalse(response.has_header('Content-Disposition')) self.assertFalse(response.has_header('Content-Disposition'))
self.assertEqual(list(response), [b'binary content']) self.assertEqual(list(response), [b'binary content'])
def test_file_from_buffer_unnamed_attachment(self): def test_file_from_buffer_unnamed_attachment(self):
response = FileResponse(io.BytesIO(b'binary content'), as_attachment=True) response = FileResponse(io.BytesIO(b'binary content'), as_attachment=True)
self.assertEqual(response['Content-Length'], '14') self.assertEqual(response.headers['Content-Length'], '14')
self.assertEqual(response['Content-Type'], 'application/octet-stream') self.assertEqual(response.headers['Content-Type'], 'application/octet-stream')
self.assertEqual(response['Content-Disposition'], 'attachment') self.assertEqual(response.headers['Content-Disposition'], 'attachment')
self.assertEqual(list(response), [b'binary content']) self.assertEqual(list(response), [b'binary content'])
@skipIf(sys.platform == 'win32', "Named pipes are Unix-only.") @skipIf(sys.platform == 'win32', "Named pipes are Unix-only.")
@ -47,9 +50,12 @@ class FileResponseTests(SimpleTestCase):
def test_file_from_disk_as_attachment(self): def test_file_from_disk_as_attachment(self):
response = FileResponse(open(__file__, 'rb'), as_attachment=True) response = FileResponse(open(__file__, 'rb'), as_attachment=True)
self.assertEqual(response['Content-Length'], str(os.path.getsize(__file__))) self.assertEqual(response.headers['Content-Length'], str(os.path.getsize(__file__)))
self.assertIn(response['Content-Type'], ['text/x-python', 'text/plain']) self.assertIn(response.headers['Content-Type'], ['text/x-python', 'text/plain'])
self.assertEqual(response['Content-Disposition'], 'attachment; filename="test_fileresponse.py"') self.assertEqual(
response.headers['Content-Disposition'],
'attachment; filename="test_fileresponse.py"',
)
response.close() response.close()
def test_compressed_response(self): def test_compressed_response(self):
@ -67,7 +73,7 @@ class FileResponseTests(SimpleTestCase):
with self.subTest(ext=extension): with self.subTest(ext=extension):
with tempfile.NamedTemporaryFile(suffix=extension) as tmp: with tempfile.NamedTemporaryFile(suffix=extension) as tmp:
response = FileResponse(tmp) response = FileResponse(tmp)
self.assertEqual(response['Content-Type'], mimetype) self.assertEqual(response.headers['Content-Type'], mimetype)
self.assertFalse(response.has_header('Content-Encoding')) self.assertFalse(response.has_header('Content-Encoding'))
def test_unicode_attachment(self): def test_unicode_attachment(self):
@ -75,8 +81,11 @@ class FileResponseTests(SimpleTestCase):
ContentFile(b'binary content', name="祝您平安.odt"), as_attachment=True, ContentFile(b'binary content', name="祝您平安.odt"), as_attachment=True,
content_type='application/vnd.oasis.opendocument.text', content_type='application/vnd.oasis.opendocument.text',
) )
self.assertEqual(response['Content-Type'], 'application/vnd.oasis.opendocument.text')
self.assertEqual( self.assertEqual(
response['Content-Disposition'], response.headers['Content-Type'],
'application/vnd.oasis.opendocument.text',
)
self.assertEqual(
response.headers['Content-Disposition'],
"attachment; filename*=utf-8''%E7%A5%9D%E6%82%A8%E5%B9%B3%E5%AE%89.odt" "attachment; filename*=utf-8''%E7%A5%9D%E6%82%A8%E5%B9%B3%E5%AE%89.odt"
) )

View File

@ -39,12 +39,12 @@ class HttpResponseBaseTests(SimpleTestCase):
""" """
r = HttpResponseBase() r = HttpResponseBase()
r['Header'] = 'Value' r.headers['Header'] = 'Value'
r.setdefault('header', 'changed') r.setdefault('header', 'changed')
self.assertEqual(r['header'], 'Value') self.assertEqual(r.headers['header'], 'Value')
r.setdefault('x-header', 'DefaultValue') r.setdefault('x-header', 'DefaultValue')
self.assertEqual(r['X-Header'], 'DefaultValue') self.assertEqual(r.headers['X-Header'], 'DefaultValue')
class HttpResponseTests(SimpleTestCase): class HttpResponseTests(SimpleTestCase):
@ -92,7 +92,7 @@ class HttpResponseTests(SimpleTestCase):
response = HttpResponse(charset=ISO88591) response = HttpResponse(charset=ISO88591)
self.assertEqual(response.charset, ISO88591) self.assertEqual(response.charset, ISO88591)
self.assertEqual(response['Content-Type'], 'text/html; charset=%s' % ISO88591) self.assertEqual(response.headers['Content-Type'], 'text/html; charset=%s' % ISO88591)
response = HttpResponse(content_type='text/plain; charset=%s' % UTF8, charset=ISO88591) response = HttpResponse(content_type='text/plain; charset=%s' % UTF8, charset=ISO88591)
self.assertEqual(response.charset, ISO88591) self.assertEqual(response.charset, ISO88591)
@ -134,7 +134,7 @@ class HttpResponseTests(SimpleTestCase):
def test_repr_no_content_type(self): def test_repr_no_content_type(self):
response = HttpResponse(status=204) response = HttpResponse(status=204)
del response['Content-Type'] del response.headers['Content-Type']
self.assertEqual(repr(response), '<HttpResponse status_code=204>') self.assertEqual(repr(response), '<HttpResponse status_code=204>')
def test_wrap_textiowrapper(self): def test_wrap_textiowrapper(self):

View File

@ -781,7 +781,7 @@ class SessionMiddlewareTests(TestCase):
) )
# SessionMiddleware sets 'Vary: Cookie' to prevent the 'Set-Cookie' # SessionMiddleware sets 'Vary: Cookie' to prevent the 'Set-Cookie'
# from being cached. # from being cached.
self.assertEqual(response['Vary'], 'Cookie') self.assertEqual(response.headers['Vary'], 'Cookie')
@override_settings(SESSION_COOKIE_DOMAIN='.example.local', SESSION_COOKIE_PATH='/example/') @override_settings(SESSION_COOKIE_DOMAIN='.example.local', SESSION_COOKIE_PATH='/example/')
def test_session_delete_on_end_with_custom_domain_and_path(self): def test_session_delete_on_end_with_custom_domain_and_path(self):
@ -826,7 +826,7 @@ class SessionMiddlewareTests(TestCase):
# A cookie should not be set. # A cookie should not be set.
self.assertEqual(response.cookies, {}) self.assertEqual(response.cookies, {})
# The session is accessed so "Vary: Cookie" should be set. # The session is accessed so "Vary: Cookie" should be set.
self.assertEqual(response['Vary'], 'Cookie') self.assertEqual(response.headers['Vary'], 'Cookie')
def test_empty_session_saved(self): def test_empty_session_saved(self):
""" """
@ -849,7 +849,7 @@ class SessionMiddlewareTests(TestCase):
'Set-Cookie: sessionid=%s' % request.session.session_key, 'Set-Cookie: sessionid=%s' % request.session.session_key,
str(response.cookies) str(response.cookies)
) )
self.assertEqual(response['Vary'], 'Cookie') self.assertEqual(response.headers['Vary'], 'Cookie')
# Empty the session data. # Empty the session data.
del request.session['foo'] del request.session['foo']
@ -866,7 +866,7 @@ class SessionMiddlewareTests(TestCase):
'Set-Cookie: sessionid=%s' % request.session.session_key, 'Set-Cookie: sessionid=%s' % request.session.session_key,
str(response.cookies) str(response.cookies)
) )
self.assertEqual(response['Vary'], 'Cookie') self.assertEqual(response.headers['Vary'], 'Cookie')
class CookieSessionTests(SessionTestsMixin, SimpleTestCase): class CookieSessionTests(SessionTestsMixin, SimpleTestCase):

View File

@ -9,7 +9,7 @@ class RenderTests(SimpleTestCase):
response = self.client.get('/render/') response = self.client.get('/render/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b'FOO.BAR../render/\n') self.assertEqual(response.content, b'FOO.BAR../render/\n')
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response.headers['Content-Type'], 'text/html; charset=utf-8')
self.assertFalse(hasattr(response.context.request, 'current_app')) self.assertFalse(hasattr(response.context.request, 'current_app'))
def test_render_with_multiple_templates(self): def test_render_with_multiple_templates(self):
@ -21,7 +21,7 @@ class RenderTests(SimpleTestCase):
response = self.client.get('/render/content_type/') response = self.client.get('/render/content_type/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b'FOO.BAR../render/content_type/\n') self.assertEqual(response.content, b'FOO.BAR../render/content_type/\n')
self.assertEqual(response['Content-Type'], 'application/x-rendertest') self.assertEqual(response.headers['Content-Type'], 'application/x-rendertest')
def test_render_with_status(self): def test_render_with_status(self):
response = self.client.get('/render/status/') response = self.client.get('/render/status/')

View File

@ -56,4 +56,4 @@ class GenericViewsSitemapTests(SitemapTestsBase):
</urlset> </urlset>
""" % (self.base_url, test_model.pk) """ % (self.base_url, test_model.pk)
self.assertXMLEqual(response.content.decode(), expected_content) self.assertXMLEqual(response.content.decode(), expected_content)
self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 10:00:00 GMT') self.assertEqual(response.headers['Last-Modified'], 'Wed, 13 Mar 2013 10:00:00 GMT')

View File

@ -116,14 +116,14 @@ class HTTPSitemapTests(SitemapTestsBase):
def test_sitemap_last_modified(self): def test_sitemap_last_modified(self):
"Last-Modified header is set correctly" "Last-Modified header is set correctly"
response = self.client.get('/lastmod/sitemap.xml') response = self.client.get('/lastmod/sitemap.xml')
self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 10:00:00 GMT') self.assertEqual(response.headers['Last-Modified'], 'Wed, 13 Mar 2013 10:00:00 GMT')
def test_sitemap_last_modified_date(self): def test_sitemap_last_modified_date(self):
""" """
The Last-Modified header should be support dates (without time). The Last-Modified header should be support dates (without time).
""" """
response = self.client.get('/lastmod/date-sitemap.xml') response = self.client.get('/lastmod/date-sitemap.xml')
self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 00:00:00 GMT') self.assertEqual(response.headers['Last-Modified'], 'Wed, 13 Mar 2013 00:00:00 GMT')
def test_sitemap_last_modified_tz(self): def test_sitemap_last_modified_tz(self):
""" """
@ -131,7 +131,7 @@ class HTTPSitemapTests(SitemapTestsBase):
to GMT. to GMT.
""" """
response = self.client.get('/lastmod/tz-sitemap.xml') response = self.client.get('/lastmod/tz-sitemap.xml')
self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 15:00:00 GMT') self.assertEqual(response.headers['Last-Modified'], 'Wed, 13 Mar 2013 15:00:00 GMT')
def test_sitemap_last_modified_missing(self): def test_sitemap_last_modified_missing(self):
"Last-Modified header is missing when sitemap has no lastmod" "Last-Modified header is missing when sitemap has no lastmod"
@ -165,7 +165,7 @@ class HTTPSitemapTests(SitemapTestsBase):
Test sitemaps are sorted by lastmod in ascending order. Test sitemaps are sorted by lastmod in ascending order.
""" """
response = self.client.get('/lastmod-sitemaps/ascending.xml') response = self.client.get('/lastmod-sitemaps/ascending.xml')
self.assertEqual(response['Last-Modified'], 'Sat, 20 Apr 2013 05:00:00 GMT') self.assertEqual(response.headers['Last-Modified'], 'Sat, 20 Apr 2013 05:00:00 GMT')
def test_sitemaps_lastmod_descending(self): def test_sitemaps_lastmod_descending(self):
""" """
@ -173,7 +173,7 @@ class HTTPSitemapTests(SitemapTestsBase):
Test sitemaps are sorted by lastmod in descending order. Test sitemaps are sorted by lastmod in descending order.
""" """
response = self.client.get('/lastmod-sitemaps/descending.xml') response = self.client.get('/lastmod-sitemaps/descending.xml')
self.assertEqual(response['Last-Modified'], 'Sat, 20 Apr 2013 05:00:00 GMT') self.assertEqual(response.headers['Last-Modified'], 'Sat, 20 Apr 2013 05:00:00 GMT')
@override_settings(USE_I18N=True, USE_L10N=True) @override_settings(USE_I18N=True, USE_L10N=True)
def test_localized_priority(self): def test_localized_priority(self):
@ -243,10 +243,10 @@ class HTTPSitemapTests(SitemapTestsBase):
def test_x_robots_sitemap(self): def test_x_robots_sitemap(self):
response = self.client.get('/simple/index.xml') response = self.client.get('/simple/index.xml')
self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive') self.assertEqual(response.headers['X-Robots-Tag'], 'noindex, noodp, noarchive')
response = self.client.get('/simple/sitemap.xml') response = self.client.get('/simple/sitemap.xml')
self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive') self.assertEqual(response.headers['X-Robots-Tag'], 'noindex, noodp, noarchive')
def test_empty_sitemap(self): def test_empty_sitemap(self):
response = self.client.get('/empty/sitemap.xml') response = self.client.get('/empty/sitemap.xml')

View File

@ -421,14 +421,14 @@ class SyndicationFeedTest(FeedTestCase):
Tests the Last-Modified header with naive publication dates. Tests the Last-Modified header with naive publication dates.
""" """
response = self.client.get('/syndication/naive-dates/') response = self.client.get('/syndication/naive-dates/')
self.assertEqual(response['Last-Modified'], 'Tue, 26 Mar 2013 01:00:00 GMT') self.assertEqual(response.headers['Last-Modified'], 'Tue, 26 Mar 2013 01:00:00 GMT')
def test_feed_last_modified_time(self): def test_feed_last_modified_time(self):
""" """
Tests the Last-Modified header with aware publication dates. Tests the Last-Modified header with aware publication dates.
""" """
response = self.client.get('/syndication/aware-dates/') response = self.client.get('/syndication/aware-dates/')
self.assertEqual(response['Last-Modified'], 'Mon, 25 Mar 2013 19:18:00 GMT') self.assertEqual(response.headers['Last-Modified'], 'Mon, 25 Mar 2013 19:18:00 GMT')
# No last-modified when feed has no item_pubdate # No last-modified when feed has no item_pubdate
response = self.client.get('/syndication/no_pubdate/') response = self.client.get('/syndication/no_pubdate/')

View File

@ -122,13 +122,13 @@ class SimpleTemplateResponseTest(SimpleTestCase):
def test_kwargs(self): def test_kwargs(self):
response = self._response(content_type='application/json', status=504, charset='ascii') response = self._response(content_type='application/json', status=504, charset='ascii')
self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.headers['content-type'], 'application/json')
self.assertEqual(response.status_code, 504) self.assertEqual(response.status_code, 504)
self.assertEqual(response.charset, 'ascii') self.assertEqual(response.charset, 'ascii')
def test_args(self): def test_args(self):
response = SimpleTemplateResponse('', {}, 'application/json', 504) response = SimpleTemplateResponse('', {}, 'application/json', 504)
self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.headers['content-type'], 'application/json')
self.assertEqual(response.status_code, 504) self.assertEqual(response.status_code, 504)
@require_jinja2 @require_jinja2
@ -175,7 +175,7 @@ class SimpleTemplateResponseTest(SimpleTestCase):
unpickled_response = pickle.loads(pickled_response) unpickled_response = pickle.loads(pickled_response)
self.assertEqual(unpickled_response.content, response.content) self.assertEqual(unpickled_response.content, response.content)
self.assertEqual(unpickled_response['content-type'], response['content-type']) self.assertEqual(unpickled_response.headers['content-type'], response.headers['content-type'])
self.assertEqual(unpickled_response.status_code, response.status_code) self.assertEqual(unpickled_response.status_code, response.status_code)
# ...and the unpickled response doesn't have the # ...and the unpickled response doesn't have the
@ -249,13 +249,13 @@ class TemplateResponseTest(SimpleTestCase):
def test_kwargs(self): def test_kwargs(self):
response = self._response(content_type='application/json', status=504) response = self._response(content_type='application/json', status=504)
self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.headers['content-type'], 'application/json')
self.assertEqual(response.status_code, 504) self.assertEqual(response.status_code, 504)
def test_args(self): def test_args(self):
response = TemplateResponse(self.factory.get('/'), '', {}, response = TemplateResponse(self.factory.get('/'), '', {},
'application/json', 504) 'application/json', 504)
self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.headers['content-type'], 'application/json')
self.assertEqual(response.status_code, 504) self.assertEqual(response.status_code, 504)
@require_jinja2 @require_jinja2
@ -287,7 +287,7 @@ class TemplateResponseTest(SimpleTestCase):
unpickled_response = pickle.loads(pickled_response) unpickled_response = pickle.loads(pickled_response)
self.assertEqual(unpickled_response.content, response.content) self.assertEqual(unpickled_response.content, response.content)
self.assertEqual(unpickled_response['content-type'], response['content-type']) self.assertEqual(unpickled_response.headers['content-type'], response.headers['content-type'])
self.assertEqual(unpickled_response.status_code, response.status_code) self.assertEqual(unpickled_response.status_code, response.status_code)
# ...and the unpickled response doesn't have the # ...and the unpickled response doesn't have the

View File

@ -159,7 +159,7 @@ class ClientTest(TestCase):
"Check the value of HTTP headers returned in a response" "Check the value of HTTP headers returned in a response"
response = self.client.get("/header_view/") response = self.client.get("/header_view/")
self.assertEqual(response['X-DJANGO-TEST'], 'Slartibartfast') self.assertEqual(response.headers['X-DJANGO-TEST'], 'Slartibartfast')
def test_response_attached_request(self): def test_response_attached_request(self):
""" """

View File

@ -102,7 +102,7 @@ def json_view(request):
def view_with_header(request): def view_with_header(request):
"A view that has a custom header" "A view that has a custom header"
response = HttpResponse() response = HttpResponse()
response['X-DJANGO-TEST'] = 'Slartibartfast' response.headers['X-DJANGO-TEST'] = 'Slartibartfast'
return response return response

View File

@ -1210,7 +1210,7 @@ class RequestMethodStringDataTests(SimpleTestCase):
) )
for content_type in valid_types: for content_type in valid_types:
response = self.client.get('/json_response/', {'content_type': content_type}) response = self.client.get('/json_response/', {'content_type': content_type})
self.assertEqual(response['Content-Type'], content_type) self.assertEqual(response.headers['Content-Type'], content_type)
self.assertEqual(response.json(), {'key': 'value'}) self.assertEqual(response.json(), {'key': 'value'})
def test_json_multiple_access(self): def test_json_multiple_access(self):

View File

@ -1481,7 +1481,7 @@ class NonHTMLResponseExceptionReporterFilter(ExceptionReportTestMixin, LoggingCa
@override_settings(DEBUG=True, ROOT_URLCONF='view_tests.urls') @override_settings(DEBUG=True, ROOT_URLCONF='view_tests.urls')
def test_non_html_response_encoding(self): def test_non_html_response_encoding(self):
response = self.client.get('/raises500/', HTTP_ACCEPT='application/json') response = self.client.get('/raises500/', HTTP_ACCEPT='application/json')
self.assertEqual(response['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(response.headers['Content-Type'], 'text/plain; charset=utf-8')
class DecoratorsTests(SimpleTestCase): class DecoratorsTests(SimpleTestCase):

View File

@ -249,7 +249,7 @@ class I18NViewTests(SimpleTestCase):
catalog = gettext.translation('djangojs', locale_dir, [lang_code]) catalog = gettext.translation('djangojs', locale_dir, [lang_code])
trans_txt = catalog.gettext('this is to be translated') trans_txt = catalog.gettext('this is to be translated')
response = self.client.get('/jsi18n/') response = self.client.get('/jsi18n/')
self.assertEqual(response['Content-Type'], 'text/javascript; charset="utf-8"') self.assertEqual(response.headers['Content-Type'], 'text/javascript; charset="utf-8"')
# response content must include a line like: # response content must include a line like:
# "this is to be translated": <value of trans_txt Python variable> # "this is to be translated": <value of trans_txt Python variable>
# json.dumps() is used to be able to check Unicode strings. # json.dumps() is used to be able to check Unicode strings.

View File

@ -10,7 +10,7 @@ class JsonResponseTests(SimpleTestCase):
response = self.client.get('/json/response/') response = self.client.get('/json/response/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual( self.assertEqual(
response['content-type'], 'application/json') response.headers['content-type'], 'application/json')
self.assertEqual(json.loads(response.content.decode()), { self.assertEqual(json.loads(response.content.decode()), {
'a': [1, 2, 3], 'a': [1, 2, 3],
'foo': {'bar': 'baz'}, 'foo': {'bar': 'baz'},

View File

@ -29,7 +29,7 @@ class StaticTests(SimpleTestCase):
file_path = path.join(media_dir, filename) file_path = path.join(media_dir, filename)
with open(file_path, 'rb') as fp: with open(file_path, 'rb') as fp:
self.assertEqual(fp.read(), response_content) self.assertEqual(fp.read(), response_content)
self.assertEqual(len(response_content), int(response['Content-Length'])) self.assertEqual(len(response_content), int(response.headers['Content-Length']))
self.assertEqual(mimetypes.guess_type(file_path)[1], response.get('Content-Encoding', None)) self.assertEqual(mimetypes.guess_type(file_path)[1], response.get('Content-Encoding', None))
def test_chunked(self): def test_chunked(self):
@ -44,7 +44,7 @@ class StaticTests(SimpleTestCase):
def test_unknown_mime_type(self): def test_unknown_mime_type(self):
response = self.client.get('/%s/file.unknown' % self.prefix) response = self.client.get('/%s/file.unknown' % self.prefix)
self.assertEqual('application/octet-stream', response['Content-Type']) self.assertEqual('application/octet-stream', response.headers['Content-Type'])
response.close() response.close()
def test_copes_with_empty_path_component(self): def test_copes_with_empty_path_component(self):
@ -87,7 +87,7 @@ class StaticTests(SimpleTestCase):
response_content = b''.join(response) response_content = b''.join(response)
with open(path.join(media_dir, file_name), 'rb') as fp: with open(path.join(media_dir, file_name), 'rb') as fp:
self.assertEqual(fp.read(), response_content) self.assertEqual(fp.read(), response_content)
self.assertEqual(len(response_content), int(response['Content-Length'])) self.assertEqual(len(response_content), int(response.headers['Content-Length']))
def test_invalid_if_modified_since2(self): def test_invalid_if_modified_since2(self):
"""Handle even more bogus If-Modified-Since values gracefully """Handle even more bogus If-Modified-Since values gracefully
@ -102,7 +102,7 @@ class StaticTests(SimpleTestCase):
response_content = b''.join(response) response_content = b''.join(response)
with open(path.join(media_dir, file_name), 'rb') as fp: with open(path.join(media_dir, file_name), 'rb') as fp:
self.assertEqual(fp.read(), response_content) self.assertEqual(fp.read(), response_content)
self.assertEqual(len(response_content), int(response['Content-Length'])) self.assertEqual(len(response_content), int(response.headers['Content-Length']))
def test_404(self): def test_404(self):
response = self.client.get('/%s/nonexistent_resource' % self.prefix) response = self.client.get('/%s/nonexistent_resource' % self.prefix)