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( response = HTTPResponse(
"An error occurred while handling an error", status=500 "An error occurred while handling an error", status=500
) )
# Run response middleware
if response is not None: if response is not None:
try: try:
response = await self._run_response_middleware( response = await request.respond(response)
request, response, request_name=request.name 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): async def handle_request(self, request):
"""Take a request from the HTTP Server and return a response object """Take a request from the HTTP Server and return a response object
@ -1039,6 +1039,8 @@ class Sanic:
response = await response response = await response
if response: if response:
response = await request.respond(response) response = await request.respond(response)
else:
response = request.stream.response
# Make sure that response is finished / run StreamingHTTP callback # Make sure that response is finished / run StreamingHTTP callback
if isinstance(response, BaseHTTPResponse): if isinstance(response, BaseHTTPResponse):
await response.send(end_stream=True) await response.send(end_stream=True)
@ -1053,9 +1055,7 @@ class Sanic:
# -------------------------------------------- # # -------------------------------------------- #
# Response Generation Failed # Response Generation Failed
# -------------------------------------------- # # -------------------------------------------- #
response = await self.handle_exception(request, e) await self.handle_exception(request, e)
response = await request.respond(response)
await response.send(end_stream=True)
# -------------------------------------------------------------------- # # -------------------------------------------------------------------- #
# Testing # Testing

View File

@ -274,9 +274,26 @@ class Http:
# From request and handler states we can respond, otherwise be silent # From request and handler states we can respond, otherwise be silent
if self.stage is Stage.HANDLER: if self.stage is Stage.HANDLER:
app = self.protocol.app app = self.protocol.app
if self.request is None:
self.create_empty_request()
response = await app.handle_exception(self.request, exception) response = await app.handle_exception(self.request, exception)
await self.respond(response).send(end_stream=True) 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): def log_response(self):
""" """
Helper method provided to enable the logging of responses in case if Helper method provided to enable the logging of responses in case if

View File

@ -137,6 +137,12 @@ class HttpProtocol(asyncio.Protocol):
except Exception: except Exception:
logger.exception("protocol.connection_task uncaught") logger.exception("protocol.connection_task uncaught")
finally: 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._http = None
self._task = None self._task = None
try: try:
@ -229,9 +235,6 @@ class HttpProtocol(asyncio.Protocol):
self.resume_writing() self.resume_writing()
if self._task: if self._task:
self._task.cancel() 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: except Exception:
logger.exception("protocol.connection_lost") logger.exception("protocol.connection_lost")