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

View File

@ -1,7 +1,13 @@
from sys import argv from sys import argv
from multidict import CIMultiDict # type: ignore 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): class Header(CIMultiDict):
def get_all(self, key): 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 httptools import parse_url # type: ignore
from sanic.exceptions import InvalidUsage from sanic.exceptions import InvalidUsage
from sanic.compat import CancelledErrors
from sanic.headers import ( from sanic.headers import (
parse_content_header, parse_content_header,
parse_forwarded, parse_forwarded,
@ -16,7 +17,7 @@ from sanic.headers import (
parse_xforwarded, parse_xforwarded,
) )
from sanic.log import error_logger, logger from sanic.log import error_logger, logger
from sanic.response import HTTPResponse from sanic.response import HTTPResponse, BaseHTTPResponse
try: try:
@ -62,6 +63,7 @@ class Request:
"endpoint", "endpoint",
"headers", "headers",
"method", "method",
"name",
"parsed_args", "parsed_args",
"parsed_not_grouped_args", "parsed_not_grouped_args",
"parsed_files", "parsed_files",
@ -89,6 +91,7 @@ class Request:
# Init but do not inhale # Init but do not inhale
self.body = b"" self.body = b""
self.ctx = SimpleNamespace() self.ctx = SimpleNamespace()
self.name = None
self.parsed_forwarded = None self.parsed_forwarded = None
self.parsed_json = None self.parsed_json = None
self.parsed_form = None self.parsed_form = None
@ -105,7 +108,7 @@ class Request:
self.__class__.__name__, self.method, self.path self.__class__.__name__, self.method, self.path
) )
def respond( async def respond(
self, response=None, *, status=200, headers=None, content_type=None self, response=None, *, status=200, headers=None, content_type=None
): ):
# This logic of determining which response to use is subject to change # This logic of determining which response to use is subject to change
@ -113,8 +116,22 @@ class Request:
response = self.stream.response or HTTPResponse( response = self.stream.response or HTTPResponse(
status=status, headers=headers, content_type=content_type, status=status, headers=headers, content_type=content_type,
) )
# Connect the response and return it # Connect the response
return self.stream.respond(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): async def receive_body(self):
self.body = b"".join([data async for data in self.stream]) self.body = b"".join([data async for data in self.stream])