Response middleware ran by async request.respond().

This commit is contained in:
L. Kärkkäinen 2020-03-09 16:59:36 +02:00
parent 2b63d2bed4
commit 2adcc72e06
3 changed files with 38 additions and 22 deletions

View File

@ -938,7 +938,7 @@ class Sanic:
"""
pass
async def handle_exception(self, request, exception, name=""):
async def handle_exception(self, request, exception):
try:
response = self.error_handler.response(request, exception)
if isawaitable(response):
@ -959,7 +959,7 @@ class Sanic:
if response is not None:
try:
response = await self._run_response_middleware(
request, response, request_name=name
request, response, request_name=request.name
)
except CancelledError:
# FIXME: Ensure exiting in a clean manner instead of this
@ -992,6 +992,7 @@ class Sanic:
try:
# Fetch handler from router
handler, args, kwargs, uri, name = self.router.get(request)
request.name = name
if request.stream.request_body:
if self.router.is_stream_handler(request):
@ -1036,22 +1037,11 @@ class Sanic:
response = handler(request, *args, **kwargs)
if isawaitable(response):
response = await response
# Run response middleware
if response is not None:
try:
response = await self._run_response_middleware(
request, response, request_name=name
)
except CancelledError:
raise
except Exception:
error_logger.exception(
"Exception occurred in one of response "
"middleware handlers"
)
if response:
response = await request.respond(response)
# Make sure that response is finished / run StreamingHTTP callback
if isinstance(response, BaseHTTPResponse):
await request.respond(response).send(end_stream=True)
await response.send(end_stream=True)
else:
raise ServerError(
f"Invalid response type {response!r} (need HTTPResponse)"
@ -1063,8 +1053,9 @@ class Sanic:
# -------------------------------------------- #
# Response Generation Failed
# -------------------------------------------- #
response = await self.handle_exception(request, e, name)
await request.respond(response).send(end_stream=True)
response = await self.handle_exception(request, e)
response = await request.respond(response)
await response.send(end_stream=True)
# -------------------------------------------------------------------- #
# Testing
@ -1345,6 +1336,8 @@ class Sanic:
_response = await _response
if _response:
response = _response
if isinstance(response, BaseHTTPResponse):
response = request.stream.respond(response)
break
return response

View File

@ -1,7 +1,13 @@
from sys import argv
from multidict import CIMultiDict # type: ignore
from asyncio import CancelledError
try:
from trio import Cancelled
CancelledErrors = CancelledError, Cancelled
except ImportError:
CancelledErrors = CancelledError,
class Header(CIMultiDict):
def get_all(self, key):

View File

@ -9,6 +9,7 @@ from urllib.parse import parse_qs, parse_qsl, unquote, urlunparse
from httptools import parse_url # type: ignore
from sanic.exceptions import InvalidUsage
from sanic.compat import CancelledErrors
from sanic.headers import (
parse_content_header,
parse_forwarded,
@ -16,7 +17,7 @@ from sanic.headers import (
parse_xforwarded,
)
from sanic.log import error_logger, logger
from sanic.response import HTTPResponse
from sanic.response import HTTPResponse, BaseHTTPResponse
try:
@ -62,6 +63,7 @@ class Request:
"endpoint",
"headers",
"method",
"name",
"parsed_args",
"parsed_not_grouped_args",
"parsed_files",
@ -89,6 +91,7 @@ class Request:
# Init but do not inhale
self.body = b""
self.ctx = SimpleNamespace()
self.name = None
self.parsed_forwarded = None
self.parsed_json = None
self.parsed_form = None
@ -105,7 +108,7 @@ class Request:
self.__class__.__name__, self.method, self.path
)
def respond(
async def respond(
self, response=None, *, status=200, headers=None, content_type=None
):
# This logic of determining which response to use is subject to change
@ -113,8 +116,22 @@ class Request:
response = self.stream.response or HTTPResponse(
status=status, headers=headers, content_type=content_type,
)
# Connect the response and return it
return self.stream.respond(response)
# Connect the response
if isinstance(response, BaseHTTPResponse):
response = self.stream.respond(response)
# Run response middleware
try:
response = await self.app._run_response_middleware(
self, response, request_name=self.name
)
except CancelledErrors:
raise
except Exception:
error_logger.exception(
"Exception occurred in one of response "
"middleware handlers"
)
return response
async def receive_body(self):
self.body = b"".join([data async for data in self.stream])