Everything but CustomServer OK.
This commit is contained in:
parent
85be5768c8
commit
2840e4cfc8
|
@ -1028,6 +1028,8 @@ class Sanic:
|
||||||
response = await self._run_response_middleware(
|
response = await self._run_response_middleware(
|
||||||
request, response, request_name=name
|
request, response, request_name=name
|
||||||
)
|
)
|
||||||
|
except CancelledError:
|
||||||
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
error_logger.exception(
|
error_logger.exception(
|
||||||
"Exception occurred in one of response "
|
"Exception occurred in one of response "
|
||||||
|
@ -1043,6 +1045,8 @@ class Sanic:
|
||||||
f"Invalid response type {response!r} (need HTTPResponse)"
|
f"Invalid response type {response!r} (need HTTPResponse)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
except CancelledError:
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
# Response Generation Failed
|
# Response Generation Failed
|
||||||
|
|
376
sanic/http.py
376
sanic/http.py
|
@ -29,6 +29,24 @@ 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__ = [
|
||||||
|
"_send",
|
||||||
|
"_receive_more",
|
||||||
|
"recv_buffer",
|
||||||
|
"protocol",
|
||||||
|
"expecting_continue",
|
||||||
|
"stage",
|
||||||
|
"keep_alive",
|
||||||
|
"head_only",
|
||||||
|
"request",
|
||||||
|
"exception",
|
||||||
|
"url",
|
||||||
|
"request_chunked",
|
||||||
|
"request_bytes_left",
|
||||||
|
"response",
|
||||||
|
"response_func",
|
||||||
|
"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
|
||||||
|
@ -40,8 +58,46 @@ class Http:
|
||||||
self.head_only = None
|
self.head_only = None
|
||||||
self.request = None
|
self.request = None
|
||||||
self.exception = None
|
self.exception = None
|
||||||
|
self.url = None
|
||||||
|
|
||||||
async def http1_receive_request(self):
|
async def http1(self):
|
||||||
|
"""HTTP 1.1 connection handler"""
|
||||||
|
while True: # As long as connection stays keep-alive
|
||||||
|
try:
|
||||||
|
# Receive and handle a request
|
||||||
|
self.stage = Stage.REQUEST
|
||||||
|
self.response_func = self.http1_response_start
|
||||||
|
await self.http1_request_header()
|
||||||
|
await self.protocol.request_handler(self.request)
|
||||||
|
# Handler finished, response should've been sent
|
||||||
|
if self.stage is Stage.HANDLER:
|
||||||
|
raise ServerError("Handler produced no response")
|
||||||
|
if self.stage is Stage.RESPONSE:
|
||||||
|
await self.send(end_stream=True)
|
||||||
|
# Consume any remaining request body (TODO: or disconnect?)
|
||||||
|
if self.request_bytes_left or self.request_chunked:
|
||||||
|
logger.error(f"{self.request} body not consumed.")
|
||||||
|
async for _ in self:
|
||||||
|
pass
|
||||||
|
except CancelledError:
|
||||||
|
# Write an appropriate response before exiting
|
||||||
|
e = self.exception or ServiceUnavailable(f"Cancelled")
|
||||||
|
self.exception = None
|
||||||
|
self.keep_alive = False
|
||||||
|
await self.error_response(e)
|
||||||
|
except Exception as e:
|
||||||
|
# Write an error response
|
||||||
|
await self.error_response(e)
|
||||||
|
# Exit and disconnect if finished
|
||||||
|
if self.stage is not Stage.IDLE or not self.keep_alive:
|
||||||
|
break
|
||||||
|
# Wait for next request
|
||||||
|
if not self.recv_buffer:
|
||||||
|
await self._receive_more()
|
||||||
|
|
||||||
|
async def http1_request_header(self):
|
||||||
|
"""Receive and parse request header into self.request."""
|
||||||
|
# Receive until full header is in buffer
|
||||||
buf = self.recv_buffer
|
buf = self.recv_buffer
|
||||||
pos = 0
|
pos = 0
|
||||||
while len(buf) < self.protocol.request_max_size:
|
while len(buf) < self.protocol.request_max_size:
|
||||||
|
@ -55,105 +111,195 @@ 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
|
||||||
self.protocol._total_request_size = pos + 4
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
reqline, *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(" ")
|
||||||
if protocol not in ("HTTP/1.0", "HTTP/1.1"):
|
if protocol == "HTTP/1.1":
|
||||||
|
self.keep_alive = True
|
||||||
|
elif protocol == "HTTP/1.0":
|
||||||
|
self.keep_alive = False
|
||||||
|
else:
|
||||||
raise Exception
|
raise Exception
|
||||||
self.head_only = method.upper() == "HEAD"
|
self.head_only = method.upper() == "HEAD"
|
||||||
headers = Header(
|
body = False
|
||||||
(name.lower(), value.lstrip())
|
headers = []
|
||||||
for name, value in (h.split(":", 1) for h in headers)
|
for name, value in (h.split(":", 1) for h in raw_headers):
|
||||||
)
|
name, value = h = name.lower(), value.lstrip()
|
||||||
|
if name in ("content-length", "transfer-encoding"):
|
||||||
|
body = True
|
||||||
|
elif name == "connection":
|
||||||
|
self.keep_alive = value.lower() == "keep-alive"
|
||||||
|
headers.append(h)
|
||||||
except:
|
except:
|
||||||
raise InvalidUsage("Bad Request")
|
raise InvalidUsage("Bad Request")
|
||||||
|
# Prepare a Request object
|
||||||
request = self.protocol.request_class(
|
request = self.protocol.request_class(
|
||||||
url_bytes=self.url.encode(),
|
url_bytes=self.url.encode(),
|
||||||
headers=headers,
|
headers=Header(headers),
|
||||||
version=protocol[-3:],
|
version=protocol[-3:],
|
||||||
method=method,
|
method=method,
|
||||||
transport=self.protocol.transport,
|
transport=self.protocol.transport,
|
||||||
app=self.protocol.app,
|
app=self.protocol.app,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Prepare a request object from the header received
|
|
||||||
request.stream = self
|
request.stream = self
|
||||||
self.protocol.state["requests_count"] += 1
|
self.protocol.state["requests_count"] += 1
|
||||||
self.keep_alive = (
|
|
||||||
protocol == "HTTP/1.1"
|
|
||||||
or headers.get("connection", "").lower() == "keep-alive"
|
|
||||||
)
|
|
||||||
# Prepare for request body
|
# Prepare for request body
|
||||||
body = headers.get("transfer-encoding") == "chunked" or int(
|
|
||||||
headers.get("content-length", 0)
|
|
||||||
)
|
|
||||||
self.request_chunked = False
|
self.request_chunked = False
|
||||||
self.request_bytes_left = 0
|
self.request_bytes_left = 0
|
||||||
if body:
|
if body:
|
||||||
|
headers = request.headers
|
||||||
expect = headers.get("expect")
|
expect = headers.get("expect")
|
||||||
if expect:
|
if expect is not None:
|
||||||
if expect.lower() == "100-continue":
|
if expect.lower() == "100-continue":
|
||||||
self.expecting_continue = True
|
self.expecting_continue = True
|
||||||
else:
|
else:
|
||||||
raise HeaderExpectationFailed(
|
raise HeaderExpectationFailed(f"Unknown Expect: {expect}")
|
||||||
f"Unknown Expect: {expect}")
|
|
||||||
request.stream = self
|
request.stream = self
|
||||||
if body is True:
|
if headers.get("transfer-encoding") == "chunked":
|
||||||
self.request_chunked = True
|
self.request_chunked = True
|
||||||
pos -= 2 # One CRLF stays in buffer
|
pos -= 2 # One CRLF stays in buffer
|
||||||
else:
|
else:
|
||||||
self.request_bytes_left = body
|
self.request_bytes_left = int(headers["content-length"])
|
||||||
# Remove header and its trailing CRLF
|
# Remove header and its trailing CRLF
|
||||||
del buf[: pos + 4]
|
del buf[: pos + 4]
|
||||||
self.stage = Stage.HANDLER
|
self.stage = Stage.HANDLER
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
async def http1(self):
|
def http1_response_start(self, data, end_stream) -> bytes:
|
||||||
"""HTTP 1.1 connection handler"""
|
res = self.response
|
||||||
while self.stage is Stage.IDLE and self.keep_alive:
|
# Compatibility with simple response body
|
||||||
try:
|
if not data and res.body:
|
||||||
# Receive request header and call handler
|
data, end_stream = res.body, True
|
||||||
await self.http1_receive_request()
|
size = len(data)
|
||||||
await self.protocol.request_handler(self.request)
|
status = res.status
|
||||||
if self.stage is Stage.HANDLER:
|
headers = res.headers
|
||||||
raise ServerError("Handler produced no response")
|
if res.content_type and "content-type" not in headers:
|
||||||
# Finish sending a response (if no error)
|
headers["content-type"] = res.content_type
|
||||||
if self.stage is Stage.RESPONSE:
|
# Not Modified, Precondition Failed
|
||||||
await self.send(end_stream=True)
|
if status in (304, 412):
|
||||||
# Consume any remaining request body
|
headers = remove_entity_headers(headers)
|
||||||
if self.request_bytes_left or self.request_chunked:
|
if not has_message_body(status):
|
||||||
logger.error(f"{self.request} body not consumed.")
|
# Header-only response status
|
||||||
async for _ in self:
|
self.response_func = None
|
||||||
pass
|
if (
|
||||||
except CancelledError:
|
data
|
||||||
# Exit after trying to finish a response
|
or not end_stream
|
||||||
self.keep_alive = False
|
or "content-length" in headers
|
||||||
if self.exception is None:
|
or "transfer-encoding" in headers
|
||||||
self.exception = ServiceUnavailable(f"Cancelled")
|
):
|
||||||
except Exception as e:
|
# TODO: This matches old Sanic operation but possibly
|
||||||
self.exception = e
|
# an exception would be more appropriate?
|
||||||
if self.exception:
|
data, size, end_stream = b"", 0, True
|
||||||
e, self.exception = self.exception, None
|
headers.pop("content-length", None)
|
||||||
# Exception while idle? Probably best to close connection
|
headers.pop("transfer-encoding", None)
|
||||||
if self.stage is Stage.IDLE:
|
#raise ServerError(
|
||||||
return
|
# f"A {status} response may only have headers, no body."
|
||||||
# Request failure? Try to respond but then disconnect
|
#)
|
||||||
if self.stage is Stage.REQUEST:
|
elif self.head_only and "content-length" in headers:
|
||||||
self.keep_alive = False
|
self.response_func = None
|
||||||
self.stage = Stage.HANDLER
|
elif end_stream:
|
||||||
# Return an error page if possible
|
# Non-streaming response (all in one block)
|
||||||
if self.stage is Stage.HANDLER:
|
headers["content-length"] = size
|
||||||
app = self.protocol.app
|
self.response_func = None
|
||||||
response = await app.handle_exception(self.request, e)
|
elif "content-length" in headers:
|
||||||
await self.respond(response).send(end_stream=True)
|
# Streaming response with size known in advance
|
||||||
|
self.response_bytes_left = int(headers["content-length"]) - size
|
||||||
|
self.response_func = self.http1_response_normal
|
||||||
|
else:
|
||||||
|
# Length not known, use chunked encoding
|
||||||
|
headers["transfer-encoding"] = "chunked"
|
||||||
|
data = b"%x\r\n%b\r\n" % (size, data) if size else None
|
||||||
|
self.response_func = self.http1_response_chunked
|
||||||
|
if self.head_only:
|
||||||
|
# Head request: don't send body
|
||||||
|
data = b""
|
||||||
|
self.response_func = self.head_response_ignored
|
||||||
|
headers["connection"] = "keep-alive" if self.keep_alive else "close"
|
||||||
|
ret = format_http1_response(status, headers.items(), data)
|
||||||
|
# Send a 100-continue if expected and not Expectation Failed
|
||||||
|
if self.expecting_continue:
|
||||||
|
self.expecting_continue = False
|
||||||
|
if status != 417:
|
||||||
|
ret = HTTP_CONTINUE + ret
|
||||||
|
# Send response
|
||||||
|
self.log_response()
|
||||||
|
self.stage = Stage.IDLE if end_stream else Stage.RESPONSE
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def head_response_ignored(self, data, end_stream):
|
||||||
|
"""HEAD response: body data silently ignored."""
|
||||||
|
if end_stream:
|
||||||
|
self.response_func = None
|
||||||
|
self.stage = Stage.IDLE
|
||||||
|
|
||||||
|
def http1_response_chunked(self, data, end_stream) -> bytes:
|
||||||
|
"""Format a part of response body in chunked encoding."""
|
||||||
|
# Chunked encoding
|
||||||
|
size = len(data)
|
||||||
|
if end_stream:
|
||||||
|
self.response_func = None
|
||||||
|
self.stage = Stage.IDLE
|
||||||
|
if size:
|
||||||
|
return b"%x\r\n%b\r\n0\r\n\r\n" % (size, data)
|
||||||
|
return b"0\r\n\r\n"
|
||||||
|
return b"%x\r\n%b\r\n" % (size, data) if size else None
|
||||||
|
|
||||||
|
def http1_response_normal(self, data: bytes, end_stream: bool) -> bytes:
|
||||||
|
"""Format / keep track of non-chunked response."""
|
||||||
|
self.response_bytes_left -= len(data)
|
||||||
|
if self.response_bytes_left <= 0:
|
||||||
|
if self.response_bytes_left < 0:
|
||||||
|
raise ServerError("Response was bigger than content-length")
|
||||||
|
self.response_func = None
|
||||||
|
self.stage = Stage.IDLE
|
||||||
|
elif end_stream:
|
||||||
|
raise ServerError("Response was smaller than content-length")
|
||||||
|
return data
|
||||||
|
|
||||||
|
async def error_response(self, exception):
|
||||||
|
# Disconnect after an error if in any other state than handler
|
||||||
|
if self.stage is not Stage.HANDLER:
|
||||||
|
self.keep_alive = False
|
||||||
|
# Request failure? Respond but then disconnect
|
||||||
|
if self.stage is Stage.REQUEST:
|
||||||
|
self.stage = Stage.HANDLER
|
||||||
|
# From request and handler states we can respond, otherwise be silent
|
||||||
|
if self.stage is Stage.HANDLER:
|
||||||
|
app = self.protocol.app
|
||||||
|
response = await app.handle_exception(self.request, exception)
|
||||||
|
await self.respond(response).send(end_stream=True)
|
||||||
|
|
||||||
|
def log_response(self):
|
||||||
|
"""
|
||||||
|
Helper method provided to enable the logging of responses in case if
|
||||||
|
the :attr:`HttpProtocol.access_log` is enabled.
|
||||||
|
|
||||||
|
:param response: Response generated for the current request
|
||||||
|
|
||||||
|
:type response: :class:`sanic.response.HTTPResponse` or
|
||||||
|
:class:`sanic.response.StreamingHTTPResponse`
|
||||||
|
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
if self.protocol.access_log:
|
||||||
|
req, res = self.request, self.response
|
||||||
|
extra = {
|
||||||
|
"status": getattr(res, "status", 0),
|
||||||
|
"byte": getattr(self, "response_bytes_left", -1),
|
||||||
|
"host": "UNKNOWN",
|
||||||
|
"request": "nil",
|
||||||
|
}
|
||||||
|
if req is not None:
|
||||||
|
if req.ip:
|
||||||
|
extra["host"] = f"{req.ip}:{req.port}"
|
||||||
|
extra["request"] = f"{req.method} {req.url}"
|
||||||
|
access_logger.info("", extra=extra)
|
||||||
|
|
||||||
# Request methods
|
# Request methods
|
||||||
|
|
||||||
async def __aiter__(self):
|
async def __aiter__(self):
|
||||||
|
"""Async iterate over request body."""
|
||||||
while True:
|
while True:
|
||||||
data = await self.read()
|
data = await self.read()
|
||||||
if not data:
|
if not data:
|
||||||
|
@ -161,6 +307,7 @@ class Http:
|
||||||
yield data
|
yield data
|
||||||
|
|
||||||
async def read(self):
|
async def read(self):
|
||||||
|
"""Read some bytes of request body."""
|
||||||
# Send a 100-continue if needed
|
# Send a 100-continue if needed
|
||||||
if self.expecting_continue:
|
if self.expecting_continue:
|
||||||
self.expecting_continue = False
|
self.expecting_continue = False
|
||||||
|
@ -227,105 +374,8 @@ class Http:
|
||||||
"""
|
"""
|
||||||
if data is None and end_stream is None:
|
if data is None and end_stream is None:
|
||||||
end_stream = True
|
end_stream = True
|
||||||
data = self.data_to_send(data, end_stream)
|
data = data.encode() if hasattr(data, "encode") else data or b""
|
||||||
if data is None:
|
data = self.response_func(data, end_stream)
|
||||||
|
if not data:
|
||||||
return
|
return
|
||||||
await self._send(data)
|
await self._send(data)
|
||||||
|
|
||||||
def data_to_send(self, data, end_stream):
|
|
||||||
"""Format output data bytes for given body data.
|
|
||||||
Headers are prepended to the first output block and then cleared.
|
|
||||||
:param data: str or bytes to be written
|
|
||||||
:return: bytes to send, or None if there is nothing to send
|
|
||||||
"""
|
|
||||||
data = data.encode() if hasattr(data, "encode") else data
|
|
||||||
size = len(data) if data is not None else 0
|
|
||||||
|
|
||||||
# Headers not yet sent?
|
|
||||||
if self.stage is Stage.HANDLER:
|
|
||||||
if self.response.body:
|
|
||||||
data = self.response.body + data if data else self.response.body
|
|
||||||
size = len(data)
|
|
||||||
r = self.response
|
|
||||||
status = r.status
|
|
||||||
headers = r.headers
|
|
||||||
if r.content_type and "content-type" not in headers:
|
|
||||||
headers["content-type"] = r.content_type
|
|
||||||
# Not Modified, Precondition Failed
|
|
||||||
if status in (304, 412):
|
|
||||||
headers = remove_entity_headers(headers)
|
|
||||||
if not has_message_body(status):
|
|
||||||
# Header-only response status
|
|
||||||
if (
|
|
||||||
size > 0
|
|
||||||
or not end_stream
|
|
||||||
or "content-length" in headers
|
|
||||||
or "transfer-encoding" in headers
|
|
||||||
):
|
|
||||||
# TODO: This matches old Sanic operation but possibly
|
|
||||||
# an exception would be more appropriate?
|
|
||||||
data = None
|
|
||||||
size = 0
|
|
||||||
end_stream = True
|
|
||||||
#raise ServerError(
|
|
||||||
# f"A {status} response may only have headers, no body."
|
|
||||||
#)
|
|
||||||
elif self.head_only and "content-length" in headers:
|
|
||||||
pass
|
|
||||||
elif end_stream:
|
|
||||||
# Non-streaming response (all in one block)
|
|
||||||
headers["content-length"] = size
|
|
||||||
elif "content-length" in headers:
|
|
||||||
# Streaming response with size known in advance
|
|
||||||
self.response_bytes_left = int(headers["content-length"]) - size
|
|
||||||
else:
|
|
||||||
# Length not known, use chunked encoding
|
|
||||||
headers["transfer-encoding"] = "chunked"
|
|
||||||
data = b"%x\r\n%b\r\n" % (size, data) if size else None
|
|
||||||
self.response_bytes_left = True
|
|
||||||
self.headers = None
|
|
||||||
if self.head_only:
|
|
||||||
data = None
|
|
||||||
self.response_bytes_left = None
|
|
||||||
if self.keep_alive:
|
|
||||||
headers["connection"] = "keep-alive"
|
|
||||||
headers["keep-alive"] = self.protocol.keep_alive_timeout
|
|
||||||
else:
|
|
||||||
headers["connection"] = "close"
|
|
||||||
ret = format_http1_response(status, headers.items(), data or b"")
|
|
||||||
# Send a 100-continue if expected and not Expectation Failed
|
|
||||||
if self.expecting_continue:
|
|
||||||
self.expecting_continue = False
|
|
||||||
if status != 417:
|
|
||||||
ret = HTTP_CONTINUE + ret
|
|
||||||
# Send response
|
|
||||||
self.stage = Stage.IDLE if end_stream else Stage.RESPONSE
|
|
||||||
return ret
|
|
||||||
|
|
||||||
# HEAD request: don't send body
|
|
||||||
if self.head_only:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self.stage is not Stage.RESPONSE:
|
|
||||||
if size:
|
|
||||||
raise RuntimeError("Cannot send data to a closed stream")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Chunked encoding
|
|
||||||
if self.response_bytes_left is True:
|
|
||||||
if end_stream:
|
|
||||||
self.response_bytes_left = None
|
|
||||||
self.stage = Stage.IDLE
|
|
||||||
if size:
|
|
||||||
return b"%x\r\n%b\r\n0\r\n\r\n" % (size, data)
|
|
||||||
return b"0\r\n\r\n"
|
|
||||||
return b"%x\r\n%b\r\n" % (size, data) if size else None
|
|
||||||
|
|
||||||
# Normal encoding
|
|
||||||
else:
|
|
||||||
self.response_bytes_left -= size
|
|
||||||
if self.response_bytes_left <= 0:
|
|
||||||
if self.response_bytes_left < 0:
|
|
||||||
raise ServerError("Response was bigger than content-length")
|
|
||||||
self.stage = Stage.IDLE
|
|
||||||
return data if size else None
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ from sanic.exceptions import (
|
||||||
ServiceUnavailable,
|
ServiceUnavailable,
|
||||||
)
|
)
|
||||||
from sanic.http import Http, Stage
|
from sanic.http import Http, Stage
|
||||||
from sanic.log import access_logger, logger
|
from sanic.log import logger
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
|
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
stage in (Stage.HANDLER, Stage.RESPONSE, Stage.FAILED)
|
stage in (Stage.HANDLER, Stage.RESPONSE, Stage.FAILED)
|
||||||
and duration > self.response_timeout
|
and duration > self.response_timeout
|
||||||
):
|
):
|
||||||
self._http.exception = RequestTimeout("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)
|
||||||
|
|
|
@ -79,12 +79,8 @@ class DelayableSanicTestClient(SanicTestClient):
|
||||||
|
|
||||||
request_timeout_default_app = Sanic("test_request_timeout_default")
|
request_timeout_default_app = Sanic("test_request_timeout_default")
|
||||||
request_no_timeout_app = Sanic("test_request_no_timeout")
|
request_no_timeout_app = Sanic("test_request_no_timeout")
|
||||||
|
request_timeout_default_app.config.REQUEST_TIMEOUT = 0.6
|
||||||
# Note: The delayed client pauses before making a request, so technically
|
request_no_timeout_app.config.REQUEST_TIMEOUT = 0.6
|
||||||
# it is in keep alive duration. Earlier Sanic versions entered a new connection
|
|
||||||
# in request mode even if no bytes of request were received.
|
|
||||||
request_timeout_default_app.config.KEEP_ALIVE_TIMEOUT = 0.6
|
|
||||||
request_no_timeout_app.config.KEEP_ALIVE_TIMEOUT = 0.6
|
|
||||||
|
|
||||||
|
|
||||||
@request_timeout_default_app.route("/1")
|
@request_timeout_default_app.route("/1")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user