We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
RedirectResponse does not accept background argument
The [RedirectResponse](https://github.com/encode/starlette/blob/e57fa2a6b1b19dd118c26a21b2f5bd4dbe46c64a/starlette/responses.py#L174) class does not accept the `background` parameter.

My use case is I am submitting a form and then doing an `HTTP 303 See Other` ([ref](https://httpstatuses.com/303)), redirecting the user to a page specific to the output of the form. That page allows the user to download the processed data, once the background task completes it.

🤔 Perhaps this is intentional?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `starlette/responses.py`
Content:
```
1 import hashlib
2 import http.cookies
3 import inspect
4 import json
5 import os
6 import stat
7 import typing
8 from email.utils import formatdate
9 from mimetypes import guess_type
10 from urllib.parse import quote, quote_plus
11 
12 from starlette.background import BackgroundTask
13 from starlette.concurrency import iterate_in_threadpool, run_until_first_complete
14 from starlette.datastructures import URL, MutableHeaders
15 from starlette.types import Receive, Scope, Send
16 
17 # Workaround for adding samesite support to pre 3.8 python
18 http.cookies.Morsel._reserved["samesite"] = "SameSite"  # type: ignore
19 
20 try:
21     import aiofiles
22     from aiofiles.os import stat as aio_stat
23 except ImportError:  # pragma: nocover
24     aiofiles = None
25     aio_stat = None
26 
27 try:
28     import ujson
29 except ImportError:  # pragma: nocover
30     ujson = None  # type: ignore
31 
32 
33 class Response:
34     media_type = None
35     charset = "utf-8"
36 
37     def __init__(
38         self,
39         content: typing.Any = None,
40         status_code: int = 200,
41         headers: dict = None,
42         media_type: str = None,
43         background: BackgroundTask = None,
44     ) -> None:
45         self.body = self.render(content)
46         self.status_code = status_code
47         if media_type is not None:
48             self.media_type = media_type
49         self.background = background
50         self.init_headers(headers)
51 
52     def render(self, content: typing.Any) -> bytes:
53         if content is None:
54             return b""
55         if isinstance(content, bytes):
56             return content
57         return content.encode(self.charset)
58 
59     def init_headers(self, headers: typing.Mapping[str, str] = None) -> None:
60         if headers is None:
61             raw_headers = []  # type: typing.List[typing.Tuple[bytes, bytes]]
62             populate_content_length = True
63             populate_content_type = True
64         else:
65             raw_headers = [
66                 (k.lower().encode("latin-1"), v.encode("latin-1"))
67                 for k, v in headers.items()
68             ]
69             keys = [h[0] for h in raw_headers]
70             populate_content_length = b"content-length" not in keys
71             populate_content_type = b"content-type" not in keys
72 
73         body = getattr(self, "body", b"")
74         if body and populate_content_length:
75             content_length = str(len(body))
76             raw_headers.append((b"content-length", content_length.encode("latin-1")))
77 
78         content_type = self.media_type
79         if content_type is not None and populate_content_type:
80             if content_type.startswith("text/"):
81                 content_type += "; charset=" + self.charset
82             raw_headers.append((b"content-type", content_type.encode("latin-1")))
83 
84         self.raw_headers = raw_headers
85 
86     @property
87     def headers(self) -> MutableHeaders:
88         if not hasattr(self, "_headers"):
89             self._headers = MutableHeaders(raw=self.raw_headers)
90         return self._headers
91 
92     def set_cookie(
93         self,
94         key: str,
95         value: str = "",
96         max_age: int = None,
97         expires: int = None,
98         path: str = "/",
99         domain: str = None,
100         secure: bool = False,
101         httponly: bool = False,
102         samesite: str = "lax",
103     ) -> None:
104         cookie = http.cookies.SimpleCookie()  # type: http.cookies.BaseCookie
105         cookie[key] = value
106         if max_age is not None:
107             cookie[key]["max-age"] = max_age
108         if expires is not None:
109             cookie[key]["expires"] = expires
110         if path is not None:
111             cookie[key]["path"] = path
112         if domain is not None:
113             cookie[key]["domain"] = domain
114         if secure:
115             cookie[key]["secure"] = True
116         if httponly:
117             cookie[key]["httponly"] = True
118         if samesite is not None:
119             assert samesite.lower() in [
120                 "strict",
121                 "lax",
122                 "none",
123             ], "samesite must be either 'strict', 'lax' or 'none'"
124             cookie[key]["samesite"] = samesite
125         cookie_val = cookie.output(header="").strip()
126         self.raw_headers.append((b"set-cookie", cookie_val.encode("latin-1")))
127 
128     def delete_cookie(self, key: str, path: str = "/", domain: str = None) -> None:
129         self.set_cookie(key, expires=0, max_age=0, path=path, domain=domain)
130 
131     async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
132         await send(
133             {
134                 "type": "http.response.start",
135                 "status": self.status_code,
136                 "headers": self.raw_headers,
137             }
138         )
139         await send({"type": "http.response.body", "body": self.body})
140 
141         if self.background is not None:
142             await self.background()
143 
144 
145 class HTMLResponse(Response):
146     media_type = "text/html"
147 
148 
149 class PlainTextResponse(Response):
150     media_type = "text/plain"
151 
152 
153 class JSONResponse(Response):
154     media_type = "application/json"
155 
156     def render(self, content: typing.Any) -> bytes:
157         return json.dumps(
158             content,
159             ensure_ascii=False,
160             allow_nan=False,
161             indent=None,
162             separators=(",", ":"),
163         ).encode("utf-8")
164 
165 
166 class UJSONResponse(JSONResponse):
167     media_type = "application/json"
168 
169     def render(self, content: typing.Any) -> bytes:
170         assert ujson is not None, "ujson must be installed to use UJSONResponse"
171         return ujson.dumps(content, ensure_ascii=False).encode("utf-8")
172 
173 
174 class RedirectResponse(Response):
175     def __init__(
176         self, url: typing.Union[str, URL], status_code: int = 307, headers: dict = None
177     ) -> None:
178         super().__init__(content=b"", status_code=status_code, headers=headers)
179         self.headers["location"] = quote_plus(str(url), safe=":/%#?&=@[]!$&'()*+,;")
180 
181 
182 class StreamingResponse(Response):
183     def __init__(
184         self,
185         content: typing.Any,
186         status_code: int = 200,
187         headers: dict = None,
188         media_type: str = None,
189         background: BackgroundTask = None,
190     ) -> None:
191         if inspect.isasyncgen(content):
192             self.body_iterator = content
193         else:
194             self.body_iterator = iterate_in_threadpool(content)
195         self.status_code = status_code
196         self.media_type = self.media_type if media_type is None else media_type
197         self.background = background
198         self.init_headers(headers)
199 
200     async def listen_for_disconnect(self, receive: Receive) -> None:
201         while True:
202             message = await receive()
203             if message["type"] == "http.disconnect":
204                 break
205 
206     async def stream_response(self, send: Send) -> None:
207         await send(
208             {
209                 "type": "http.response.start",
210                 "status": self.status_code,
211                 "headers": self.raw_headers,
212             }
213         )
214         async for chunk in self.body_iterator:
215             if not isinstance(chunk, bytes):
216                 chunk = chunk.encode(self.charset)
217             await send({"type": "http.response.body", "body": chunk, "more_body": True})
218 
219         await send({"type": "http.response.body", "body": b"", "more_body": False})
220 
221     async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
222         await run_until_first_complete(
223             (self.stream_response, {"send": send}),
224             (self.listen_for_disconnect, {"receive": receive}),
225         )
226 
227         if self.background is not None:
228             await self.background()
229 
230 
231 class FileResponse(Response):
232     chunk_size = 4096
233 
234     def __init__(
235         self,
236         path: str,
237         status_code: int = 200,
238         headers: dict = None,
239         media_type: str = None,
240         background: BackgroundTask = None,
241         filename: str = None,
242         stat_result: os.stat_result = None,
243         method: str = None,
244     ) -> None:
245         assert aiofiles is not None, "'aiofiles' must be installed to use FileResponse"
246         self.path = path
247         self.status_code = status_code
248         self.filename = filename
249         self.send_header_only = method is not None and method.upper() == "HEAD"
250         if media_type is None:
251             media_type = guess_type(filename or path)[0] or "text/plain"
252         self.media_type = media_type
253         self.background = background
254         self.init_headers(headers)
255         if self.filename is not None:
256             content_disposition_filename = quote(self.filename)
257             if content_disposition_filename != self.filename:
258                 content_disposition = "attachment; filename*=utf-8''{}".format(
259                     content_disposition_filename
260                 )
261             else:
262                 content_disposition = 'attachment; filename="{}"'.format(self.filename)
263             self.headers.setdefault("content-disposition", content_disposition)
264         self.stat_result = stat_result
265         if stat_result is not None:
266             self.set_stat_headers(stat_result)
267 
268     def set_stat_headers(self, stat_result: os.stat_result) -> None:
269         content_length = str(stat_result.st_size)
270         last_modified = formatdate(stat_result.st_mtime, usegmt=True)
271         etag_base = str(stat_result.st_mtime) + "-" + str(stat_result.st_size)
272         etag = hashlib.md5(etag_base.encode()).hexdigest()
273 
274         self.headers.setdefault("content-length", content_length)
275         self.headers.setdefault("last-modified", last_modified)
276         self.headers.setdefault("etag", etag)
277 
278     async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
279         if self.stat_result is None:
280             try:
281                 stat_result = await aio_stat(self.path)
282                 self.set_stat_headers(stat_result)
283             except FileNotFoundError:
284                 raise RuntimeError(f"File at path {self.path} does not exist.")
285             else:
286                 mode = stat_result.st_mode
287                 if not stat.S_ISREG(mode):
288                     raise RuntimeError(f"File at path {self.path} is not a file.")
289         await send(
290             {
291                 "type": "http.response.start",
292                 "status": self.status_code,
293                 "headers": self.raw_headers,
294             }
295         )
296         if self.send_header_only:
297             await send({"type": "http.response.body", "body": b"", "more_body": False})
298         else:
299             async with aiofiles.open(self.path, mode="rb") as file:
300                 more_body = True
301                 while more_body:
302                     chunk = await file.read(self.chunk_size)
303                     more_body = len(chunk) == self.chunk_size
304                     await send(
305                         {
306                             "type": "http.response.body",
307                             "body": chunk,
308                             "more_body": more_body,
309                         }
310                     )
311         if self.background is not None:
312             await self.background()
313
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/starlette/responses.py b/starlette/responses.py
--- a/starlette/responses.py
+++ b/starlette/responses.py
@@ -173,9 +173,15 @@
 
 class RedirectResponse(Response):
     def __init__(
-        self, url: typing.Union[str, URL], status_code: int = 307, headers: dict = None
+        self,
+        url: typing.Union[str, URL],
+        status_code: int = 307,
+        headers: dict = None,
+        background: BackgroundTask = None,
     ) -> None:
-        super().__init__(content=b"", status_code=status_code, headers=headers)
+        super().__init__(
+            content=b"", status_code=status_code, headers=headers, background=background
+        )
         self.headers["location"] = quote_plus(str(url), safe=":/%#?&=@[]!$&'()*+,;")

{"golden_diff": "diff --git a/starlette/responses.py b/starlette/responses.py\n--- a/starlette/responses.py\n+++ b/starlette/responses.py\n@@ -173,9 +173,15 @@\n \n class RedirectResponse(Response):\n     def __init__(\n-        self, url: typing.Union[str, URL], status_code: int = 307, headers: dict = None\n+        self,\n+        url: typing.Union[str, URL],\n+        status_code: int = 307,\n+        headers: dict = None,\n+        background: BackgroundTask = None,\n     ) -> None:\n-        super().__init__(content=b\"\", status_code=status_code, headers=headers)\n+        super().__init__(\n+            content=b\"\", status_code=status_code, headers=headers, background=background\n+        )\n         self.headers[\"location\"] = quote_plus(str(url), safe=\":/%#?&=@[]!$&'()*+,;\")\n", "issue": "RedirectResponse does not accept background argument\nThe [RedirectResponse](https://github.com/encode/starlette/blob/e57fa2a6b1b19dd118c26a21b2f5bd4dbe46c64a/starlette/responses.py#L174) class does not accept the `background` parameter.\r\n\r\nMy use case is I am submitting a form and then doing an `HTTP 303 See Other` ([ref](https://httpstatuses.com/303)), redirecting the user to a page specific to the output of the form. That page allows the user to download the processed data, once the background task completes it.\r\n\r\n\ud83e\udd14 Perhaps this is intentional?\n", "before_files": [{"content": "import hashlib\nimport http.cookies\nimport inspect\nimport json\nimport os\nimport stat\nimport typing\nfrom email.utils import formatdate\nfrom mimetypes import guess_type\nfrom urllib.parse import quote, quote_plus\n\nfrom starlette.background import BackgroundTask\nfrom starlette.concurrency import iterate_in_threadpool, run_until_first_complete\nfrom starlette.datastructures import URL, MutableHeaders\nfrom starlette.types import Receive, Scope, Send\n\n# Workaround for adding samesite support to pre 3.8 python\nhttp.cookies.Morsel._reserved[\"samesite\"] = \"SameSite\"  # type: ignore\n\ntry:\n    import aiofiles\n    from aiofiles.os import stat as aio_stat\nexcept ImportError:  # pragma: nocover\n    aiofiles = None\n    aio_stat = None\n\ntry:\n    import ujson\nexcept ImportError:  # pragma: nocover\n    ujson = None  # type: ignore\n\n\nclass Response:\n    media_type = None\n    charset = \"utf-8\"\n\n    def __init__(\n        self,\n        content: typing.Any = None,\n        status_code: int = 200,\n        headers: dict = None,\n        media_type: str = None,\n        background: BackgroundTask = None,\n    ) -> None:\n        self.body = self.render(content)\n        self.status_code = status_code\n        if media_type is not None:\n            self.media_type = media_type\n        self.background = background\n        self.init_headers(headers)\n\n    def render(self, content: typing.Any) -> bytes:\n        if content is None:\n            return b\"\"\n        if isinstance(content, bytes):\n            return content\n        return content.encode(self.charset)\n\n    def init_headers(self, headers: typing.Mapping[str, str] = None) -> None:\n        if headers is None:\n            raw_headers = []  # type: typing.List[typing.Tuple[bytes, bytes]]\n            populate_content_length = True\n            populate_content_type = True\n        else:\n            raw_headers = [\n                (k.lower().encode(\"latin-1\"), v.encode(\"latin-1\"))\n                for k, v in headers.items()\n            ]\n            keys = [h[0] for h in raw_headers]\n            populate_content_length = b\"content-length\" not in keys\n            populate_content_type = b\"content-type\" not in keys\n\n        body = getattr(self, \"body\", b\"\")\n        if body and populate_content_length:\n            content_length = str(len(body))\n            raw_headers.append((b\"content-length\", content_length.encode(\"latin-1\")))\n\n        content_type = self.media_type\n        if content_type is not None and populate_content_type:\n            if content_type.startswith(\"text/\"):\n                content_type += \"; charset=\" + self.charset\n            raw_headers.append((b\"content-type\", content_type.encode(\"latin-1\")))\n\n        self.raw_headers = raw_headers\n\n    @property\n    def headers(self) -> MutableHeaders:\n        if not hasattr(self, \"_headers\"):\n            self._headers = MutableHeaders(raw=self.raw_headers)\n        return self._headers\n\n    def set_cookie(\n        self,\n        key: str,\n        value: str = \"\",\n        max_age: int = None,\n        expires: int = None,\n        path: str = \"/\",\n        domain: str = None,\n        secure: bool = False,\n        httponly: bool = False,\n        samesite: str = \"lax\",\n    ) -> None:\n        cookie = http.cookies.SimpleCookie()  # type: http.cookies.BaseCookie\n        cookie[key] = value\n        if max_age is not None:\n            cookie[key][\"max-age\"] = max_age\n        if expires is not None:\n            cookie[key][\"expires\"] = expires\n        if path is not None:\n            cookie[key][\"path\"] = path\n        if domain is not None:\n            cookie[key][\"domain\"] = domain\n        if secure:\n            cookie[key][\"secure\"] = True\n        if httponly:\n            cookie[key][\"httponly\"] = True\n        if samesite is not None:\n            assert samesite.lower() in [\n                \"strict\",\n                \"lax\",\n                \"none\",\n            ], \"samesite must be either 'strict', 'lax' or 'none'\"\n            cookie[key][\"samesite\"] = samesite\n        cookie_val = cookie.output(header=\"\").strip()\n        self.raw_headers.append((b\"set-cookie\", cookie_val.encode(\"latin-1\")))\n\n    def delete_cookie(self, key: str, path: str = \"/\", domain: str = None) -> None:\n        self.set_cookie(key, expires=0, max_age=0, path=path, domain=domain)\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        await send(\n            {\n                \"type\": \"http.response.start\",\n                \"status\": self.status_code,\n                \"headers\": self.raw_headers,\n            }\n        )\n        await send({\"type\": \"http.response.body\", \"body\": self.body})\n\n        if self.background is not None:\n            await self.background()\n\n\nclass HTMLResponse(Response):\n    media_type = \"text/html\"\n\n\nclass PlainTextResponse(Response):\n    media_type = \"text/plain\"\n\n\nclass JSONResponse(Response):\n    media_type = \"application/json\"\n\n    def render(self, content: typing.Any) -> bytes:\n        return json.dumps(\n            content,\n            ensure_ascii=False,\n            allow_nan=False,\n            indent=None,\n            separators=(\",\", \":\"),\n        ).encode(\"utf-8\")\n\n\nclass UJSONResponse(JSONResponse):\n    media_type = \"application/json\"\n\n    def render(self, content: typing.Any) -> bytes:\n        assert ujson is not None, \"ujson must be installed to use UJSONResponse\"\n        return ujson.dumps(content, ensure_ascii=False).encode(\"utf-8\")\n\n\nclass RedirectResponse(Response):\n    def __init__(\n        self, url: typing.Union[str, URL], status_code: int = 307, headers: dict = None\n    ) -> None:\n        super().__init__(content=b\"\", status_code=status_code, headers=headers)\n        self.headers[\"location\"] = quote_plus(str(url), safe=\":/%#?&=@[]!$&'()*+,;\")\n\n\nclass StreamingResponse(Response):\n    def __init__(\n        self,\n        content: typing.Any,\n        status_code: int = 200,\n        headers: dict = None,\n        media_type: str = None,\n        background: BackgroundTask = None,\n    ) -> None:\n        if inspect.isasyncgen(content):\n            self.body_iterator = content\n        else:\n            self.body_iterator = iterate_in_threadpool(content)\n        self.status_code = status_code\n        self.media_type = self.media_type if media_type is None else media_type\n        self.background = background\n        self.init_headers(headers)\n\n    async def listen_for_disconnect(self, receive: Receive) -> None:\n        while True:\n            message = await receive()\n            if message[\"type\"] == \"http.disconnect\":\n                break\n\n    async def stream_response(self, send: Send) -> None:\n        await send(\n            {\n                \"type\": \"http.response.start\",\n                \"status\": self.status_code,\n                \"headers\": self.raw_headers,\n            }\n        )\n        async for chunk in self.body_iterator:\n            if not isinstance(chunk, bytes):\n                chunk = chunk.encode(self.charset)\n            await send({\"type\": \"http.response.body\", \"body\": chunk, \"more_body\": True})\n\n        await send({\"type\": \"http.response.body\", \"body\": b\"\", \"more_body\": False})\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        await run_until_first_complete(\n            (self.stream_response, {\"send\": send}),\n            (self.listen_for_disconnect, {\"receive\": receive}),\n        )\n\n        if self.background is not None:\n            await self.background()\n\n\nclass FileResponse(Response):\n    chunk_size = 4096\n\n    def __init__(\n        self,\n        path: str,\n        status_code: int = 200,\n        headers: dict = None,\n        media_type: str = None,\n        background: BackgroundTask = None,\n        filename: str = None,\n        stat_result: os.stat_result = None,\n        method: str = None,\n    ) -> None:\n        assert aiofiles is not None, \"'aiofiles' must be installed to use FileResponse\"\n        self.path = path\n        self.status_code = status_code\n        self.filename = filename\n        self.send_header_only = method is not None and method.upper() == \"HEAD\"\n        if media_type is None:\n            media_type = guess_type(filename or path)[0] or \"text/plain\"\n        self.media_type = media_type\n        self.background = background\n        self.init_headers(headers)\n        if self.filename is not None:\n            content_disposition_filename = quote(self.filename)\n            if content_disposition_filename != self.filename:\n                content_disposition = \"attachment; filename*=utf-8''{}\".format(\n                    content_disposition_filename\n                )\n            else:\n                content_disposition = 'attachment; filename=\"{}\"'.format(self.filename)\n            self.headers.setdefault(\"content-disposition\", content_disposition)\n        self.stat_result = stat_result\n        if stat_result is not None:\n            self.set_stat_headers(stat_result)\n\n    def set_stat_headers(self, stat_result: os.stat_result) -> None:\n        content_length = str(stat_result.st_size)\n        last_modified = formatdate(stat_result.st_mtime, usegmt=True)\n        etag_base = str(stat_result.st_mtime) + \"-\" + str(stat_result.st_size)\n        etag = hashlib.md5(etag_base.encode()).hexdigest()\n\n        self.headers.setdefault(\"content-length\", content_length)\n        self.headers.setdefault(\"last-modified\", last_modified)\n        self.headers.setdefault(\"etag\", etag)\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        if self.stat_result is None:\n            try:\n                stat_result = await aio_stat(self.path)\n                self.set_stat_headers(stat_result)\n            except FileNotFoundError:\n                raise RuntimeError(f\"File at path {self.path} does not exist.\")\n            else:\n                mode = stat_result.st_mode\n                if not stat.S_ISREG(mode):\n                    raise RuntimeError(f\"File at path {self.path} is not a file.\")\n        await send(\n            {\n                \"type\": \"http.response.start\",\n                \"status\": self.status_code,\n                \"headers\": self.raw_headers,\n            }\n        )\n        if self.send_header_only:\n            await send({\"type\": \"http.response.body\", \"body\": b\"\", \"more_body\": False})\n        else:\n            async with aiofiles.open(self.path, mode=\"rb\") as file:\n                more_body = True\n                while more_body:\n                    chunk = await file.read(self.chunk_size)\n                    more_body = len(chunk) == self.chunk_size\n                    await send(\n                        {\n                            \"type\": \"http.response.body\",\n                            \"body\": chunk,\n                            \"more_body\": more_body,\n                        }\n                    )\n        if self.background is not None:\n            await self.background()\n", "path": "starlette/responses.py"}], "after_files": [{"content": "import hashlib\nimport http.cookies\nimport inspect\nimport json\nimport os\nimport stat\nimport typing\nfrom email.utils import formatdate\nfrom mimetypes import guess_type\nfrom urllib.parse import quote, quote_plus\n\nfrom starlette.background import BackgroundTask\nfrom starlette.concurrency import iterate_in_threadpool, run_until_first_complete\nfrom starlette.datastructures import URL, MutableHeaders\nfrom starlette.types import Receive, Scope, Send\n\n# Workaround for adding samesite support to pre 3.8 python\nhttp.cookies.Morsel._reserved[\"samesite\"] = \"SameSite\"  # type: ignore\n\ntry:\n    import aiofiles\n    from aiofiles.os import stat as aio_stat\nexcept ImportError:  # pragma: nocover\n    aiofiles = None\n    aio_stat = None\n\ntry:\n    import ujson\nexcept ImportError:  # pragma: nocover\n    ujson = None  # type: ignore\n\n\nclass Response:\n    media_type = None\n    charset = \"utf-8\"\n\n    def __init__(\n        self,\n        content: typing.Any = None,\n        status_code: int = 200,\n        headers: dict = None,\n        media_type: str = None,\n        background: BackgroundTask = None,\n    ) -> None:\n        self.body = self.render(content)\n        self.status_code = status_code\n        if media_type is not None:\n            self.media_type = media_type\n        self.background = background\n        self.init_headers(headers)\n\n    def render(self, content: typing.Any) -> bytes:\n        if content is None:\n            return b\"\"\n        if isinstance(content, bytes):\n            return content\n        return content.encode(self.charset)\n\n    def init_headers(self, headers: typing.Mapping[str, str] = None) -> None:\n        if headers is None:\n            raw_headers = []  # type: typing.List[typing.Tuple[bytes, bytes]]\n            populate_content_length = True\n            populate_content_type = True\n        else:\n            raw_headers = [\n                (k.lower().encode(\"latin-1\"), v.encode(\"latin-1\"))\n                for k, v in headers.items()\n            ]\n            keys = [h[0] for h in raw_headers]\n            populate_content_length = b\"content-length\" not in keys\n            populate_content_type = b\"content-type\" not in keys\n\n        body = getattr(self, \"body\", b\"\")\n        if body and populate_content_length:\n            content_length = str(len(body))\n            raw_headers.append((b\"content-length\", content_length.encode(\"latin-1\")))\n\n        content_type = self.media_type\n        if content_type is not None and populate_content_type:\n            if content_type.startswith(\"text/\"):\n                content_type += \"; charset=\" + self.charset\n            raw_headers.append((b\"content-type\", content_type.encode(\"latin-1\")))\n\n        self.raw_headers = raw_headers\n\n    @property\n    def headers(self) -> MutableHeaders:\n        if not hasattr(self, \"_headers\"):\n            self._headers = MutableHeaders(raw=self.raw_headers)\n        return self._headers\n\n    def set_cookie(\n        self,\n        key: str,\n        value: str = \"\",\n        max_age: int = None,\n        expires: int = None,\n        path: str = \"/\",\n        domain: str = None,\n        secure: bool = False,\n        httponly: bool = False,\n        samesite: str = \"lax\",\n    ) -> None:\n        cookie = http.cookies.SimpleCookie()  # type: http.cookies.BaseCookie\n        cookie[key] = value\n        if max_age is not None:\n            cookie[key][\"max-age\"] = max_age\n        if expires is not None:\n            cookie[key][\"expires\"] = expires\n        if path is not None:\n            cookie[key][\"path\"] = path\n        if domain is not None:\n            cookie[key][\"domain\"] = domain\n        if secure:\n            cookie[key][\"secure\"] = True\n        if httponly:\n            cookie[key][\"httponly\"] = True\n        if samesite is not None:\n            assert samesite.lower() in [\n                \"strict\",\n                \"lax\",\n                \"none\",\n            ], \"samesite must be either 'strict', 'lax' or 'none'\"\n            cookie[key][\"samesite\"] = samesite\n        cookie_val = cookie.output(header=\"\").strip()\n        self.raw_headers.append((b\"set-cookie\", cookie_val.encode(\"latin-1\")))\n\n    def delete_cookie(self, key: str, path: str = \"/\", domain: str = None) -> None:\n        self.set_cookie(key, expires=0, max_age=0, path=path, domain=domain)\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        await send(\n            {\n                \"type\": \"http.response.start\",\n                \"status\": self.status_code,\n                \"headers\": self.raw_headers,\n            }\n        )\n        await send({\"type\": \"http.response.body\", \"body\": self.body})\n\n        if self.background is not None:\n            await self.background()\n\n\nclass HTMLResponse(Response):\n    media_type = \"text/html\"\n\n\nclass PlainTextResponse(Response):\n    media_type = \"text/plain\"\n\n\nclass JSONResponse(Response):\n    media_type = \"application/json\"\n\n    def render(self, content: typing.Any) -> bytes:\n        return json.dumps(\n            content,\n            ensure_ascii=False,\n            allow_nan=False,\n            indent=None,\n            separators=(\",\", \":\"),\n        ).encode(\"utf-8\")\n\n\nclass UJSONResponse(JSONResponse):\n    media_type = \"application/json\"\n\n    def render(self, content: typing.Any) -> bytes:\n        assert ujson is not None, \"ujson must be installed to use UJSONResponse\"\n        return ujson.dumps(content, ensure_ascii=False).encode(\"utf-8\")\n\n\nclass RedirectResponse(Response):\n    def __init__(\n        self,\n        url: typing.Union[str, URL],\n        status_code: int = 307,\n        headers: dict = None,\n        background: BackgroundTask = None,\n    ) -> None:\n        super().__init__(\n            content=b\"\", status_code=status_code, headers=headers, background=background\n        )\n        self.headers[\"location\"] = quote_plus(str(url), safe=\":/%#?&=@[]!$&'()*+,;\")\n\n\nclass StreamingResponse(Response):\n    def __init__(\n        self,\n        content: typing.Any,\n        status_code: int = 200,\n        headers: dict = None,\n        media_type: str = None,\n        background: BackgroundTask = None,\n    ) -> None:\n        if inspect.isasyncgen(content):\n            self.body_iterator = content\n        else:\n            self.body_iterator = iterate_in_threadpool(content)\n        self.status_code = status_code\n        self.media_type = self.media_type if media_type is None else media_type\n        self.background = background\n        self.init_headers(headers)\n\n    async def listen_for_disconnect(self, receive: Receive) -> None:\n        while True:\n            message = await receive()\n            if message[\"type\"] == \"http.disconnect\":\n                break\n\n    async def stream_response(self, send: Send) -> None:\n        await send(\n            {\n                \"type\": \"http.response.start\",\n                \"status\": self.status_code,\n                \"headers\": self.raw_headers,\n            }\n        )\n        async for chunk in self.body_iterator:\n            if not isinstance(chunk, bytes):\n                chunk = chunk.encode(self.charset)\n            await send({\"type\": \"http.response.body\", \"body\": chunk, \"more_body\": True})\n\n        await send({\"type\": \"http.response.body\", \"body\": b\"\", \"more_body\": False})\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        await run_until_first_complete(\n            (self.stream_response, {\"send\": send}),\n            (self.listen_for_disconnect, {\"receive\": receive}),\n        )\n\n        if self.background is not None:\n            await self.background()\n\n\nclass FileResponse(Response):\n    chunk_size = 4096\n\n    def __init__(\n        self,\n        path: str,\n        status_code: int = 200,\n        headers: dict = None,\n        media_type: str = None,\n        background: BackgroundTask = None,\n        filename: str = None,\n        stat_result: os.stat_result = None,\n        method: str = None,\n    ) -> None:\n        assert aiofiles is not None, \"'aiofiles' must be installed to use FileResponse\"\n        self.path = path\n        self.status_code = status_code\n        self.filename = filename\n        self.send_header_only = method is not None and method.upper() == \"HEAD\"\n        if media_type is None:\n            media_type = guess_type(filename or path)[0] or \"text/plain\"\n        self.media_type = media_type\n        self.background = background\n        self.init_headers(headers)\n        if self.filename is not None:\n            content_disposition_filename = quote(self.filename)\n            if content_disposition_filename != self.filename:\n                content_disposition = \"attachment; filename*=utf-8''{}\".format(\n                    content_disposition_filename\n                )\n            else:\n                content_disposition = 'attachment; filename=\"{}\"'.format(self.filename)\n            self.headers.setdefault(\"content-disposition\", content_disposition)\n        self.stat_result = stat_result\n        if stat_result is not None:\n            self.set_stat_headers(stat_result)\n\n    def set_stat_headers(self, stat_result: os.stat_result) -> None:\n        content_length = str(stat_result.st_size)\n        last_modified = formatdate(stat_result.st_mtime, usegmt=True)\n        etag_base = str(stat_result.st_mtime) + \"-\" + str(stat_result.st_size)\n        etag = hashlib.md5(etag_base.encode()).hexdigest()\n\n        self.headers.setdefault(\"content-length\", content_length)\n        self.headers.setdefault(\"last-modified\", last_modified)\n        self.headers.setdefault(\"etag\", etag)\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        if self.stat_result is None:\n            try:\n                stat_result = await aio_stat(self.path)\n                self.set_stat_headers(stat_result)\n            except FileNotFoundError:\n                raise RuntimeError(f\"File at path {self.path} does not exist.\")\n            else:\n                mode = stat_result.st_mode\n                if not stat.S_ISREG(mode):\n                    raise RuntimeError(f\"File at path {self.path} is not a file.\")\n        await send(\n            {\n                \"type\": \"http.response.start\",\n                \"status\": self.status_code,\n                \"headers\": self.raw_headers,\n            }\n        )\n        if self.send_header_only:\n            await send({\"type\": \"http.response.body\", \"body\": b\"\", \"more_body\": False})\n        else:\n            async with aiofiles.open(self.path, mode=\"rb\") as file:\n                more_body = True\n                while more_body:\n                    chunk = await file.read(self.chunk_size)\n                    more_body = len(chunk) == self.chunk_size\n                    await send(\n                        {\n                            \"type\": \"http.response.body\",\n                            \"body\": chunk,\n                            \"more_body\": more_body,\n                        }\n                    )\n        if self.background is not None:\n            await self.background()\n", "path": "starlette/responses.py"}]}