8
8
"""
9
9
10
10
try :
11
- from typing import Optional , Dict , Union , Tuple
11
+ from typing import Optional , Dict , Union , Tuple , Callable
12
12
from socket import socket
13
13
from socketpool import SocketPool
14
14
except ImportError :
21
21
BackslashInPathError ,
22
22
FileNotExistsError ,
23
23
ParentDirectoryReferenceError ,
24
- ResponseAlreadySentException ,
24
+ ResponseAlreadySentError ,
25
25
)
26
26
from .mime_type import MIMEType
27
27
from .request import HTTPRequest
28
28
from .status import HTTPStatus , CommonHTTPStatus
29
29
from .headers import HTTPHeaders
30
30
31
31
32
+ def _prevent_multiple_send_calls (function : Callable ):
33
+ """
34
+ Decorator that prevents calling ``send`` or ``send_file`` more than once.
35
+ """
36
+
37
+ def wrapper (self : "HTTPResponse" , * args , ** kwargs ):
38
+ if self ._response_already_sent : # pylint: disable=protected-access
39
+ raise ResponseAlreadySentError
40
+
41
+ result = function (self , * args , ** kwargs )
42
+ return result
43
+
44
+ return wrapper
45
+
46
+
32
47
class HTTPResponse :
33
48
"""
34
49
Response to a given `HTTPRequest`. Use in `HTTPServer.route` decorator functions.
@@ -79,8 +94,8 @@ def route_func(request):
79
94
"""
80
95
Defaults to ``text/plain`` if not set.
81
96
82
- Can be explicitly provided in the constructor, in `send()` or
83
- implicitly determined from filename in `send_file()`.
97
+ Can be explicitly provided in the constructor, in `` send()` ` or
98
+ implicitly determined from filename in `` send_file()` `.
84
99
85
100
Common MIME types are defined in `adafruit_httpserver.mime_type.MIMEType`.
86
101
"""
@@ -100,7 +115,7 @@ def __init__( # pylint: disable=too-many-arguments
100
115
Sets `status`, ``headers`` and `http_version`
101
116
and optionally default ``content_type``.
102
117
103
- To send the response, call `send` or `send_file`.
118
+ To send the response, call `` send`` or `` send_file` `.
104
119
For chunked response use
105
120
``with HTTPRequest(request, content_type=..., chunked=True) as r:`` and `send_chunk`.
106
121
"""
@@ -121,7 +136,7 @@ def _send_headers(
121
136
) -> None :
122
137
"""
123
138
Sends headers.
124
- Implicitly called by `send` and `send_file` and in
139
+ Implicitly called by `` send`` and `` send_file` ` and in
125
140
``with HTTPResponse(request, chunked=True) as response:`` context manager.
126
141
"""
127
142
headers = self .headers .copy ()
@@ -147,6 +162,7 @@ def _send_headers(
147
162
self .request .connection , response_message_header .encode ("utf-8" )
148
163
)
149
164
165
+ @_prevent_multiple_send_calls
150
166
def send (
151
167
self ,
152
168
body : str = "" ,
@@ -158,8 +174,6 @@ def send(
158
174
159
175
Should be called **only once** per response.
160
176
"""
161
- if self ._response_already_sent :
162
- raise ResponseAlreadySentException
163
177
164
178
if getattr (body , "encode" , None ):
165
179
encoded_response_message_body = body .encode ("utf-8" )
@@ -200,6 +214,7 @@ def _get_file_length(file_path: str) -> int:
200
214
except OSError :
201
215
raise FileNotExistsError (file_path ) # pylint: disable=raise-missing-from
202
216
217
+ @_prevent_multiple_send_calls
203
218
def send_file (
204
219
self ,
205
220
filename : str = "index.html" ,
@@ -214,8 +229,6 @@ def send_file(
214
229
215
230
Should be called **only once** per response.
216
231
"""
217
- if self ._response_already_sent :
218
- raise ResponseAlreadySentException
219
232
220
233
if safe :
221
234
self ._check_file_path_is_valid (filename )
0 commit comments