@@ -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