@@ -34,15 +34,18 @@ class HTTPResponse:
34
34
def route_func(request):
35
35
response = HTTPResponse(request)
36
36
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")
37
42
38
43
# Response with 'Transfer-Encoding: chunked' header
39
44
@server.route(path, method)
40
45
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")
46
49
"""
47
50
48
51
request : HTTPRequest
@@ -51,47 +54,54 @@ def route_func(request):
51
54
http_version : str
52
55
status : HTTPStatus
53
56
headers : HTTPHeaders
57
+ content_type : str
54
58
55
59
def __init__ ( # pylint: disable=too-many-arguments
56
60
self ,
57
61
request : HTTPRequest ,
58
62
status : Union [HTTPStatus , Tuple [int , str ]] = CommonHTTPStatus .OK_200 ,
59
63
headers : Union [HTTPHeaders , Dict [str , str ]] = None ,
64
+ content_type : str = MIMEType .TYPE_TXT ,
60
65
http_version : str = "HTTP/1.1" ,
66
+ chunked : bool = False ,
61
67
) -> None :
62
68
"""
63
69
Creates an HTTP response.
64
70
65
71
Sets `status`, ``headers`` and `http_version`.
66
72
67
73
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`.
69
76
"""
70
77
self .request = request
71
78
self .status = status if isinstance (status , HTTPStatus ) else HTTPStatus (* status )
72
79
self .headers = (
73
80
headers .copy () if isinstance (headers , HTTPHeaders ) else HTTPHeaders (headers )
74
81
)
82
+ self .content_type = content_type
75
83
self .http_version = http_version
84
+ self .chunked = chunked
76
85
77
- def send_headers (
86
+ def _send_headers (
78
87
self ,
79
88
content_length : Optional [int ] = None ,
80
89
content_type : str = MIMEType .TYPE_TXT ,
81
- chunked : bool = False ,
82
90
) -> None :
83
91
"""
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.
85
95
"""
86
96
headers = self .headers .copy ()
87
97
88
98
response_message_header = (
89
99
f"{ self .http_version } { self .status .code } { self .status .text } \r \n "
90
100
)
91
101
92
- headers .setdefault ("Content-Type" , content_type )
102
+ headers .setdefault ("Content-Type" , content_type or self . content_type )
93
103
headers .setdefault ("Connection" , "close" )
94
- if chunked :
104
+ if self . chunked :
95
105
headers .setdefault ("Transfer-Encoding" , "chunked" )
96
106
else :
97
107
headers .setdefault ("Content-Length" , content_length )
@@ -107,16 +117,17 @@ def send_headers(
107
117
def send (
108
118
self ,
109
119
body : str = "" ,
110
- content_type : str = MIMEType . TYPE_TXT ,
120
+ content_type : str = None ,
111
121
) -> None :
112
122
"""
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.
115
126
"""
116
127
encoded_response_message_body = body .encode ("utf-8" )
117
128
118
- self .send_headers (
119
- content_type = content_type ,
129
+ self ._send_headers (
130
+ content_type = content_type or self . content_type ,
120
131
content_length = len (encoded_response_message_body ),
121
132
)
122
133
self ._send_bytes (self .request .connection , encoded_response_message_body )
@@ -138,7 +149,7 @@ def send_file(
138
149
HTTPResponse (self .request , status = CommonHTTPStatus .NOT_FOUND_404 ).send ()
139
150
return
140
151
141
- self .send_headers (
152
+ self ._send_headers (
142
153
content_type = MIMEType .from_file_name (filename ),
143
154
content_length = file_length ,
144
155
)
@@ -151,7 +162,8 @@ def send_chunk_body(self, chunk: str = "") -> None:
151
162
"""
152
163
Send chunk of data to the given socket.
153
164
154
- Call without `chunk` to finish the session.
165
+ Should be used only inside
166
+ ``with HTTPResponse(request, chunked=True) as response:`` context manager.
155
167
156
168
:param str chunk: String data to be sent.
157
169
"""
@@ -161,6 +173,16 @@ def send_chunk_body(self, chunk: str = "") -> None:
161
173
self .request .connection , f"{ hex_length } \r \n { chunk } \r \n " .encode ("utf-8" )
162
174
)
163
175
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
+
164
186
@staticmethod
165
187
def _send_bytes (
166
188
conn : Union ["SocketPool.Socket" , "socket.socket" ],
0 commit comments