populate the initial data using setitem to ensure values are correctly encoded convert headers keyvalue to asciilatin1 native strings charset must be ascii or latin1 if mimeencode is true and value can t be represented in the given charset apply mimeencoding ensure string is valid in given charset convert bytestring using given charset ensure string is valid in given charset encoding to a string of the specified charset failed but we don t know what type that value was or if it contains newlines which we may need to check for before sending it to be encoded for multiple character sets an http response base class with dictionaryaccessed headers this class doesn t handle content it should not be used directly use the httpresponse and streaminghttpresponse subclasses instead this parameter is set by the handler it s necessary to preserve the historical behavior of requestfinished leave self reasonphrase unset in order to use the default reason phrase for status code the contenttype header may not yet be set because the charset is being inserted into it extract the charset and strip its double quotes note that having parsed it from the contenttype we don t store it back into the charset for later intentionally to allow for the contenttype to be switched again later http headers as a bytestring return brn join key encodeascii b value encodelatin1 for key value in self headers items bytes serializeheaders property def contenttypeforreprself return s self headerscontenttype if contenttype in self headers else def setitemself header value self headersheader value def delitemself header del self headersheader def getitemself header return self headersheader def hasheaderself header set a cookie expires can be a string in the correct format a naive datetime datetime object in utc an aware datetime datetime object in any time zone if it is a datetime datetime object then calculate maxage maxage can be intfloat specifying seconds datetime timedelta object add one second so the date matches exactly a fraction of time gets lost between converting to a timedelta and then the date string just set maxage the maxage logic will set expires ie requires expires so set it if hasn t been already set a header unless it has already been set self headers setdefaultkey value def setsignedcookieself key value salt kwargs value signing getcookiesignersaltkey salt signvalue return self setcookiekey value kwargs def deletecookieself key path domainnone samesitenone browsers can ignore the setcookie header if the cookie doesn t use the secure flag and the cookie name starts with host or secure or the samesite is none secure key startswithsecure host or samesite and samesite lower none self setcookie key maxage0 pathpath domaindomain securesecure expiresthu 01 jan 1970 00 00 00 gmt samesitesamesite common methods used by subclasses def makebytesself value per pep 3333 this response body must be bytes to avoid returning an instance of a subclass this function returns bytesvalue this doesn t make a copy when value already contains bytes handle string types we can t rely on forcebytes here because python attempts str conversion first when self charset utf8 it reencodes the content handle nonstring types these methods partially implement the filelike object interface see https docs python orglibraryio htmlio iobase the wsgi server must call this method upon completion of the request see http blog dscpl com au201210obligationsforcallingcloseon html free resources that were still referenced these methods partially implement a streamlike object interface see https docs python orglibraryio htmlio iobase an http response class with a string as content this content can be read appended to or replaced content is a bytestring see the content property methods full http message including headers as a bytestring return self serializeheaders brnrn self content bytes serialize property def contentself return b joinself container content setter def contentself value consume iterators upon assignment to allow repeated iteration if hasattrvalue iter and not isinstance value bytes memoryview str content b joinself makebyteschunk for chunk in value if hasattrvalue close try value close except exception pass else content self makebytesvalue create a list of properly encoded bytestrings to support write self container content def iterself return iterself container def writeself content self container appendself makebytescontent def tellself return lenself content def getvalueself return self content def writableself return true def writelinesself lines for line in lines self writeline class streaminghttpresponsehttpresponsebase streaming true def initself streamingcontent args kwargs super initargs kwargs streamingcontent should be an iterable of bytestrings see the streamingcontent property methods self streamingcontent streamingcontent def reprself return clss statuscodestatuscodedcontenttypes cls self class qualname statuscode self statuscode contenttype self contenttypeforrepr property def contentself raise attributeerror this s instance has no content attribute use streamingcontent instead self class name property def streamingcontentself if self isasync pull to lexical scope to capture fixed reference in case streamingcontent is set again later iterator self iterator async def awrapper async for part in iterator yield self makebytespart return awrapper else return mapself makebytes self iterator streamingcontent setter def streamingcontentself value self setstreamingcontentvalue def setstreamingcontentself value ensure we can never iterate on value more than once try self iterator itervalue self isasync false except typeerror self iterator aitervalue self isasync true if hasattrvalue close self resourceclosers appendvalue close def iterself try return iterself streamingcontent except typeerror warnings warn streaminghttpresponse must consume asynchronous iterators in order to serve them synchronously use a synchronous iterator instead warning async iterator consume in asynctosync and map back async def tolistiterator aslist async for chunk in iterator aslist appendchunk return aslist return mapself makebytes iterasynctosynctolistself iterator async def aiterself try async for part in self streamingcontent yield part except typeerror warnings warn streaminghttpresponse must consume synchronous iterators in order to serve them asynchronously use an asynchronous iterator instead warning sync iterator consume via synctoasync and yield via async generator for part in await synctoasynclistself streamingcontent yield part def getvalueself return b joinself streamingcontent class fileresponsestreaminghttpresponse blocksize 4096 def initself args asattachmentfalse filename kwargs self asattachment asattachment self filename filename self noexplicitcontenttype contenttype not in kwargs or kwargscontenttype is none super initargs kwargs def setstreamingcontentself value if not hasattrvalue read self filetostream none return super setstreamingcontentvalue self filetostream filelike value if hasattrfilelike close self resourceclosers appendfilelike close value iterlambda filelike readself blocksize b self setheadersfilelike super setstreamingcontentvalue def setheadersself filelike filename getattrfilelike name filename filename if isinstancefilename str else seekable hasattrfilelike seek and not hasattrfilelike seekable or filelike seekable if hasattrfilelike tell if seekable initialposition filelike tell filelike seek0 io seekend self headerscontentlength filelike tell initialposition filelike seekinitialposition elif hasattrfilelike getbuffer self headerscontentlength filelike getbuffer nbytes filelike tell elif os path existsfilename self headerscontentlength os path getsizefilename filelike tell elif seekable self headerscontentlength sum iterlambda lenfilelike readself blocksize 0 filelike seekintself headerscontentlength io seekend filename os path basenameself filename or filename if self noexplicitcontenttype if filename contenttype encoding mimetypes guesstypefilename encoding isn t set to prevent browsers from automatically uncompressing files contenttype br applicationxbrotli bzip2 applicationxbzip compress applicationxcompress gzip applicationgzip xz applicationxxz getencoding contenttype self headerscontenttype contenttype or applicationoctetstream else self headerscontenttype applicationoctetstream if contentdisposition contentdispositionheader self asattachment filename self headerscontentdisposition contentdisposition class httpresponseredirectbasehttpresponse allowedschemes http https ftp def initself redirectto args kwargs super initargs kwargs selflocation iritouriredirectto parsed urlparsestrredirectto if parsed scheme and parsed scheme not in self allowedschemes raise disallowedredirect unsafe redirect to url with protocol s parsed scheme url propertylambda self selflocation def reprself return clss statuscodestatuscodedcontenttypes urlurls cls self class name statuscode self statuscode contenttype self contenttypeforrepr url self url class httpresponseredirecthttpresponseredirectbase statuscode 302 class httpresponsepermanentredirecthttpresponseredirectbase statuscode 301 class httpresponsenotmodifiedhttpresponse statuscode 304 def initself args kwargs super initargs kwargs del selfcontenttype httpresponse content setter def contentself value if value raise attributeerror you cannot set content to a 304 not modified response self container class httpresponsebadrequesthttpresponse statuscode 400 class httpresponsenotfoundhttpresponse statuscode 404 class httpresponseforbiddenhttpresponse statuscode 403 class httpresponsenotallowedhttpresponse statuscode 405 def initself permittedmethods args kwargs super initargs kwargs selfallow joinpermittedmethods def reprself return clss methodss statuscodestatuscodedcontenttypes cls self class name statuscode self statuscode contenttype self contenttypeforrepr methods selfallow class httpresponsegonehttpresponse statuscode 410 class httpresponseservererrorhttpresponse statuscode 500 class http404exception pass class jsonresponsehttpresponse def init self data encoderdjangojsonencoder safetrue jsondumpsparamsnone kwargs if safe and not isinstancedata dict raise typeerror in order to allow nondict objects to be serialized set the safe parameter to false if jsondumpsparams is none jsondumpsparams kwargs setdefaultcontenttype applicationjson data json dumpsdata clsencoder jsondumpsparams super initcontentdata kwargs populate the initial data using __setitem__ to ensure values are correctly encoded 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 ensure string is valid in given charset convert bytestring using given charset ensure string is valid in given charset encoding to a string of the specified charset failed but we don t know what type that value was or if it contains newlines which we may need to check for before sending it to be encoded for multiple character sets an http response base class with dictionary accessed headers this class doesn t handle content it should not be used directly use the httpresponse and streaminghttpresponse subclasses instead this parameter is set by the handler it s necessary to preserve the historical behavior of request_finished leave self _reason_phrase unset in order to use the default reason phrase for status code the content type header may not yet be set because the charset is being inserted into it extract the charset and strip its double quotes note that having parsed it from the content type we don t store it back into the _charset for later intentionally to allow for the content type to be switched again later http headers as a bytestring case insensitive check for a header set a cookie expires can be a string in the correct format a naive datetime datetime object in utc an aware datetime datetime object in any time zone if it is a datetime datetime object then calculate max_age max_age can be int float specifying seconds datetime timedelta object add one second so the date matches exactly a fraction of time gets lost between converting to a timedelta and then the date string just set max_age the max_age logic will set expires ie requires expires so set it if hasn t been already set a header unless it has already been set browsers can ignore the set cookie header if the cookie doesn t use the secure flag and the cookie name starts with __host or __secure or the samesite is none common methods used by subclasses turn a value into a bytestring encoded in the output charset per pep 3333 this response body must be bytes to avoid returning an instance of a subclass this function returns bytes value this doesn t make a copy when value already contains bytes handle string types we can t rely on force_bytes here because python attempts str conversion first when self _charset utf 8 it re encodes the content handle non string types these methods partially implement the file like object interface see https docs python org library io html io iobase the wsgi server must call this method upon completion of the request see http blog dscpl com au 2012 10 obligations for calling close on html free resources that were still referenced these methods partially implement a stream like object interface see https docs python org library io html io iobase an http response class with a string as content this content can be read appended to or replaced content is a bytestring see the content property methods full http message including headers as a bytestring consume iterators upon assignment to allow repeated iteration create a list of properly encoded bytestrings to support write a streaming http response class with an iterator as content this should only be iterated once when the response is streamed to the client however it can be appended to or replaced with a new iterator that wraps the original content or yields entirely new content streaming_content should be an iterable of bytestrings see the streaming_content property methods pull to lexical scope to capture fixed reference in case streaming_content is set again later ensure we can never iterate on value more than once async iterator consume in async_to_sync and map back sync iterator consume via sync_to_async and yield via async generator a streaming http response class optimized for files set some common response headers content length content type and content disposition based on the filelike response content encoding isn t set to prevent browsers from automatically uncompressing files an http response class that consumes data to be serialized to json param data data to be dumped into json by default only dict objects are allowed to be passed due to a security flaw before ecmascript 5 see the safe parameter for more information param encoder should be a json encoder class defaults to django core serializers json djangojsonencoder param safe controls if only dict objects may be serialized defaults to true param json_dumps_params a dictionary of kwargs passed to json dumps
import datetime
import io
import json
import mimetypes
import os
import re
import sys
import time
import warnings
from email.header import Header
from http.client import responses
from urllib.parse import urlparse

from asgiref.sync import async_to_sync, sync_to_async

from django.conf import settings
from django.core import signals, signing
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 content_disposition_header, http_date
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):
        
        self._store = {}
        if data:
            for header, value in self._unpack_items(data):
                self[header] = value

    def _convert_to_charset(self, value, charset, mime_encode=False):
        
        try:
            if isinstance(value, str):
                
                value.encode(charset)
            elif isinstance(value, bytes):
                
                value = value.decode(charset)
            else:
                value = str(value)
                
                value.encode(charset)
            if "\n" in value or "\r" in value:
                raise BadHeaderError(
                    f"Header values can't contain newlines (got {value!r})"
                )
        except UnicodeError as e:
            
            
            
            
            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(
                    f"Header values can't contain newlines (got {value!r})"
                ) from 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


class HttpResponseBase:
    

    status_code = 200

    def __init__(
        self, content_type=None, status=None, reason=None, charset=None, headers=None
    ):
        self.headers = ResponseHeaders(headers)
        self._charset = charset
        if "Content-Type" not in self.headers:
            if content_type is None:
                content_type = f"text/html; charset={self.charset}"
            self.headers["Content-Type"] = content_type
        elif content_type:
            raise ValueError(
                "'headers' must not contain 'Content-Type' when the "
                "'content_type' parameter is provided."
            )
        self._resource_closers = []
        
        
        self._handler_class = None
        self.cookies = SimpleCookie()
        self.closed = False
        if status is not None:
            try:
                self.status_code = int(status)
            except (ValueError, TypeError):
                raise TypeError("HTTP status code must be an integer.")

            if not 100 <= self.status_code <= 599:
                raise ValueError("HTTP status code must be an integer from 100 to 599.")
        self._reason_phrase = reason

    @property
    def reason_phrase(self):
        if self._reason_phrase is not None:
            return self._reason_phrase
        
        
        return responses.get(self.status_code, "Unknown Status Code")

    @reason_phrase.setter
    def reason_phrase(self, value):
        self._reason_phrase = value

    @property
    def charset(self):
        if self._charset is not None:
            return self._charset
        
        
        if content_type := self.headers.get("Content-Type"):
            if matched := _charset_from_content_type_re.search(content_type):
                
                
                
                
                return matched["charset"].replace('"', "")
        return settings.DEFAULT_CHARSET

    @charset.setter
    def charset(self, value):
        self._charset = value

    def serialize_headers(self):
        
        return b"\r\n".join(
            [
                key.encode("ascii") + b": " + value.encode("latin-1")
                for key, value in self.headers.items()
            ]
        )

    __bytes__ = serialize_headers

    @property
    def _content_type_for_repr(self):
        return (
            ', "%s"' % self.headers["Content-Type"]
            if "Content-Type" in self.headers
            else ""
        )

    def __setitem__(self, header, value):
        self.headers[header] = value

    def __delitem__(self, header):
        del self.headers[header]

    def __getitem__(self, header):
        return self.headers[header]

    def has_header(self, header):
        
        return header in self.headers

    __contains__ = has_header

    def items(self):
        return self.headers.items()

    def get(self, header, alternate=None):
        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,
    ):
        
        self.cookies[key] = value
        if expires is not None:
            if isinstance(expires, datetime.datetime):
                if timezone.is_naive(expires):
                    expires = timezone.make_aware(expires, datetime.timezone.utc)
                delta = expires - datetime.datetime.now(tz=datetime.timezone.utc)
                
                
                
                delta += datetime.timedelta(seconds=1)
                
                expires = None
                if max_age is not None:
                    raise ValueError("'expires' and 'max_age' can't be used together.")
                max_age = max(0, delta.days * 86400 + delta.seconds)
            else:
                self.cookies[key]["expires"] = expires
        else:
            self.cookies[key]["expires"] = ""
        if max_age is not None:
            if isinstance(max_age, datetime.timedelta):
                max_age = max_age.total_seconds()
            self.cookies[key]["max-age"] = int(max_age)
            
            if not expires:
                self.cookies[key]["expires"] = http_date(time.time() + max_age)
        if path is not None:
            self.cookies[key]["path"] = path
        if domain is not None:
            self.cookies[key]["domain"] = domain
        if secure:
            self.cookies[key]["secure"] = True
        if httponly:
            self.cookies[key]["httponly"] = True
        if samesite:
            if samesite.lower() not in ("lax", "none", "strict"):
                raise ValueError('samesite must be "lax", "none", or "strict".')
            self.cookies[key]["samesite"] = samesite

    def setdefault(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)
        return self.set_cookie(key, value, **kwargs)

    def delete_cookie(self, key, path="/", domain=None, samesite=None):
        
        
        
        
        secure = key.startswith(("__Secure-", "__Host-")) or (
            samesite and samesite.lower() == "none"
        )
        self.set_cookie(
            key,
            max_age=0,
            path=path,
            domain=domain,
            secure=secure,
            expires="Thu, 01 Jan 1970 00:00:00 GMT",
            samesite=samesite,
        )

    

    def make_bytes(self, value):
        
        
        
        

        
        
        
        if isinstance(value, (bytes, memoryview)):
            return bytes(value)
        if isinstance(value, str):
            return bytes(value.encode(self.charset))
        
        return str(value).encode(self.charset)

    
    

    
    
    def close(self):
        for closer in self._resource_closers:
            try:
                closer()
            except Exception:
                pass
        
        self._resource_closers.clear()
        self.closed = True
        signals.request_finished.send(sender=self._handler_class)

    def write(self, content):
        raise OSError("This %s instance is not writable" % self.__class__.__name__)

    def flush(self):
        pass

    def tell(self):
        raise OSError(
            "This %s instance cannot tell its position" % self.__class__.__name__
        )

    
    

    def readable(self):
        return False

    def seekable(self):
        return False

    def writable(self):
        return False

    def writelines(self, lines):
        raise OSError("This %s instance is not writable" % self.__class__.__name__)


class HttpResponse(HttpResponseBase):
    

    streaming = False

    def __init__(self, content=b"", *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        self.content = content

    def __repr__(self):
        return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % {
            "cls": self.__class__.__name__,
            "status_code": self.status_code,
            "content_type": self._content_type_for_repr,
        }

    def serialize(self):
        
        return self.serialize_headers() + b"\r\n\r\n" + self.content

    __bytes__ = serialize

    @property
    def content(self):
        return b"".join(self._container)

    @content.setter
    def content(self, value):
        
        if hasattr(value, "__iter__") and not isinstance(
            value, (bytes, memoryview, str)
        ):
            content = b"".join(self.make_bytes(chunk) for chunk in value)
            if hasattr(value, "close"):
                try:
                    value.close()
                except Exception:
                    pass
        else:
            content = self.make_bytes(value)
        
        self._container = [content]

    def __iter__(self):
        return iter(self._container)

    def write(self, content):
        self._container.append(self.make_bytes(content))

    def tell(self):
        return len(self.content)

    def getvalue(self):
        return self.content

    def writable(self):
        return True

    def writelines(self, lines):
        for line in lines:
            self.write(line)


class StreamingHttpResponse(HttpResponseBase):
    

    streaming = True

    def __init__(self, streaming_content=(), *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        
        self.streaming_content = streaming_content

    def __repr__(self):
        return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % {
            "cls": self.__class__.__qualname__,
            "status_code": self.status_code,
            "content_type": self._content_type_for_repr,
        }

    @property
    def content(self):
        raise AttributeError(
            "This %s instance has no `content` attribute. Use "
            "`streaming_content` instead." % self.__class__.__name__
        )

    @property
    def streaming_content(self):
        if self.is_async:
            
            
            _iterator = self._iterator

            async def awrapper():
                async for part in _iterator:
                    yield self.make_bytes(part)

            return awrapper()
        else:
            return map(self.make_bytes, self._iterator)

    @streaming_content.setter
    def streaming_content(self, value):
        self._set_streaming_content(value)

    def _set_streaming_content(self, value):
        
        try:
            self._iterator = iter(value)
            self.is_async = False
        except TypeError:
            self._iterator = aiter(value)
            self.is_async = True
        if hasattr(value, "close"):
            self._resource_closers.append(value.close)

    def __iter__(self):
        try:
            return iter(self.streaming_content)
        except TypeError:
            warnings.warn(
                "StreamingHttpResponse must consume asynchronous iterators in order to "
                "serve them synchronously. Use a synchronous iterator instead.",
                Warning,
            )

            
            async def to_list(_iterator):
                as_list = []
                async for chunk in _iterator:
                    as_list.append(chunk)
                return as_list

            return map(self.make_bytes, iter(async_to_sync(to_list)(self._iterator)))

    async def __aiter__(self):
        try:
            async for part in self.streaming_content:
                yield part
        except TypeError:
            warnings.warn(
                "StreamingHttpResponse must consume synchronous iterators in order to "
                "serve them asynchronously. Use an asynchronous iterator instead.",
                Warning,
            )
            
            
            for part in await sync_to_async(list)(self.streaming_content):
                yield part

    def getvalue(self):
        return b"".join(self.streaming_content)


class FileResponse(StreamingHttpResponse):
    

    block_size = 4096

    def __init__(self, *args, as_attachment=False, filename="", **kwargs):
        self.as_attachment = as_attachment
        self.filename = filename
        self._no_explicit_content_type = (
            "content_type" not in kwargs or kwargs["content_type"] is None
        )
        super().__init__(*args, **kwargs)

    def _set_streaming_content(self, value):
        if not hasattr(value, "read"):
            self.file_to_stream = None
            return super()._set_streaming_content(value)

        self.file_to_stream = filelike = value
        if hasattr(filelike, "close"):
            self._resource_closers.append(filelike.close)
        value = iter(lambda: filelike.read(self.block_size), b"")
        self.set_headers(filelike)
        super()._set_streaming_content(value)

    def set_headers(self, filelike):
        
        filename = getattr(filelike, "name", "")
        filename = filename if isinstance(filename, str) else ""
        seekable = hasattr(filelike, "seek") and (
            not hasattr(filelike, "seekable") or filelike.seekable()
        )
        if hasattr(filelike, "tell"):
            if seekable:
                initial_position = filelike.tell()
                filelike.seek(0, io.SEEK_END)
                self.headers["Content-Length"] = filelike.tell() - initial_position
                filelike.seek(initial_position)
            elif hasattr(filelike, "getbuffer"):
                self.headers["Content-Length"] = (
                    filelike.getbuffer().nbytes - filelike.tell()
                )
            elif os.path.exists(filename):
                self.headers["Content-Length"] = (
                    os.path.getsize(filename) - filelike.tell()
                )
        elif seekable:
            self.headers["Content-Length"] = sum(
                iter(lambda: len(filelike.read(self.block_size)), 0)
            )
            filelike.seek(-int(self.headers["Content-Length"]), io.SEEK_END)

        filename = os.path.basename(self.filename or filename)
        if self._no_explicit_content_type:
            if filename:
                content_type, encoding = mimetypes.guess_type(filename)
                
                
                content_type = {
                    "br": "application/x-brotli",
                    "bzip2": "application/x-bzip",
                    "compress": "application/x-compress",
                    "gzip": "application/gzip",
                    "xz": "application/x-xz",
                }.get(encoding, content_type)
                self.headers["Content-Type"] = (
                    content_type or "application/octet-stream"
                )
            else:
                self.headers["Content-Type"] = "application/octet-stream"

        if content_disposition := content_disposition_header(
            self.as_attachment, filename
        ):
            self.headers["Content-Disposition"] = content_disposition


class HttpResponseRedirectBase(HttpResponse):
    allowed_schemes = ["http", "https", "ftp"]

    def __init__(self, redirect_to, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self["Location"] = iri_to_uri(redirect_to)
        parsed = urlparse(str(redirect_to))
        if parsed.scheme and parsed.scheme not in self.allowed_schemes:
            raise DisallowedRedirect(
                "Unsafe redirect to URL with protocol '%s'" % parsed.scheme
            )

    url = property(lambda self: self["Location"])

    def __repr__(self):
        return (
            '<%(cls)s status_code=%(status_code)d%(content_type)s, url="%(url)s">'
            % {
                "cls": self.__class__.__name__,
                "status_code": self.status_code,
                "content_type": self._content_type_for_repr,
                "url": self.url,
            }
        )


class HttpResponseRedirect(HttpResponseRedirectBase):
    status_code = 302


class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
    status_code = 301


class HttpResponseNotModified(HttpResponse):
    status_code = 304

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        del self["content-type"]

    @HttpResponse.content.setter
    def content(self, value):
        if value:
            raise AttributeError(
                "You cannot set content to a 304 (Not Modified) response"
            )
        self._container = []


class HttpResponseBadRequest(HttpResponse):
    status_code = 400


class HttpResponseNotFound(HttpResponse):
    status_code = 404


class HttpResponseForbidden(HttpResponse):
    status_code = 403


class HttpResponseNotAllowed(HttpResponse):
    status_code = 405

    def __init__(self, permitted_methods, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self["Allow"] = ", ".join(permitted_methods)

    def __repr__(self):
        return "<%(cls)s [%(methods)s] status_code=%(status_code)d%(content_type)s>" % {
            "cls": self.__class__.__name__,
            "status_code": self.status_code,
            "content_type": self._content_type_for_repr,
            "methods": self["Allow"],
        }


class HttpResponseGone(HttpResponse):
    status_code = 410


class HttpResponseServerError(HttpResponse):
    status_code = 500


class Http404(Exception):
    pass


class JsonResponse(HttpResponse):
    

    def __init__(
        self,
        data,
        encoder=DjangoJSONEncoder,
        safe=True,
        json_dumps_params=None,
        **kwargs,
    ):
        if safe and not isinstance(data, dict):
            raise TypeError(
                "In order to allow non-dict objects to be serialized set the "
                "safe parameter to False."
            )
        if json_dumps_params is None:
            json_dumps_params = {}
        kwargs.setdefault("content_type", "application/json")
        data = json.dumps(data, cls=encoder, **json_dumps_params)
        super().__init__(content=data, **kwargs)