Middleware and error handling refactoring.

This commit is contained in:
L. Kärkkäinen 2020-03-10 16:45:26 +02:00
parent d2d6008eec
commit 17d100400f
3 changed files with 40 additions and 20 deletions

View File

@ -955,22 +955,22 @@ class Sanic:
response = HTTPResponse(
"An error occurred while handling an error", status=500
)
# Run response middleware
if response is not None:
try:
response = await self._run_response_middleware(
request, response, request_name=request.name
response = await request.respond(response)
except:
# Skip response middleware
request.stream.respond(response)
await response.send(end_stream=True)
raise
else:
response = request.stream.response
if isinstance(response, BaseHTTPResponse):
await response.send(end_stream=True)
else:
raise ServerError(
f"Invalid response type {response!r} (need HTTPResponse)"
)
except CancelledError:
# FIXME: Ensure exiting in a clean manner instead of this
# and verify py37 and py38 test_middleware.py separately
request.stream.keep_alive = False
except Exception:
error_logger.exception(
"Exception occurred in one of response "
"middleware handlers"
)
return response
async def handle_request(self, request):
"""Take a request from the HTTP Server and return a response object
@ -1039,6 +1039,8 @@ class Sanic:
response = await response
if response:
response = await request.respond(response)
else:
response = request.stream.response
# Make sure that response is finished / run StreamingHTTP callback
if isinstance(response, BaseHTTPResponse):
await response.send(end_stream=True)
@ -1053,9 +1055,7 @@ class Sanic:
# -------------------------------------------- #
# Response Generation Failed
# -------------------------------------------- #
response = await self.handle_exception(request, e)
response = await request.respond(response)
await response.send(end_stream=True)
await self.handle_exception(request, e)
# -------------------------------------------------------------------- #
# Testing

View File

@ -274,9 +274,26 @@ class Http:
# From request and handler states we can respond, otherwise be silent
if self.stage is Stage.HANDLER:
app = self.protocol.app
if self.request is None:
self.create_empty_request()
response = await app.handle_exception(self.request, exception)
await self.respond(response).send(end_stream=True)
def create_empty_request(self):
"""Current error handling code needs a request object that won't exist
if an error occurred during before a request was received. Create a
bogus response for error handling use."""
# FIXME: Avoid this by refactoring error handling and response code
self.request = self.protocol.request_class(
url_bytes=self.url.encode() if self.url else b"*",
headers=Header({}),
version="1.1",
method="NONE",
transport=self.protocol.transport,
app=self.protocol.app,
)
self.request.stream = self
def log_response(self):
"""
Helper method provided to enable the logging of responses in case if

View File

@ -137,6 +137,12 @@ class HttpProtocol(asyncio.Protocol):
except Exception:
logger.exception("protocol.connection_task uncaught")
finally:
if self._debug and self._http and self._http.request:
ip = self.transport.get_extra_info("peername")
logger.error(
"Connection lost before response written"
f" @ {ip} {self._http.request}"
)
self._http = None
self._task = None
try:
@ -229,9 +235,6 @@ class HttpProtocol(asyncio.Protocol):
self.resume_writing()
if self._task:
self._task.cancel()
if self._debug and self._http and self._http.response:
ip = self.transport.get_extra_info("peername")
logger.error(f"Connection lost before response written @ {ip}")
except Exception:
logger.exception("protocol.connection_lost")