Modifications the handle_request function to detect and gracefully handle the case that the request_handler Task is canceled by the sanic server while it is handling the request. One common occurrence of this is when the server issues a ResponseTimeout error, it also cancels the response_handler Task.

The Canceled exception handler purposely sets `response` to `None` to drop references to the handler coroutine, in an attempt to preemptively release resources.
This commit also fixes a possible reference-before-assignment of the `response` variable in the `handle_request` function.
Finally, another byproduct of this change is that ResponseMiddleware will no longer run if the `response` is `None`.
This commit is contained in:
Ashley Sommer 2018-08-06 14:12:30 +10:00
parent b238be54a4
commit 39ff02b6e4

View File

@ -571,6 +571,10 @@ class Sanic:
:return: Nothing :return: Nothing
""" """
# Define `response` var here to remove warnings about
# allocation before assignment below.
response = None
cancelled = False
try: try:
# -------------------------------------------- # # -------------------------------------------- #
# Request Middleware # Request Middleware
@ -597,6 +601,13 @@ class Sanic:
response = handler(request, *args, **kwargs) response = handler(request, *args, **kwargs)
if isawaitable(response): if isawaitable(response):
response = await response response = await response
except CancelledError:
# If response handler times out, the server handles the error
# and cancels the handle_request job.
# In this case, the transport is already closed and we cannot
# issue a response.
response = None
cancelled = True
except Exception as e: except Exception as e:
# -------------------------------------------- # # -------------------------------------------- #
# Response Generation Failed # Response Generation Failed
@ -622,13 +633,22 @@ class Sanic:
# -------------------------------------------- # # -------------------------------------------- #
# Response Middleware # Response Middleware
# -------------------------------------------- # # -------------------------------------------- #
try: # Don't run response middleware if response is None
response = await self._run_response_middleware(request, if response is not None:
response) try:
except BaseException: response = await self._run_response_middleware(request,
error_logger.exception( response)
'Exception occurred in one of response middleware handlers' except CancelledError:
) # Response middleware can timeout too, as above.
response = None
cancelled = True
except BaseException:
error_logger.exception(
'Exception occurred in one of response '
'middleware handlers'
)
if cancelled:
raise CancelledError()
# pass the response to the correct callback # pass the response to the correct callback
if isinstance(response, StreamingHTTPResponse): if isinstance(response, StreamingHTTPResponse):