Fixed #31789 -- Added a new headers interface to HttpResponse.
This commit is contained in:
parent
71ae1ab012
commit
bcc2befd0e
|
@ -10,7 +10,7 @@ from django.utils.translation import gettext as _
|
|||
class CSPMiddleware(MiddlewareMixin):
|
||||
"""The admin's JavaScript should be compatible with CSP."""
|
||||
def process_response(self, request, response):
|
||||
response['Content-Security-Policy'] = "default-src 'self'"
|
||||
response.headers['Content-Security-Policy'] = "default-src 'self'"
|
||||
return response
|
||||
|
||||
|
||||
|
|
|
@ -24,5 +24,5 @@ class XViewMiddleware(MiddlewareMixin):
|
|||
if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or
|
||||
(request.user.is_active and request.user.is_staff)):
|
||||
response = HttpResponse()
|
||||
response['X-View'] = get_view_name(view_func)
|
||||
response.headers['X-View'] = get_view_name(view_func)
|
||||
return response
|
||||
|
|
|
@ -14,7 +14,7 @@ def x_robots_tag(func):
|
|||
@wraps(func)
|
||||
def inner(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 inner
|
||||
|
||||
|
@ -88,5 +88,5 @@ def sitemap(request, sitemaps, section=None,
|
|||
if all_sites_lastmod and lastmod is not None:
|
||||
# if lastmod is defined for all sites, set header so as
|
||||
# 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
|
||||
|
|
|
@ -42,7 +42,7 @@ class Feed:
|
|||
if hasattr(self, 'item_pubdate') or hasattr(self, 'item_updateddate'):
|
||||
# if item_pubdate or item_updateddate is defined for the feed, set
|
||||
# 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()))
|
||||
feedgen.write(response, 'utf-8')
|
||||
return response
|
||||
|
|
|
@ -5,6 +5,7 @@ import os
|
|||
import re
|
||||
import sys
|
||||
import time
|
||||
from collections.abc import Mapping
|
||||
from email.header import Header
|
||||
from http.client import responses
|
||||
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.http.cookie import SimpleCookie
|
||||
from django.utils import timezone
|
||||
from django.utils.datastructures import CaseInsensitiveMapping
|
||||
from django.utils.encoding import iri_to_uri
|
||||
from django.utils.http import http_date
|
||||
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)
|
||||
|
||||
|
||||
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):
|
||||
pass
|
||||
|
||||
|
@ -37,10 +98,7 @@ class HttpResponseBase:
|
|||
status_code = 200
|
||||
|
||||
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
|
||||
# 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.headers = ResponseHeaders({})
|
||||
self._resource_closers = []
|
||||
# This parameter is set by the handler. It's necessary to preserve the
|
||||
# historical behavior of request_finished.
|
||||
|
@ -95,7 +153,7 @@ class HttpResponseBase:
|
|||
|
||||
headers = [
|
||||
(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)
|
||||
|
||||
|
@ -103,57 +161,28 @@ class HttpResponseBase:
|
|||
|
||||
@property
|
||||
def _content_type_for_repr(self):
|
||||
return ', "%s"' % self['Content-Type'] if 'Content-Type' in self 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
|
||||
return ', "%s"' % self.headers['Content-Type'] if 'Content-Type' in self.headers else ''
|
||||
|
||||
def __setitem__(self, header, value):
|
||||
header = self._convert_to_charset(header, 'ascii')
|
||||
value = self._convert_to_charset(value, 'latin-1', mime_encode=True)
|
||||
self._headers[header.lower()] = (header, value)
|
||||
self.headers[header] = value
|
||||
|
||||
def __delitem__(self, header):
|
||||
self._headers.pop(header.lower(), False)
|
||||
del self.headers[header]
|
||||
|
||||
def __getitem__(self, header):
|
||||
return self._headers[header.lower()][1]
|
||||
return self.headers[header]
|
||||
|
||||
def has_header(self, header):
|
||||
"""Case-insensitive check for a header."""
|
||||
return header.lower() in self._headers
|
||||
return header in self.headers
|
||||
|
||||
__contains__ = has_header
|
||||
|
||||
def items(self):
|
||||
return self._headers.values()
|
||||
return self.headers.items()
|
||||
|
||||
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='/',
|
||||
domain=None, secure=False, httponly=False, samesite=None):
|
||||
|
@ -203,8 +232,7 @@ class HttpResponseBase:
|
|||
|
||||
def setdefault(self, key, value):
|
||||
"""Set a header unless it has already been set."""
|
||||
if key not in self:
|
||||
self[key] = value
|
||||
self.headers.setdefault(key, value)
|
||||
|
||||
def set_signed_cookie(self, key, value, salt='', **kwargs):
|
||||
value = signing.get_cookie_signer(salt=key + salt).sign(value)
|
||||
|
@ -430,19 +458,19 @@ class FileResponse(StreamingHttpResponse):
|
|||
filename = getattr(filelike, 'name', None)
|
||||
filename = filename if (isinstance(filename, str) and filename) else self.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'):
|
||||
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:
|
||||
content_type, encoding = mimetypes.guess_type(filename)
|
||||
# Encoding isn't set to prevent browsers from automatically
|
||||
# uncompressing files.
|
||||
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:
|
||||
self['Content-Type'] = 'application/octet-stream'
|
||||
self.headers['Content-Type'] = 'application/octet-stream'
|
||||
|
||||
filename = self.filename or os.path.basename(filename)
|
||||
if filename:
|
||||
|
@ -452,9 +480,9 @@ class FileResponse(StreamingHttpResponse):
|
|||
file_expr = 'filename="{}"'.format(filename)
|
||||
except UnicodeEncodeError:
|
||||
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:
|
||||
self['Content-Disposition'] = 'attachment'
|
||||
self.headers['Content-Disposition'] = 'attachment'
|
||||
|
||||
|
||||
class HttpResponseRedirectBase(HttpResponse):
|
||||
|
|
|
@ -30,8 +30,10 @@ class XFrameOptionsMiddleware(MiddlewareMixin):
|
|||
if getattr(response, 'xframe_options_exempt', False):
|
||||
return response
|
||||
|
||||
response['X-Frame-Options'] = self.get_xframe_options_value(request,
|
||||
response)
|
||||
response.headers['X-Frame-Options'] = self.get_xframe_options_value(
|
||||
request,
|
||||
response,
|
||||
)
|
||||
return response
|
||||
|
||||
def get_xframe_options_value(self, request, response):
|
||||
|
|
|
@ -110,7 +110,7 @@ class CommonMiddleware(MiddlewareMixin):
|
|||
# Add the Content-Length header to non-streaming responses if not
|
||||
# already set.
|
||||
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
|
||||
|
||||
|
|
|
@ -31,21 +31,21 @@ class GZipMiddleware(MiddlewareMixin):
|
|||
# Delete the `Content-Length` header for streaming content, because
|
||||
# we won't know the compressed size until we stream it.
|
||||
response.streaming_content = compress_sequence(response.streaming_content)
|
||||
del response['Content-Length']
|
||||
del response.headers['Content-Length']
|
||||
else:
|
||||
# Return the compressed content only if it's actually shorter.
|
||||
compressed_content = compress_string(response.content)
|
||||
if len(compressed_content) >= len(response.content):
|
||||
return response
|
||||
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
|
||||
# of RFC 7232 section-2.1 while also allowing conditional request
|
||||
# matches on ETags.
|
||||
etag = response.get('ETag')
|
||||
if etag and etag.startswith('"'):
|
||||
response['ETag'] = 'W/' + etag
|
||||
response['Content-Encoding'] = 'gzip'
|
||||
response.headers['ETag'] = 'W/' + etag
|
||||
response.headers['Content-Encoding'] = 'gzip'
|
||||
|
||||
return response
|
||||
|
|
|
@ -57,5 +57,5 @@ class LocaleMiddleware(MiddlewareMixin):
|
|||
|
||||
if not (i18n_patterns_used and language_from_path):
|
||||
patch_vary_headers(response, ('Accept-Language',))
|
||||
response.setdefault('Content-Language', language)
|
||||
response.headers.setdefault('Content-Language', language)
|
||||
return response
|
||||
|
|
|
@ -38,18 +38,18 @@ class SecurityMiddleware(MiddlewareMixin):
|
|||
sts_header = sts_header + "; includeSubDomains"
|
||||
if self.sts_preload:
|
||||
sts_header = sts_header + "; preload"
|
||||
response['Strict-Transport-Security'] = sts_header
|
||||
response.headers['Strict-Transport-Security'] = sts_header
|
||||
|
||||
if self.content_type_nosniff:
|
||||
response.setdefault('X-Content-Type-Options', 'nosniff')
|
||||
response.headers.setdefault('X-Content-Type-Options', 'nosniff')
|
||||
|
||||
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:
|
||||
# Support a comma-separated string or iterable of values to allow
|
||||
# fallback.
|
||||
response.setdefault('Referrer-Policy', ','.join(
|
||||
response.headers.setdefault('Referrer-Policy', ','.join(
|
||||
[v.strip() for v in self.referrer_policy.split(',')]
|
||||
if isinstance(self.referrer_policy, str) else self.referrer_policy
|
||||
))
|
||||
|
|
|
@ -62,7 +62,7 @@ def patch_cache_control(response, **kwargs):
|
|||
|
||||
cc = defaultdict(set)
|
||||
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)
|
||||
if directive == 'no-cache':
|
||||
# no-cache supports multiple field names.
|
||||
|
@ -100,7 +100,7 @@ def patch_cache_control(response, **kwargs):
|
|||
else:
|
||||
directives.append(dictvalue(directive, values))
|
||||
cc = ', '.join(directives)
|
||||
response['Cache-Control'] = cc
|
||||
response.headers['Cache-Control'] = cc
|
||||
|
||||
|
||||
def get_max_age(response):
|
||||
|
@ -110,7 +110,7 @@ def get_max_age(response):
|
|||
"""
|
||||
if not response.has_header('Cache-Control'):
|
||||
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:
|
||||
return int(cc['max-age'])
|
||||
except (ValueError, TypeError, KeyError):
|
||||
|
@ -119,7 +119,7 @@ def get_max_age(response):
|
|||
|
||||
def set_response_etag(response):
|
||||
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
|
||||
|
||||
|
||||
|
@ -140,7 +140,7 @@ def _not_modified(request, response=None):
|
|||
# Last-Modified.
|
||||
for header in ('Cache-Control', 'Content-Location', 'Date', 'ETag', 'Expires', 'Last-Modified', 'Vary'):
|
||||
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
|
||||
# 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:
|
||||
cache_timeout = 0 # Can't have max-age negative
|
||||
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)
|
||||
|
||||
|
||||
|
@ -284,7 +284,7 @@ def patch_vary_headers(response, newheaders):
|
|||
# implementations may rely on the order of the Vary contents in, say,
|
||||
# computing an MD5 hash.
|
||||
if response.has_header('Vary'):
|
||||
vary_headers = cc_delim_re.split(response['Vary'])
|
||||
vary_headers = cc_delim_re.split(response.headers['Vary'])
|
||||
else:
|
||||
vary_headers = []
|
||||
# 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]
|
||||
vary_headers += additional_headers
|
||||
if '*' in vary_headers:
|
||||
response['Vary'] = '*'
|
||||
response.headers['Vary'] = '*'
|
||||
else:
|
||||
response['Vary'] = ', '.join(vary_headers)
|
||||
response.headers['Vary'] = ', '.join(vary_headers)
|
||||
|
||||
|
||||
def has_vary_header(response, header_query):
|
||||
|
@ -304,7 +304,7 @@ def has_vary_header(response, header_query):
|
|||
"""
|
||||
if not response.has_header('Vary'):
|
||||
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}
|
||||
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
|
||||
# multiple keys in the cache. See #18191 for details.
|
||||
headerlist = []
|
||||
for header in cc_delim_re.split(response['Vary']):
|
||||
for header in cc_delim_re.split(response.headers['Vary']):
|
||||
header = header.upper().replace('-', '_')
|
||||
if header != 'ACCEPT_LANGUAGE' or not is_accept_language_redundant:
|
||||
headerlist.append('HTTP_' + header)
|
||||
|
|
|
@ -102,9 +102,9 @@ def condition(etag_func=None, last_modified_func=None):
|
|||
# and if the request method is safe.
|
||||
if request.method in ('GET', 'HEAD'):
|
||||
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:
|
||||
response.setdefault('ETag', res_etag)
|
||||
response.headers.setdefault('ETag', res_etag)
|
||||
|
||||
return response
|
||||
|
||||
|
|
|
@ -107,8 +107,8 @@ class View:
|
|||
def options(self, request, *args, **kwargs):
|
||||
"""Handle responding to requests for the OPTIONS HTTP verb."""
|
||||
response = HttpResponse()
|
||||
response['Allow'] = ', '.join(self._allowed_methods())
|
||||
response['Content-Length'] = '0'
|
||||
response.headers['Allow'] = ', '.join(self._allowed_methods())
|
||||
response.headers['Content-Length'] = '0'
|
||||
return response
|
||||
|
||||
def _allowed_methods(self):
|
||||
|
|
|
@ -48,9 +48,9 @@ def serve(request, path, document_root=None, show_indexes=False):
|
|||
content_type, encoding = mimetypes.guess_type(str(fullpath))
|
||||
content_type = content_type or 'application/octet-stream'
|
||||
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:
|
||||
response["Content-Encoding"] = encoding
|
||||
response.headers["Content-Encoding"] = encoding
|
||||
return response
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ Here's an example::
|
|||
def some_view(request):
|
||||
# Create the HttpResponse object with the appropriate CSV header.
|
||||
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.writerow(['First row', 'Foo', 'Bar', 'Baz'])
|
||||
|
@ -88,7 +88,7 @@ the assembly and transmission of a large CSV file::
|
|||
writer = csv.writer(pseudo_buffer)
|
||||
response = StreamingHttpResponse((writer.writerow(row) for row in rows),
|
||||
content_type="text/csv")
|
||||
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
|
||||
response.headers['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
|
||||
return response
|
||||
|
||||
Using the template system
|
||||
|
@ -109,7 +109,7 @@ Here's an example, which generates the same CSV file as above::
|
|||
def some_view(request):
|
||||
# Create the HttpResponse object with the appropriate CSV header.
|
||||
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
|
||||
# some other source.
|
||||
|
|
|
@ -700,17 +700,29 @@ generators are immediately closed.
|
|||
If you need the response to be streamed from the iterator to the client, you
|
||||
must use the :class:`StreamingHttpResponse` class instead.
|
||||
|
||||
.. _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['Age'] = 120
|
||||
>>> del response['Age']
|
||||
|
||||
Note that unlike a dictionary, ``del`` doesn't raise ``KeyError`` if the header
|
||||
field doesn't exist.
|
||||
This proxies to ``HttpResponse.headers``, and is the original interface offered
|
||||
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
|
||||
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
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -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::
|
||||
|
||||
>>> 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
|
||||
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.
|
||||
|
||||
.. 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
|
||||
|
||||
A string denoting the charset in which the response will be encoded. If not
|
||||
|
|
|
@ -309,7 +309,10 @@ Pagination
|
|||
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
|
||||
~~~~~~~~
|
||||
|
|
|
@ -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.
|
||||
|
||||
The advantage to using the ``vary_on_headers`` decorator rather than manually
|
||||
setting the ``Vary`` header (using something like
|
||||
``response['Vary'] = 'user-agent'``) is that the decorator *adds* to the
|
||||
``Vary`` header (which may already exist), rather than setting it from scratch
|
||||
and potentially overriding anything that was already in there.
|
||||
setting the ``Vary`` header (using something like ``response.headers['Vary'] =
|
||||
'user-agent'``) is that the decorator *adds* to the ``Vary`` header (which may
|
||||
already exist), rather than setting it from scratch and potentially overriding
|
||||
anything that was already in there.
|
||||
|
||||
You can pass multiple headers to ``vary_on_headers()``::
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ And the view::
|
|||
last_book = self.get_queryset().latest('publication_date')
|
||||
response = HttpResponse()
|
||||
# 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
|
||||
|
||||
If the view is accessed from a ``GET`` request, an object list is returned in
|
||||
|
|
|
@ -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
|
||||
:exc:`~django.urls.Resolver404` exception.
|
||||
|
||||
You can also use dictionary syntax on the response object to query the value
|
||||
of any settings in the HTTP headers. For example, you could determine the
|
||||
content type of a response using ``response['Content-Type']``.
|
||||
As with a normal response, you can also access the headers through
|
||||
:attr:`.HttpResponse.headers`. For example, you could determine the content
|
||||
type of a response using ``response.headers['Content-Type']``.
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
|
|
@ -13,7 +13,7 @@ class XViewMiddlewareTest(TestDataMixin, AdminDocsTestCase):
|
|||
self.client.force_login(self.superuser)
|
||||
response = self.client.head('/xview/func/')
|
||||
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.save()
|
||||
response = self.client.head('/xview/func/')
|
||||
|
@ -31,7 +31,7 @@ class XViewMiddlewareTest(TestDataMixin, AdminDocsTestCase):
|
|||
self.client.force_login(self.superuser)
|
||||
response = self.client.head('/xview/class/')
|
||||
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.save()
|
||||
response = self.client.head('/xview/class/')
|
||||
|
@ -45,7 +45,7 @@ class XViewMiddlewareTest(TestDataMixin, AdminDocsTestCase):
|
|||
def test_callable_object_view(self):
|
||||
self.client.force_login(self.superuser)
|
||||
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=[])
|
||||
def test_no_auth_middleware(self):
|
||||
|
|
|
@ -2964,7 +2964,7 @@ class AdminViewStringPrimaryKeyTest(TestCase):
|
|||
)
|
||||
|
||||
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')
|
||||
|
|
|
@ -993,7 +993,7 @@ class LogoutTest(AuthViewsTestCase):
|
|||
in #25490.
|
||||
"""
|
||||
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):
|
||||
# Bug 11223
|
||||
|
|
|
@ -1700,9 +1700,9 @@ class CacheUtils(SimpleTestCase):
|
|||
with self.subTest(initial_vary=initial_vary, newheaders=newheaders):
|
||||
response = HttpResponse()
|
||||
if initial_vary is not None:
|
||||
response['Vary'] = initial_vary
|
||||
response.headers['Vary'] = initial_vary
|
||||
patch_vary_headers(response, newheaders)
|
||||
self.assertEqual(response['Vary'], resulting_vary)
|
||||
self.assertEqual(response.headers['Vary'], resulting_vary)
|
||||
|
||||
def test_get_cache_key(self):
|
||||
request = self.factory.get(self.path)
|
||||
|
@ -1753,7 +1753,7 @@ class CacheUtils(SimpleTestCase):
|
|||
def test_learn_cache_key(self):
|
||||
request = self.factory.head(self.path)
|
||||
response = HttpResponse()
|
||||
response['Vary'] = 'Pony'
|
||||
response.headers['Vary'] = 'Pony'
|
||||
# Make sure that the Vary header is added to the key hash
|
||||
learn_cache_key(request, response)
|
||||
|
||||
|
@ -1795,9 +1795,9 @@ class CacheUtils(SimpleTestCase):
|
|||
with self.subTest(initial_cc=initial_cc, newheaders=newheaders):
|
||||
response = HttpResponse()
|
||||
if initial_cc is not None:
|
||||
response['Cache-Control'] = initial_cc
|
||||
response.headers['Cache-Control'] = initial_cc
|
||||
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)
|
||||
|
||||
|
||||
|
@ -1892,7 +1892,7 @@ class CacheI18nTest(SimpleTestCase):
|
|||
request.META['HTTP_ACCEPT_LANGUAGE'] = accept_language
|
||||
request.META['HTTP_ACCEPT_ENCODING'] = 'gzip;q=1.0, identity; q=0.5, *;q=0'
|
||||
response = HttpResponse()
|
||||
response['Vary'] = vary
|
||||
response.headers['Vary'] = vary
|
||||
key = learn_cache_key(request, response)
|
||||
key2 = get_cache_key(request)
|
||||
self.assertEqual(key, reference_key)
|
||||
|
@ -1905,7 +1905,7 @@ class CacheI18nTest(SimpleTestCase):
|
|||
request = self.factory.get(self.path)
|
||||
request.META['HTTP_ACCEPT_ENCODING'] = 'gzip;q=1.0, identity; q=0.5, *;q=0'
|
||||
response = HttpResponse()
|
||||
response['Vary'] = 'accept-encoding'
|
||||
response.headers['Vary'] = 'accept-encoding'
|
||||
key = learn_cache_key(request, response)
|
||||
self.assertIn(lang, key, "Cache keys should include the language name when translation is active")
|
||||
self.check_accept_language_vary(
|
||||
|
@ -2364,9 +2364,9 @@ class TestWithTemplateResponse(SimpleTestCase):
|
|||
template = engines['django'].from_string("This is a test")
|
||||
response = TemplateResponse(HttpRequest(), template)
|
||||
if initial_vary is not None:
|
||||
response['Vary'] = initial_vary
|
||||
response.headers['Vary'] = initial_vary
|
||||
patch_vary_headers(response, newheaders)
|
||||
self.assertEqual(response['Vary'], resulting_vary)
|
||||
self.assertEqual(response.headers['Vary'], resulting_vary)
|
||||
|
||||
def test_get_cache_key(self):
|
||||
request = self.factory.get(self.path)
|
||||
|
|
|
@ -21,12 +21,12 @@ class ConditionalGet(SimpleTestCase):
|
|||
self.assertEqual(response.content, FULL_RESPONSE.encode())
|
||||
if response.request['REQUEST_METHOD'] in ('GET', 'HEAD'):
|
||||
if check_last_modified:
|
||||
self.assertEqual(response['Last-Modified'], LAST_MODIFIED_STR)
|
||||
self.assertEqual(response.headers['Last-Modified'], LAST_MODIFIED_STR)
|
||||
if check_etag:
|
||||
self.assertEqual(response['ETag'], ETAG)
|
||||
self.assertEqual(response.headers['ETag'], ETAG)
|
||||
else:
|
||||
self.assertNotIn('Last-Modified', response)
|
||||
self.assertNotIn('ETag', response)
|
||||
self.assertNotIn('Last-Modified', response.headers)
|
||||
self.assertNotIn('ETag', response.headers)
|
||||
|
||||
def assertNotModified(self, response):
|
||||
self.assertEqual(response.status_code, 304)
|
||||
|
|
|
@ -184,11 +184,11 @@ class ShortcutViewTests(TestCase):
|
|||
response = shortcut(self.request, user_ct.id, obj.id)
|
||||
self.assertEqual(
|
||||
'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'}):
|
||||
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):
|
||||
"""The view returns 404 when Model.get_absolute_url() isn't defined."""
|
||||
|
|
|
@ -438,7 +438,7 @@ class XFrameOptionsDecoratorsTests(TestCase):
|
|||
def a_view(request):
|
||||
return HttpResponse()
|
||||
r = a_view(HttpRequest())
|
||||
self.assertEqual(r['X-Frame-Options'], 'DENY')
|
||||
self.assertEqual(r.headers['X-Frame-Options'], 'DENY')
|
||||
|
||||
def test_sameorigin_decorator(self):
|
||||
"""
|
||||
|
@ -449,7 +449,7 @@ class XFrameOptionsDecoratorsTests(TestCase):
|
|||
def a_view(request):
|
||||
return HttpResponse()
|
||||
r = a_view(HttpRequest())
|
||||
self.assertEqual(r['X-Frame-Options'], 'SAMEORIGIN')
|
||||
self.assertEqual(r.headers['X-Frame-Options'], 'SAMEORIGIN')
|
||||
|
||||
def test_exempt_decorator(self):
|
||||
"""
|
||||
|
@ -477,6 +477,6 @@ class NeverCacheDecoratorTest(TestCase):
|
|||
return HttpResponse()
|
||||
r = a_view(HttpRequest())
|
||||
self.assertEqual(
|
||||
set(r['Cache-Control'].split(', ')),
|
||||
set(r.headers['Cache-Control'].split(', ')),
|
||||
{'max-age=0', 'no-cache', 'no-store', 'must-revalidate', 'private'},
|
||||
)
|
||||
|
|
|
@ -195,7 +195,7 @@ class ViewTest(SimpleTestCase):
|
|||
view = SimpleView.as_view()
|
||||
response = view(request)
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertTrue(response['Allow'])
|
||||
self.assertTrue(response.headers['Allow'])
|
||||
|
||||
def test_options_for_get_view(self):
|
||||
"""
|
||||
|
@ -226,7 +226,7 @@ class ViewTest(SimpleTestCase):
|
|||
|
||||
def _assert_allows(self, response, *expected_methods):
|
||||
"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)
|
||||
|
||||
def test_args_kwargs_request_on_self(self):
|
||||
|
@ -390,7 +390,7 @@ class TemplateViewTest(SimpleTestCase):
|
|||
|
||||
def test_content_type(self):
|
||||
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):
|
||||
match = resolve('/template/content_type/')
|
||||
|
@ -461,12 +461,12 @@ class RedirectViewTest(SimpleTestCase):
|
|||
"Named pattern parameter should reverse to the matching pattern"
|
||||
response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), pk=1)
|
||||
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):
|
||||
response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), 1)
|
||||
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):
|
||||
"Default is a temporary redirect"
|
||||
|
|
|
@ -292,44 +292,44 @@ class HttpResponseTests(unittest.TestCase):
|
|||
r = HttpResponse()
|
||||
|
||||
# ASCII strings or bytes values are converted to strings.
|
||||
r['key'] = 'test'
|
||||
self.assertEqual(r['key'], 'test')
|
||||
r['key'] = b'test'
|
||||
self.assertEqual(r['key'], 'test')
|
||||
r.headers['key'] = 'test'
|
||||
self.assertEqual(r.headers['key'], 'test')
|
||||
r.headers['key'] = b'test'
|
||||
self.assertEqual(r.headers['key'], 'test')
|
||||
self.assertIn(b'test', r.serialize_headers())
|
||||
|
||||
# Non-ASCII values are serialized to Latin-1.
|
||||
r['key'] = 'café'
|
||||
r.headers['key'] = 'café'
|
||||
self.assertIn('café'.encode('latin-1'), r.serialize_headers())
|
||||
|
||||
# Other Unicode values are MIME-encoded (there's no way to pass them as
|
||||
# bytes).
|
||||
r['key'] = '†'
|
||||
self.assertEqual(r['key'], '=?utf-8?b?4oCg?=')
|
||||
r.headers['key'] = '†'
|
||||
self.assertEqual(r.headers['key'], '=?utf-8?b?4oCg?=')
|
||||
self.assertIn(b'=?utf-8?b?4oCg?=', r.serialize_headers())
|
||||
|
||||
# The response also converts string or bytes keys to strings, but requires
|
||||
# them to contain ASCII
|
||||
r = HttpResponse()
|
||||
del r['Content-Type']
|
||||
r['foo'] = 'bar'
|
||||
headers = list(r.items())
|
||||
del r.headers['Content-Type']
|
||||
r.headers['foo'] = 'bar'
|
||||
headers = list(r.headers.items())
|
||||
self.assertEqual(len(headers), 1)
|
||||
self.assertEqual(headers[0], ('foo', 'bar'))
|
||||
|
||||
r = HttpResponse()
|
||||
del r['Content-Type']
|
||||
r[b'foo'] = 'bar'
|
||||
headers = list(r.items())
|
||||
del r.headers['Content-Type']
|
||||
r.headers[b'foo'] = 'bar'
|
||||
headers = list(r.headers.items())
|
||||
self.assertEqual(len(headers), 1)
|
||||
self.assertEqual(headers[0], ('foo', 'bar'))
|
||||
self.assertIsInstance(headers[0][0], str)
|
||||
|
||||
r = HttpResponse()
|
||||
with self.assertRaises(UnicodeError):
|
||||
r.__setitem__('føø', 'bar')
|
||||
r.headers.__setitem__('føø', 'bar')
|
||||
with self.assertRaises(UnicodeError):
|
||||
r.__setitem__('føø'.encode(), 'bar')
|
||||
r.headers.__setitem__('føø'.encode(), 'bar')
|
||||
|
||||
def test_long_line(self):
|
||||
# Bug #20889: long lines trigger newlines to be added to headers
|
||||
|
@ -337,18 +337,18 @@ class HttpResponseTests(unittest.TestCase):
|
|||
h = HttpResponse()
|
||||
f = b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz a\xcc\x88'
|
||||
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
|
||||
# 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):
|
||||
# Bug #10188: Do not allow newlines in headers (CR or LF)
|
||||
r = HttpResponse()
|
||||
with self.assertRaises(BadHeaderError):
|
||||
r.__setitem__('test\rstr', 'test')
|
||||
r.headers.__setitem__('test\rstr', 'test')
|
||||
with self.assertRaises(BadHeaderError):
|
||||
r.__setitem__('test\nstr', 'test')
|
||||
r.headers.__setitem__('test\nstr', 'test')
|
||||
|
||||
def test_dict_behavior(self):
|
||||
"""
|
||||
|
@ -436,7 +436,7 @@ class HttpResponseTests(unittest.TestCase):
|
|||
|
||||
# with Content-Encoding header
|
||||
r = HttpResponse()
|
||||
r['Content-Encoding'] = 'winning'
|
||||
r.headers['Content-Encoding'] = 'winning'
|
||||
r.write(b'abc')
|
||||
r.write(b'def')
|
||||
self.assertEqual(r.content, b'abcdef')
|
||||
|
@ -462,6 +462,14 @@ class HttpResponseTests(unittest.TestCase):
|
|||
with self.assertRaises(DisallowedRedirect):
|
||||
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):
|
||||
def test_redirect(self):
|
||||
|
@ -474,7 +482,7 @@ class HttpResponseSubclassesTests(SimpleTestCase):
|
|||
content_type='text/html',
|
||||
)
|
||||
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):
|
||||
"""Make sure HttpResponseRedirect works with lazy strings."""
|
||||
|
@ -523,7 +531,7 @@ class HttpResponseSubclassesTests(SimpleTestCase):
|
|||
|
||||
def test_not_allowed_repr_no_content_type(self):
|
||||
response = HttpResponseNotAllowed(('GET', 'POST'))
|
||||
del response['Content-Type']
|
||||
del response.headers['Content-Type']
|
||||
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):
|
||||
C1 = pickle.loads(pickle.dumps(C, protocol=proto))
|
||||
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'
|
||||
|
|
|
@ -116,7 +116,7 @@ class PathUnusedTests(URLTestCaseBase):
|
|||
def test_no_lang_activate(self):
|
||||
response = self.client.get('/nl/foo/')
|
||||
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')
|
||||
|
||||
|
||||
|
@ -200,7 +200,7 @@ class URLRedirectTests(URLTestCaseBase):
|
|||
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en')
|
||||
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)
|
||||
|
||||
def test_en_redirect_wrong_url(self):
|
||||
|
@ -211,7 +211,7 @@ class URLRedirectTests(URLTestCaseBase):
|
|||
response = self.client.get('/profiel/registreren/', HTTP_ACCEPT_LANGUAGE='nl')
|
||||
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)
|
||||
|
||||
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')
|
||||
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)
|
||||
|
||||
def test_pl_pl_redirect(self):
|
||||
|
@ -230,7 +230,7 @@ class URLRedirectTests(URLTestCaseBase):
|
|||
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='pl-pl')
|
||||
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)
|
||||
|
||||
@override_settings(
|
||||
|
@ -258,7 +258,7 @@ class URLVaryAcceptLanguageTests(URLTestCaseBase):
|
|||
self.assertRedirects(response, '/en/account/register/')
|
||||
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.assertFalse(response.get('Vary'))
|
||||
|
||||
|
@ -297,7 +297,7 @@ class URLRedirectWithoutTrailingSlashSettingTests(URLTestCaseBase):
|
|||
response = self.client.get('/account/register-without-slash', HTTP_ACCEPT_LANGUAGE='en')
|
||||
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)
|
||||
|
||||
|
||||
|
@ -310,13 +310,13 @@ class URLResponseTests(URLTestCaseBase):
|
|||
def test_en_url(self):
|
||||
response = self.client.get('/en/account/register/')
|
||||
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')
|
||||
|
||||
def test_nl_url(self):
|
||||
response = self.client.get('/nl/profiel/registreren/')
|
||||
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')
|
||||
|
||||
def test_wrong_en_prefix(self):
|
||||
|
@ -330,19 +330,19 @@ class URLResponseTests(URLTestCaseBase):
|
|||
def test_pt_br_url(self):
|
||||
response = self.client.get('/pt-br/conta/registre-se/')
|
||||
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')
|
||||
|
||||
def test_en_path(self):
|
||||
response = self.client.get('/en/account/register-as-path/')
|
||||
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')
|
||||
|
||||
def test_nl_path(self):
|
||||
response = self.client.get('/nl/profiel/registreren-als-pad/')
|
||||
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')
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
response = HttpResponse(*args, **kwargs)
|
||||
if headers:
|
||||
for k, v in headers.items():
|
||||
response[k] = v
|
||||
response.headers[k] = v
|
||||
return response
|
||||
return get_response
|
||||
|
||||
|
@ -47,7 +47,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
"Strict-Transport-Security: max-age=3600" to the response.
|
||||
"""
|
||||
self.assertEqual(
|
||||
self.process_response(secure=True)["Strict-Transport-Security"],
|
||||
self.process_response(secure=True).headers['Strict-Transport-Security'],
|
||||
'max-age=3600',
|
||||
)
|
||||
|
||||
|
@ -60,7 +60,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
response = self.process_response(
|
||||
secure=True,
|
||||
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)
|
||||
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
|
||||
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)
|
||||
def test_sts_off(self):
|
||||
|
@ -76,7 +79,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
With SECURE_HSTS_SECONDS=0, the middleware does not add a
|
||||
"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)
|
||||
def test_sts_include_subdomains(self):
|
||||
|
@ -86,7 +92,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
"includeSubDomains" directive to the response.
|
||||
"""
|
||||
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)
|
||||
def test_sts_no_include_subdomains(self):
|
||||
|
@ -96,7 +105,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
the "includeSubDomains" directive to the response.
|
||||
"""
|
||||
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)
|
||||
def test_sts_preload(self):
|
||||
|
@ -106,7 +115,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
directive to the response.
|
||||
"""
|
||||
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)
|
||||
def test_sts_subdomains_and_preload(self):
|
||||
|
@ -117,7 +129,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
to the response.
|
||||
"""
|
||||
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)
|
||||
def test_sts_no_preload(self):
|
||||
|
@ -127,7 +142,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
the "preload" directive to the response.
|
||||
"""
|
||||
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)
|
||||
def test_content_type_on(self):
|
||||
|
@ -135,7 +153,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
With SECURE_CONTENT_TYPE_NOSNIFF set to True, the middleware adds
|
||||
"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)
|
||||
def test_content_type_already_present(self):
|
||||
|
@ -144,7 +165,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
already present in the response.
|
||||
"""
|
||||
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)
|
||||
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
|
||||
"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)
|
||||
def test_xss_filter_on(self):
|
||||
|
@ -160,7 +181,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
With SECURE_BROWSER_XSS_FILTER set to True, the middleware adds
|
||||
"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)
|
||||
def test_xss_filter_already_present(self):
|
||||
|
@ -169,7 +193,7 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
already present in the response.
|
||||
"""
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
"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):
|
||||
"""
|
||||
|
@ -245,7 +269,10 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
)
|
||||
for value, expected in tests:
|
||||
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')
|
||||
def test_referrer_policy_already_present(self):
|
||||
|
@ -254,4 +281,4 @@ class SecurityMiddlewareTest(SimpleTestCase):
|
|||
present in the response.
|
||||
"""
|
||||
response = self.process_response(headers={'Referrer-Policy': 'unsafe-url'})
|
||||
self.assertEqual(response['Referrer-Policy'], 'unsafe-url')
|
||||
self.assertEqual(response.headers['Referrer-Policy'], 'unsafe-url')
|
||||
|
|
|
@ -292,7 +292,7 @@ class CommonMiddlewareTest(SimpleTestCase):
|
|||
return response
|
||||
|
||||
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 get_response(req):
|
||||
|
@ -308,11 +308,11 @@ class CommonMiddlewareTest(SimpleTestCase):
|
|||
|
||||
def get_response(req):
|
||||
response = HttpResponse()
|
||||
response['Content-Length'] = bad_content_length
|
||||
response.headers['Content-Length'] = bad_content_length
|
||||
return response
|
||||
|
||||
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
|
||||
|
||||
|
@ -607,7 +607,7 @@ class ConditionalGetMiddlewareTest(SimpleTestCase):
|
|||
self.assertEqual(new_response.status_code, 304)
|
||||
base_response = get_response(self.req)
|
||||
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.assertNotIn('Content-Language', new_response)
|
||||
|
||||
|
@ -622,7 +622,7 @@ class ConditionalGetMiddlewareTest(SimpleTestCase):
|
|||
return HttpResponse(status=200)
|
||||
|
||||
response = ConditionalGetMiddleware(self.get_response)(self.req)
|
||||
etag = response['ETag']
|
||||
etag = response.headers['ETag']
|
||||
put_request = self.request_factory.put('/', HTTP_IF_MATCH=etag)
|
||||
conditional_get_response = ConditionalGetMiddleware(get_200_response)(put_request)
|
||||
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'):
|
||||
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'):
|
||||
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):
|
||||
"""
|
||||
|
@ -666,11 +666,11 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
|
|||
"""
|
||||
with override_settings(X_FRAME_OPTIONS='DENY'):
|
||||
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'):
|
||||
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):
|
||||
"""
|
||||
|
@ -680,7 +680,7 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
|
|||
with override_settings(X_FRAME_OPTIONS=None):
|
||||
del settings.X_FRAME_OPTIONS # restored by override_settings
|
||||
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):
|
||||
"""
|
||||
|
@ -689,21 +689,21 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
|
|||
"""
|
||||
def same_origin_response(request):
|
||||
response = HttpResponse()
|
||||
response['X-Frame-Options'] = 'SAMEORIGIN'
|
||||
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
|
||||
return response
|
||||
|
||||
def deny_response(request):
|
||||
response = HttpResponse()
|
||||
response['X-Frame-Options'] = 'DENY'
|
||||
response.headers['X-Frame-Options'] = 'DENY'
|
||||
return response
|
||||
|
||||
with override_settings(X_FRAME_OPTIONS='DENY'):
|
||||
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'):
|
||||
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):
|
||||
"""
|
||||
|
@ -722,10 +722,10 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
|
|||
|
||||
with override_settings(X_FRAME_OPTIONS='SAMEORIGIN'):
|
||||
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())
|
||||
self.assertIsNone(r.get('X-Frame-Options'))
|
||||
self.assertIsNone(r.headers.get('X-Frame-Options'))
|
||||
|
||||
def test_is_extendable(self):
|
||||
"""
|
||||
|
@ -749,16 +749,16 @@ class XFrameOptionsMiddlewareTest(SimpleTestCase):
|
|||
|
||||
with override_settings(X_FRAME_OPTIONS='DENY'):
|
||||
r = OtherXFrameOptionsMiddleware(same_origin_response)(HttpRequest())
|
||||
self.assertEqual(r['X-Frame-Options'], 'SAMEORIGIN')
|
||||
self.assertEqual(r.headers['X-Frame-Options'], 'SAMEORIGIN')
|
||||
|
||||
request = HttpRequest()
|
||||
request.sameorigin = True
|
||||
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'):
|
||||
r = OtherXFrameOptionsMiddleware(get_response_empty)(HttpRequest())
|
||||
self.assertEqual(r['X-Frame-Options'], 'DENY')
|
||||
self.assertEqual(r.headers['X-Frame-Options'], 'DENY')
|
||||
|
||||
|
||||
class GZipMiddlewareTest(SimpleTestCase):
|
||||
|
@ -916,12 +916,12 @@ class ETagGZipMiddlewareTest(SimpleTestCase):
|
|||
"""
|
||||
def get_response(req):
|
||||
response = HttpResponse(self.compressible_string)
|
||||
response['ETag'] = '"eggs"'
|
||||
response.headers['ETag'] = '"eggs"'
|
||||
return response
|
||||
|
||||
request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
|
||||
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):
|
||||
"""
|
||||
|
@ -929,12 +929,12 @@ class ETagGZipMiddlewareTest(SimpleTestCase):
|
|||
"""
|
||||
def get_response(req):
|
||||
response = HttpResponse(self.compressible_string)
|
||||
response['ETag'] = 'W/"eggs"'
|
||||
response.headers['ETag'] = 'W/"eggs"'
|
||||
return response
|
||||
|
||||
request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
|
||||
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):
|
||||
"""
|
||||
|
@ -949,7 +949,7 @@ class ETagGZipMiddlewareTest(SimpleTestCase):
|
|||
|
||||
request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
|
||||
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_response = ConditionalGetMiddleware(get_response)(next_request)
|
||||
self.assertEqual(next_response.status_code, 304)
|
||||
|
|
|
@ -12,23 +12,26 @@ from django.test import SimpleTestCase
|
|||
class FileResponseTests(SimpleTestCase):
|
||||
def test_file_from_disk_response(self):
|
||||
response = FileResponse(open(__file__, 'rb'))
|
||||
self.assertEqual(response['Content-Length'], str(os.path.getsize(__file__)))
|
||||
self.assertIn(response['Content-Type'], ['text/x-python', 'text/plain'])
|
||||
self.assertEqual(response['Content-Disposition'], 'inline; filename="test_fileresponse.py"')
|
||||
self.assertEqual(response.headers['Content-Length'], str(os.path.getsize(__file__)))
|
||||
self.assertIn(response.headers['Content-Type'], ['text/x-python', 'text/plain'])
|
||||
self.assertEqual(
|
||||
response.headers['Content-Disposition'],
|
||||
'inline; filename="test_fileresponse.py"',
|
||||
)
|
||||
response.close()
|
||||
|
||||
def test_file_from_buffer_response(self):
|
||||
response = FileResponse(io.BytesIO(b'binary content'))
|
||||
self.assertEqual(response['Content-Length'], '14')
|
||||
self.assertEqual(response['Content-Type'], 'application/octet-stream')
|
||||
self.assertEqual(response.headers['Content-Length'], '14')
|
||||
self.assertEqual(response.headers['Content-Type'], 'application/octet-stream')
|
||||
self.assertFalse(response.has_header('Content-Disposition'))
|
||||
self.assertEqual(list(response), [b'binary content'])
|
||||
|
||||
def test_file_from_buffer_unnamed_attachment(self):
|
||||
response = FileResponse(io.BytesIO(b'binary content'), as_attachment=True)
|
||||
self.assertEqual(response['Content-Length'], '14')
|
||||
self.assertEqual(response['Content-Type'], 'application/octet-stream')
|
||||
self.assertEqual(response['Content-Disposition'], 'attachment')
|
||||
self.assertEqual(response.headers['Content-Length'], '14')
|
||||
self.assertEqual(response.headers['Content-Type'], 'application/octet-stream')
|
||||
self.assertEqual(response.headers['Content-Disposition'], 'attachment')
|
||||
self.assertEqual(list(response), [b'binary content'])
|
||||
|
||||
@skipIf(sys.platform == 'win32', "Named pipes are Unix-only.")
|
||||
|
@ -47,9 +50,12 @@ class FileResponseTests(SimpleTestCase):
|
|||
|
||||
def test_file_from_disk_as_attachment(self):
|
||||
response = FileResponse(open(__file__, 'rb'), as_attachment=True)
|
||||
self.assertEqual(response['Content-Length'], str(os.path.getsize(__file__)))
|
||||
self.assertIn(response['Content-Type'], ['text/x-python', 'text/plain'])
|
||||
self.assertEqual(response['Content-Disposition'], 'attachment; filename="test_fileresponse.py"')
|
||||
self.assertEqual(response.headers['Content-Length'], str(os.path.getsize(__file__)))
|
||||
self.assertIn(response.headers['Content-Type'], ['text/x-python', 'text/plain'])
|
||||
self.assertEqual(
|
||||
response.headers['Content-Disposition'],
|
||||
'attachment; filename="test_fileresponse.py"',
|
||||
)
|
||||
response.close()
|
||||
|
||||
def test_compressed_response(self):
|
||||
|
@ -67,7 +73,7 @@ class FileResponseTests(SimpleTestCase):
|
|||
with self.subTest(ext=extension):
|
||||
with tempfile.NamedTemporaryFile(suffix=extension) as tmp:
|
||||
response = FileResponse(tmp)
|
||||
self.assertEqual(response['Content-Type'], mimetype)
|
||||
self.assertEqual(response.headers['Content-Type'], mimetype)
|
||||
self.assertFalse(response.has_header('Content-Encoding'))
|
||||
|
||||
def test_unicode_attachment(self):
|
||||
|
@ -75,8 +81,11 @@ class FileResponseTests(SimpleTestCase):
|
|||
ContentFile(b'binary content', name="祝您平安.odt"), as_attachment=True,
|
||||
content_type='application/vnd.oasis.opendocument.text',
|
||||
)
|
||||
self.assertEqual(response['Content-Type'], 'application/vnd.oasis.opendocument.text')
|
||||
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"
|
||||
)
|
||||
|
|
|
@ -39,12 +39,12 @@ class HttpResponseBaseTests(SimpleTestCase):
|
|||
"""
|
||||
r = HttpResponseBase()
|
||||
|
||||
r['Header'] = 'Value'
|
||||
r.headers['Header'] = 'Value'
|
||||
r.setdefault('header', 'changed')
|
||||
self.assertEqual(r['header'], 'Value')
|
||||
self.assertEqual(r.headers['header'], 'Value')
|
||||
|
||||
r.setdefault('x-header', 'DefaultValue')
|
||||
self.assertEqual(r['X-Header'], 'DefaultValue')
|
||||
self.assertEqual(r.headers['X-Header'], 'DefaultValue')
|
||||
|
||||
|
||||
class HttpResponseTests(SimpleTestCase):
|
||||
|
@ -92,7 +92,7 @@ class HttpResponseTests(SimpleTestCase):
|
|||
|
||||
response = HttpResponse(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)
|
||||
self.assertEqual(response.charset, ISO88591)
|
||||
|
@ -134,7 +134,7 @@ class HttpResponseTests(SimpleTestCase):
|
|||
|
||||
def test_repr_no_content_type(self):
|
||||
response = HttpResponse(status=204)
|
||||
del response['Content-Type']
|
||||
del response.headers['Content-Type']
|
||||
self.assertEqual(repr(response), '<HttpResponse status_code=204>')
|
||||
|
||||
def test_wrap_textiowrapper(self):
|
||||
|
|
|
@ -781,7 +781,7 @@ class SessionMiddlewareTests(TestCase):
|
|||
)
|
||||
# SessionMiddleware sets 'Vary: Cookie' to prevent the 'Set-Cookie'
|
||||
# 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/')
|
||||
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.
|
||||
self.assertEqual(response.cookies, {})
|
||||
# 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):
|
||||
"""
|
||||
|
@ -849,7 +849,7 @@ class SessionMiddlewareTests(TestCase):
|
|||
'Set-Cookie: sessionid=%s' % request.session.session_key,
|
||||
str(response.cookies)
|
||||
)
|
||||
self.assertEqual(response['Vary'], 'Cookie')
|
||||
self.assertEqual(response.headers['Vary'], 'Cookie')
|
||||
|
||||
# Empty the session data.
|
||||
del request.session['foo']
|
||||
|
@ -866,7 +866,7 @@ class SessionMiddlewareTests(TestCase):
|
|||
'Set-Cookie: sessionid=%s' % request.session.session_key,
|
||||
str(response.cookies)
|
||||
)
|
||||
self.assertEqual(response['Vary'], 'Cookie')
|
||||
self.assertEqual(response.headers['Vary'], 'Cookie')
|
||||
|
||||
|
||||
class CookieSessionTests(SessionTestsMixin, SimpleTestCase):
|
||||
|
|
|
@ -9,7 +9,7 @@ class RenderTests(SimpleTestCase):
|
|||
response = self.client.get('/render/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
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'))
|
||||
|
||||
def test_render_with_multiple_templates(self):
|
||||
|
@ -21,7 +21,7 @@ class RenderTests(SimpleTestCase):
|
|||
response = self.client.get('/render/content_type/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
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):
|
||||
response = self.client.get('/render/status/')
|
||||
|
|
|
@ -56,4 +56,4 @@ class GenericViewsSitemapTests(SitemapTestsBase):
|
|||
</urlset>
|
||||
""" % (self.base_url, test_model.pk)
|
||||
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')
|
||||
|
|
|
@ -116,14 +116,14 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
def test_sitemap_last_modified(self):
|
||||
"Last-Modified header is set correctly"
|
||||
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):
|
||||
"""
|
||||
The Last-Modified header should be support dates (without time).
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
|
@ -131,7 +131,7 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
to GMT.
|
||||
"""
|
||||
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):
|
||||
"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.
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
|
@ -173,7 +173,7 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
Test sitemaps are sorted by lastmod in descending order.
|
||||
"""
|
||||
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)
|
||||
def test_localized_priority(self):
|
||||
|
@ -243,10 +243,10 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
|
||||
def test_x_robots_sitemap(self):
|
||||
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')
|
||||
self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive')
|
||||
self.assertEqual(response.headers['X-Robots-Tag'], 'noindex, noodp, noarchive')
|
||||
|
||||
def test_empty_sitemap(self):
|
||||
response = self.client.get('/empty/sitemap.xml')
|
||||
|
|
|
@ -421,14 +421,14 @@ class SyndicationFeedTest(FeedTestCase):
|
|||
Tests the Last-Modified header with naive publication 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):
|
||||
"""
|
||||
Tests the Last-Modified header with aware publication 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
|
||||
response = self.client.get('/syndication/no_pubdate/')
|
||||
|
|
|
@ -122,13 +122,13 @@ class SimpleTemplateResponseTest(SimpleTestCase):
|
|||
|
||||
def test_kwargs(self):
|
||||
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.charset, 'ascii')
|
||||
|
||||
def test_args(self):
|
||||
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)
|
||||
|
||||
@require_jinja2
|
||||
|
@ -175,7 +175,7 @@ class SimpleTemplateResponseTest(SimpleTestCase):
|
|||
unpickled_response = pickle.loads(pickled_response)
|
||||
|
||||
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)
|
||||
|
||||
# ...and the unpickled response doesn't have the
|
||||
|
@ -249,13 +249,13 @@ class TemplateResponseTest(SimpleTestCase):
|
|||
|
||||
def test_kwargs(self):
|
||||
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)
|
||||
|
||||
def test_args(self):
|
||||
response = TemplateResponse(self.factory.get('/'), '', {},
|
||||
'application/json', 504)
|
||||
self.assertEqual(response['content-type'], 'application/json')
|
||||
self.assertEqual(response.headers['content-type'], 'application/json')
|
||||
self.assertEqual(response.status_code, 504)
|
||||
|
||||
@require_jinja2
|
||||
|
@ -287,7 +287,7 @@ class TemplateResponseTest(SimpleTestCase):
|
|||
unpickled_response = pickle.loads(pickled_response)
|
||||
|
||||
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)
|
||||
|
||||
# ...and the unpickled response doesn't have the
|
||||
|
|
|
@ -159,7 +159,7 @@ class ClientTest(TestCase):
|
|||
"Check the value of HTTP headers returned in a response"
|
||||
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):
|
||||
"""
|
||||
|
|
|
@ -102,7 +102,7 @@ def json_view(request):
|
|||
def view_with_header(request):
|
||||
"A view that has a custom header"
|
||||
response = HttpResponse()
|
||||
response['X-DJANGO-TEST'] = 'Slartibartfast'
|
||||
response.headers['X-DJANGO-TEST'] = 'Slartibartfast'
|
||||
return response
|
||||
|
||||
|
||||
|
|
|
@ -1210,7 +1210,7 @@ class RequestMethodStringDataTests(SimpleTestCase):
|
|||
)
|
||||
for content_type in valid_types:
|
||||
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'})
|
||||
|
||||
def test_json_multiple_access(self):
|
||||
|
|
|
@ -1481,7 +1481,7 @@ class NonHTMLResponseExceptionReporterFilter(ExceptionReportTestMixin, LoggingCa
|
|||
@override_settings(DEBUG=True, ROOT_URLCONF='view_tests.urls')
|
||||
def test_non_html_response_encoding(self):
|
||||
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):
|
||||
|
|
|
@ -249,7 +249,7 @@ class I18NViewTests(SimpleTestCase):
|
|||
catalog = gettext.translation('djangojs', locale_dir, [lang_code])
|
||||
trans_txt = catalog.gettext('this is to be translated')
|
||||
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:
|
||||
# "this is to be translated": <value of trans_txt Python variable>
|
||||
# json.dumps() is used to be able to check Unicode strings.
|
||||
|
|
|
@ -10,7 +10,7 @@ class JsonResponseTests(SimpleTestCase):
|
|||
response = self.client.get('/json/response/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(
|
||||
response['content-type'], 'application/json')
|
||||
response.headers['content-type'], 'application/json')
|
||||
self.assertEqual(json.loads(response.content.decode()), {
|
||||
'a': [1, 2, 3],
|
||||
'foo': {'bar': 'baz'},
|
||||
|
|
|
@ -29,7 +29,7 @@ class StaticTests(SimpleTestCase):
|
|||
file_path = path.join(media_dir, filename)
|
||||
with open(file_path, 'rb') as fp:
|
||||
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))
|
||||
|
||||
def test_chunked(self):
|
||||
|
@ -44,7 +44,7 @@ class StaticTests(SimpleTestCase):
|
|||
|
||||
def test_unknown_mime_type(self):
|
||||
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()
|
||||
|
||||
def test_copes_with_empty_path_component(self):
|
||||
|
@ -87,7 +87,7 @@ class StaticTests(SimpleTestCase):
|
|||
response_content = b''.join(response)
|
||||
with open(path.join(media_dir, file_name), 'rb') as fp:
|
||||
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):
|
||||
"""Handle even more bogus If-Modified-Since values gracefully
|
||||
|
@ -102,7 +102,7 @@ class StaticTests(SimpleTestCase):
|
|||
response_content = b''.join(response)
|
||||
with open(path.join(media_dir, file_name), 'rb') as fp:
|
||||
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):
|
||||
response = self.client.get('/%s/nonexistent_resource' % self.prefix)
|
||||
|
|
Loading…
Reference in New Issue