This commit is contained in:
L. Kärkkäinen 2020-03-01 13:21:02 +02:00
parent f8298939c0
commit 5086076590
7 changed files with 42 additions and 25 deletions

View File

@ -968,7 +968,6 @@ class Sanic:
) )
return response 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
to be sent back The HTTP Server only expects a response object, so to be sent back The HTTP Server only expects a response object, so
@ -1062,7 +1061,6 @@ class Sanic:
response = await self.handle_exception(request, e, name) response = await self.handle_exception(request, e, name)
await request.respond(response).send(end_stream=True) await request.respond(response).send(end_stream=True)
# -------------------------------------------------------------------- # # -------------------------------------------------------------------- #
# Testing # Testing
# -------------------------------------------------------------------- # # -------------------------------------------------------------------- #
@ -1092,7 +1090,7 @@ class Sanic:
stop_event: Any = None, stop_event: Any = None,
register_sys_signals: bool = True, register_sys_signals: bool = True,
access_log: Optional[bool] = None, access_log: Optional[bool] = None,
**kwargs: Any **kwargs: Any,
) -> None: ) -> None:
"""Run the HTTP Server and listen until keyboard interrupt or term """Run the HTTP Server and listen until keyboard interrupt or term
signal. On termination, drain connections before closing. signal. On termination, drain connections before closing.

View File

@ -345,13 +345,17 @@ class ASGIApp:
await self.transport.send(self.response_start) await self.transport.send(self.response_start)
self.response_start = None self.response_start = None
if self.response_body: if self.response_body:
data = self.response_body + data if data else self.response_body data = (
self.response_body + data if data else self.response_body
)
self.response_body = None self.response_body = None
await self.transport.send({ await self.transport.send(
"type": "http.response.body", {
"body": data.encode() if hasattr(data, "encode") else data, "type": "http.response.body",
"more_body": not end_stream, "body": data.encode() if hasattr(data, "encode") else data,
}) "more_body": not end_stream,
}
)
async def __call__(self) -> None: async def __call__(self) -> None:
""" """

View File

@ -28,6 +28,7 @@ class Stage(Enum):
HTTP_CONTINUE = b"HTTP/1.1 100 Continue\r\n\r\n" HTTP_CONTINUE = b"HTTP/1.1 100 Continue\r\n\r\n"
class Http: class Http:
__slots__ = [ __slots__ = [
"_send", "_send",
@ -47,6 +48,7 @@ class Http:
"response_func", "response_func",
"response_bytes_left", "response_bytes_left",
] ]
def __init__(self, protocol): def __init__(self, protocol):
self._send = protocol.send self._send = protocol.send
self._receive_more = protocol.receive_more self._receive_more = protocol.receive_more
@ -111,7 +113,7 @@ class Http:
self.stage = Stage.REQUEST self.stage = Stage.REQUEST
else: else:
raise PayloadTooLarge("Payload Too Large") raise PayloadTooLarge("Payload Too Large")
# Parse header content # Parse header content
try: try:
reqline, *raw_headers = buf[:pos].decode().split("\r\n") reqline, *raw_headers = buf[:pos].decode().split("\r\n")
method, self.url, protocol = reqline.split(" ") method, self.url, protocol = reqline.split(" ")
@ -193,9 +195,9 @@ class Http:
data, size, end_stream = b"", 0, True data, size, end_stream = b"", 0, True
headers.pop("content-length", None) headers.pop("content-length", None)
headers.pop("transfer-encoding", None) headers.pop("transfer-encoding", None)
#raise ServerError( # raise ServerError(
# f"A {status} response may only have headers, no body." # f"A {status} response may only have headers, no body."
#) # )
elif self.head_only and "content-length" in headers: elif self.head_only and "content-length" in headers:
self.response_func = None self.response_func = None
elif end_stream: elif end_stream:
@ -344,13 +346,15 @@ class Http:
del buf[:size] del buf[:size]
self.request_bytes_left -= size self.request_bytes_left -= size
self.protocol._total_request_size += size self.protocol._total_request_size += size
if self.protocol._total_request_size > self.protocol.request_max_size: if (
self.protocol._total_request_size
> self.protocol.request_max_size
):
self.keep_alive = False self.keep_alive = False
raise PayloadTooLarge("Payload Too Large") raise PayloadTooLarge("Payload Too Large")
return data return data
return None return None
# Response methods # Response methods
def respond(self, response): def respond(self, response):

View File

@ -17,8 +17,8 @@ from sanic.headers import (
parse_host, parse_host,
parse_xforwarded, parse_xforwarded,
) )
from sanic.response import HTTPResponse
from sanic.log import error_logger, logger from sanic.log import error_logger, logger
from sanic.response import HTTPResponse
try: try:
@ -132,12 +132,14 @@ class Request:
Custom context is now stored in `request.custom_context.yourkey`""" Custom context is now stored in `request.custom_context.yourkey`"""
setattr(self.ctx, key, value) setattr(self.ctx, key, value)
def respond(self, status=200, headers=None, content_type=DEFAULT_HTTP_CONTENT_TYPE): def respond(
self, status=200, headers=None, content_type=DEFAULT_HTTP_CONTENT_TYPE
):
return self.stream.respond( return self.stream.respond(
status if isinstance(status, HTTPResponse) else HTTPResponse( status
status=status, if isinstance(status, HTTPResponse)
headers=headers, else HTTPResponse(
content_type=content_type, status=status, headers=headers, content_type=content_type,
) )
) )

View File

@ -71,13 +71,12 @@ class StreamingHTTPResponse(BaseHTTPResponse):
async def stream(self, request): async def stream(self, request):
self.send = request.respond( self.send = request.respond(
self.status, self.status, self.headers, self.content_type,
self.headers,
self.content_type,
).send ).send
await self.streaming_fn(self) await self.streaming_fn(self)
await self.send(end_stream=True) await self.send(end_stream=True)
class HTTPResponse(BaseHTTPResponse): class HTTPResponse(BaseHTTPResponse):
__slots__ = ("body", "status", "content_type", "headers", "_cookies") __slots__ = ("body", "status", "content_type", "headers", "_cookies")

View File

@ -183,7 +183,14 @@ class HttpProtocol(asyncio.Protocol):
): ):
self._http.exception = ServiceUnavailable("Response Timeout") self._http.exception = ServiceUnavailable("Response Timeout")
else: else:
interval = min(self.keep_alive_timeout, self.request_timeout, self.response_timeout) / 2 interval = (
min(
self.keep_alive_timeout,
self.request_timeout,
self.response_timeout,
)
/ 2
)
self.loop.call_later(max(0.1, interval), self.check_timeouts) self.loop.call_later(max(0.1, interval), self.check_timeouts)
return return
self._task.cancel() self._task.cancel()
@ -265,6 +272,7 @@ class HttpProtocol(asyncio.Protocol):
except: except:
logger.exception("protocol.data_received") logger.exception("protocol.data_received")
def trigger_events(events, loop): def trigger_events(events, loop):
"""Trigger event callbacks (functions or async) """Trigger event callbacks (functions or async)

View File

@ -41,7 +41,9 @@ class SanicTestClient:
url, verify=False, *args, **kwargs url, verify=False, *args, **kwargs
) )
except httpx.exceptions.ConnectionClosed: except httpx.exceptions.ConnectionClosed:
logger.error(f"{method.upper()} {url} broken HTTP, response is None!") logger.error(
f"{method.upper()} {url} broken HTTP, response is None!"
)
return None return None
except NameError: except NameError:
raise Exception(response.status_code) raise Exception(response.status_code)