88"""
99
1010try :
11- from typing import Optional , Dict , Union , Tuple
11+ from typing import Optional , Dict , Union , Tuple , Callable
1212 from socket import socket
1313 from socketpool import SocketPool
1414except ImportError :
2121 BackslashInPathError ,
2222 FileNotExistsError ,
2323 ParentDirectoryReferenceError ,
24- ResponseAlreadySentException ,
24+ ResponseAlreadySentError ,
2525)
2626from .mime_type import MIMEType
2727from .request import HTTPRequest
2828from .status import HTTPStatus , CommonHTTPStatus
2929from .headers import HTTPHeaders
3030
3131
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+
3247class HTTPResponse :
3348 """
3449 Response to a given `HTTPRequest`. Use in `HTTPServer.route` decorator functions.
@@ -79,8 +94,8 @@ def route_func(request):
7994 """
8095 Defaults to ``text/plain`` if not set.
8196
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()` `.
8499
85100 Common MIME types are defined in `adafruit_httpserver.mime_type.MIMEType`.
86101 """
@@ -100,7 +115,7 @@ def __init__( # pylint: disable=too-many-arguments
100115 Sets `status`, ``headers`` and `http_version`
101116 and optionally default ``content_type``.
102117
103- To send the response, call `send` or `send_file`.
118+ To send the response, call `` send`` or `` send_file` `.
104119 For chunked response use
105120 ``with HTTPRequest(request, content_type=..., chunked=True) as r:`` and `send_chunk`.
106121 """
@@ -121,7 +136,7 @@ def _send_headers(
121136 ) -> None :
122137 """
123138 Sends headers.
124- Implicitly called by `send` and `send_file` and in
139+ Implicitly called by `` send`` and `` send_file` ` and in
125140 ``with HTTPResponse(request, chunked=True) as response:`` context manager.
126141 """
127142 headers = self .headers .copy ()
@@ -147,6 +162,7 @@ def _send_headers(
147162 self .request .connection , response_message_header .encode ("utf-8" )
148163 )
149164
165+ @_prevent_multiple_send_calls
150166 def send (
151167 self ,
152168 body : str = "" ,
@@ -158,8 +174,6 @@ def send(
158174
159175 Should be called **only once** per response.
160176 """
161- if self ._response_already_sent :
162- raise ResponseAlreadySentException
163177
164178 if getattr (body , "encode" , None ):
165179 encoded_response_message_body = body .encode ("utf-8" )
@@ -200,6 +214,7 @@ def _get_file_length(file_path: str) -> int:
200214 except OSError :
201215 raise FileNotExistsError (file_path ) # pylint: disable=raise-missing-from
202216
217+ @_prevent_multiple_send_calls
203218 def send_file (
204219 self ,
205220 filename : str = "index.html" ,
@@ -214,8 +229,6 @@ def send_file(
214229
215230 Should be called **only once** per response.
216231 """
217- if self ._response_already_sent :
218- raise ResponseAlreadySentException
219232
220233 if safe :
221234 self ._check_file_path_is_valid (filename )
0 commit comments