Coverage for faststream / asgi / response.py: 97%
24 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
1from collections.abc import Mapping
2from typing import TYPE_CHECKING, Any
4from faststream._internal._compat import json_dumps
6if TYPE_CHECKING:
7 from .types import Receive, Scope, Send
10class AsgiResponse:
11 def __init__(
12 self,
13 body: bytes = b"",
14 status_code: int = 200,
15 headers: Mapping[str, str] | None = None,
16 ) -> None:
17 self.status_code = status_code
18 self.body = body
19 self.raw_headers = _get_response_headers(body, headers, status_code)
21 def __repr__(self) -> str:
22 inner = [f"status_code={self.status_code}"]
23 if (ln := len(self.body)) > 100:
24 inner.append(
25 f"body={self.body[:100]!r}".rstrip(" \\n'") + f" ...' ({ln} bytes)"
26 )
27 else:
28 inner.append(f"body={self.body!r}")
29 inner.append(f"headers={self.raw_headers}")
30 return f"{self.__class__.__name__}({', '.join(inner)})"
32 async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None:
33 prefix = "websocket." if (scope["type"] == "websocket") else ""
34 await send(
35 {
36 "type": f"{prefix}http.response.start",
37 "status": self.status_code,
38 "headers": self.raw_headers,
39 },
40 )
41 await send(
42 {
43 "type": f"{prefix}http.response.body",
44 "body": self.body,
45 },
46 )
49def JSONResponse( # noqa: N802
50 data: Any,
51 status_code: int = 200,
52 headers: Mapping[str, str] | None = None,
53) -> AsgiResponse:
54 if not isinstance(data, bytes): 54 ↛ 57line 54 didn't jump to line 57 because the condition on line 54 was always true
55 data = json_dumps(data)
57 return AsgiResponse(
58 data,
59 status_code,
60 {"Content-Type": "application/json", **(headers or {})},
61 )
64def _get_response_headers(
65 body: bytes,
66 headers: Mapping[str, str] | None,
67 status_code: int,
68) -> list[tuple[bytes, bytes]]:
69 if headers is None:
70 raw_headers: list[tuple[bytes, bytes]] = []
71 populate_content_length = True
73 else:
74 raw_headers = [
75 (k.lower().encode("latin-1"), v.encode("latin-1")) for k, v in headers.items()
76 ]
77 keys = [h[0] for h in raw_headers]
78 populate_content_length = b"content-length" not in keys
80 if (
81 body
82 and populate_content_length
83 and not (status_code < 200 or status_code in {204, 304})
84 ):
85 content_length = str(len(body))
86 raw_headers.append((b"content-length", content_length.encode("latin-1")))
88 return raw_headers