Skip to content

Commit 2ca4756

Browse files
committed
Changed HTTPResponse to use context managers
1 parent 00d3247 commit 2ca4756

File tree

1 file changed

+40
-18
lines changed

1 file changed

+40
-18
lines changed

adafruit_httpserver/response.py

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,18 @@ class HTTPResponse:
3434
def route_func(request):
3535
response = HTTPResponse(request)
3636
response.send("Some content", content_type="text/plain")
37+
# or
38+
@server.route(path, method)
39+
def route_func(request):
40+
with HTTPResponse(request) as response:
41+
response.send("Some content", content_type="text/plain")
3742
3843
# Response with 'Transfer-Encoding: chunked' header
3944
@server.route(path, method)
4045
def route_func(request):
41-
response = HTTPResponse(request, content_type="text/html")
42-
response.send_headers(content_type="text/plain", chunked=True)
43-
response.send_body_chunk("Some content")
44-
response.send_body_chunk("Some more content")
45-
response.send_body_chunk("") # Send empty packet to finish chunked stream
46+
with HTTPResponse(request, content_type="text/plain", chunked=True) as response:
47+
response.send_body_chunk("Some content")
48+
response.send_body_chunk("Some more content")
4649
"""
4750

4851
request: HTTPRequest
@@ -51,47 +54,54 @@ def route_func(request):
5154
http_version: str
5255
status: HTTPStatus
5356
headers: HTTPHeaders
57+
content_type: str
5458

5559
def __init__( # pylint: disable=too-many-arguments
5660
self,
5761
request: HTTPRequest,
5862
status: Union[HTTPStatus, Tuple[int, str]] = CommonHTTPStatus.OK_200,
5963
headers: Union[HTTPHeaders, Dict[str, str]] = None,
64+
content_type: str = MIMEType.TYPE_TXT,
6065
http_version: str = "HTTP/1.1",
66+
chunked: bool = False,
6167
) -> None:
6268
"""
6369
Creates an HTTP response.
6470
6571
Sets `status`, ``headers`` and `http_version`.
6672
6773
To send the response, call `send` or `send_file`.
68-
For chunked response ``send_headers(chunked=True)`` and then `send_chunk_body`.
74+
For chunked response use
75+
``with HTTPRequest(request, content_type=..., chunked=True) as r:`` and `send_chunk_body`.
6976
"""
7077
self.request = request
7178
self.status = status if isinstance(status, HTTPStatus) else HTTPStatus(*status)
7279
self.headers = (
7380
headers.copy() if isinstance(headers, HTTPHeaders) else HTTPHeaders(headers)
7481
)
82+
self.content_type = content_type
7583
self.http_version = http_version
84+
self.chunked = chunked
7685

77-
def send_headers(
86+
def _send_headers(
7887
self,
7988
content_length: Optional[int] = None,
8089
content_type: str = MIMEType.TYPE_TXT,
81-
chunked: bool = False,
8290
) -> None:
8391
"""
84-
Send response with `body` over the given socket.
92+
Sends headers.
93+
Implicitly called by `send` and `send_file` and in
94+
``with HTTPResponse(request, chunked=True) as response:`` context manager.
8595
"""
8696
headers = self.headers.copy()
8797

8898
response_message_header = (
8999
f"{self.http_version} {self.status.code} {self.status.text}\r\n"
90100
)
91101

92-
headers.setdefault("Content-Type", content_type)
102+
headers.setdefault("Content-Type", content_type or self.content_type)
93103
headers.setdefault("Connection", "close")
94-
if chunked:
104+
if self.chunked:
95105
headers.setdefault("Transfer-Encoding", "chunked")
96106
else:
97107
headers.setdefault("Content-Length", content_length)
@@ -107,16 +117,17 @@ def send_headers(
107117
def send(
108118
self,
109119
body: str = "",
110-
content_type: str = MIMEType.TYPE_TXT,
120+
content_type: str = None,
111121
) -> None:
112122
"""
113-
Send response with `body` over the given socket.
114-
Implicitly calls `send_headers` before sending the body.
123+
Sends response with `body` over the given socket.
124+
Implicitly calls ``_send_headers`` before sending the body.
125+
Should be called only once per response.
115126
"""
116127
encoded_response_message_body = body.encode("utf-8")
117128

118-
self.send_headers(
119-
content_type=content_type,
129+
self._send_headers(
130+
content_type=content_type or self.content_type,
120131
content_length=len(encoded_response_message_body),
121132
)
122133
self._send_bytes(self.request.connection, encoded_response_message_body)
@@ -138,7 +149,7 @@ def send_file(
138149
HTTPResponse(self.request, status=CommonHTTPStatus.NOT_FOUND_404).send()
139150
return
140151

141-
self.send_headers(
152+
self._send_headers(
142153
content_type=MIMEType.from_file_name(filename),
143154
content_length=file_length,
144155
)
@@ -151,7 +162,8 @@ def send_chunk_body(self, chunk: str = "") -> None:
151162
"""
152163
Send chunk of data to the given socket.
153164
154-
Call without `chunk` to finish the session.
165+
Should be used only inside
166+
``with HTTPResponse(request, chunked=True) as response:`` context manager.
155167
156168
:param str chunk: String data to be sent.
157169
"""
@@ -161,6 +173,16 @@ def send_chunk_body(self, chunk: str = "") -> None:
161173
self.request.connection, f"{hex_length}\r\n{chunk}\r\n".encode("utf-8")
162174
)
163175

176+
def __enter__(self):
177+
if self.chunked:
178+
self._send_headers()
179+
return self
180+
181+
def __exit__(self, *args, **kwargs):
182+
if self.chunked:
183+
self.send_chunk_body("")
184+
return True
185+
164186
@staticmethod
165187
def _send_bytes(
166188
conn: Union["SocketPool.Socket", "socket.socket"],

0 commit comments

Comments
 (0)