Change Request timeout process
This add a request timeout exception. It cancels task, when request is timeout.
This commit is contained in:
parent
fc19f2ea34
commit
c01cbb3a8c
21
examples/request_timeout.py
Normal file
21
examples/request_timeout.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from sanic import Sanic
|
||||||
|
import asyncio
|
||||||
|
from sanic.response import text
|
||||||
|
from sanic.config import Config
|
||||||
|
from sanic.exceptions import RequestTimeout
|
||||||
|
|
||||||
|
Config.REQUEST_TIMEOUT = 1
|
||||||
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
async def test(request):
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
return text('Hello, world!')
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception(RequestTimeout)
|
||||||
|
def timeout(request, exception):
|
||||||
|
return text('RequestTimeout from error_handler.')
|
||||||
|
|
||||||
|
app.run(host="0.0.0.0", port=8000)
|
|
@ -30,6 +30,10 @@ class FileNotFound(NotFound):
|
||||||
self.relative_url = relative_url
|
self.relative_url = relative_url
|
||||||
|
|
||||||
|
|
||||||
|
class RequestTimeout(SanicException):
|
||||||
|
status_code = 408
|
||||||
|
|
||||||
|
|
||||||
class Handler:
|
class Handler:
|
||||||
handlers = None
|
handlers = None
|
||||||
|
|
||||||
|
|
|
@ -250,6 +250,7 @@ class Sanic:
|
||||||
'sock': sock,
|
'sock': sock,
|
||||||
'debug': debug,
|
'debug': debug,
|
||||||
'request_handler': self.handle_request,
|
'request_handler': self.handle_request,
|
||||||
|
'error_handler': self.error_handler,
|
||||||
'request_timeout': self.config.REQUEST_TIMEOUT,
|
'request_timeout': self.config.REQUEST_TIMEOUT,
|
||||||
'request_max_size': self.config.REQUEST_MAX_SIZE,
|
'request_max_size': self.config.REQUEST_MAX_SIZE,
|
||||||
'loop': loop
|
'loop': loop
|
||||||
|
|
|
@ -13,6 +13,8 @@ except ImportError:
|
||||||
|
|
||||||
from .log import log
|
from .log import log
|
||||||
from .request import Request
|
from .request import Request
|
||||||
|
from .response import HTTPResponse
|
||||||
|
from .exceptions import RequestTimeout
|
||||||
|
|
||||||
|
|
||||||
class Signal:
|
class Signal:
|
||||||
|
@ -33,8 +35,8 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
# connection management
|
# connection management
|
||||||
'_total_request_size', '_timeout_handler', '_last_communication_time')
|
'_total_request_size', '_timeout_handler', '_last_communication_time')
|
||||||
|
|
||||||
def __init__(self, *, loop, request_handler, signal=Signal(),
|
def __init__(self, *, loop, request_handler, error_handler,
|
||||||
connections={}, request_timeout=60,
|
signal=Signal(), connections={}, request_timeout=60,
|
||||||
request_max_size=None):
|
request_max_size=None):
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.transport = None
|
self.transport = None
|
||||||
|
@ -45,11 +47,13 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self.signal = signal
|
self.signal = signal
|
||||||
self.connections = connections
|
self.connections = connections
|
||||||
self.request_handler = request_handler
|
self.request_handler = request_handler
|
||||||
|
self.error_handler = error_handler
|
||||||
self.request_timeout = request_timeout
|
self.request_timeout = request_timeout
|
||||||
self.request_max_size = request_max_size
|
self.request_max_size = request_max_size
|
||||||
self._total_request_size = 0
|
self._total_request_size = 0
|
||||||
self._timeout_handler = None
|
self._timeout_handler = None
|
||||||
self._last_request_time = None
|
self._last_request_time = None
|
||||||
|
self._request_handler_task = None
|
||||||
|
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
# Connection
|
# Connection
|
||||||
|
@ -75,7 +79,17 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self._timeout_handler = \
|
self._timeout_handler = \
|
||||||
self.loop.call_later(time_left, self.connection_timeout)
|
self.loop.call_later(time_left, self.connection_timeout)
|
||||||
else:
|
else:
|
||||||
self.bail_out("Request timed out, connection closed")
|
self._request_handler_task.cancel()
|
||||||
|
try:
|
||||||
|
response = self.error_handler.response(
|
||||||
|
self.request, RequestTimeout('Request Timeout'))
|
||||||
|
except Exception as e:
|
||||||
|
response = HTTPResponse(
|
||||||
|
'Request Timeout', RequestTimeout.status_code)
|
||||||
|
self.transport.write(
|
||||||
|
response.output(
|
||||||
|
self.request.version, False, self.request_timeout))
|
||||||
|
self.transport.close()
|
||||||
|
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
# Parsing
|
# Parsing
|
||||||
|
@ -132,7 +146,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self.request.body = body
|
self.request.body = body
|
||||||
|
|
||||||
def on_message_complete(self):
|
def on_message_complete(self):
|
||||||
self.loop.create_task(
|
self._request_handler_task = self.loop.create_task(
|
||||||
self.request_handler(self.request, self.write_response))
|
self.request_handler(self.request, self.write_response))
|
||||||
|
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
|
@ -165,6 +179,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self.request = None
|
self.request = None
|
||||||
self.url = None
|
self.url = None
|
||||||
self.headers = None
|
self.headers = None
|
||||||
|
self._request_handler_task = None
|
||||||
self._total_request_size = 0
|
self._total_request_size = 0
|
||||||
|
|
||||||
def close_if_idle(self):
|
def close_if_idle(self):
|
||||||
|
@ -204,8 +219,8 @@ def trigger_events(events, loop):
|
||||||
loop.run_until_complete(result)
|
loop.run_until_complete(result)
|
||||||
|
|
||||||
|
|
||||||
def serve(host, port, request_handler, before_start=None, after_start=None,
|
def serve(host, port, request_handler, error_handler, before_start=None,
|
||||||
before_stop=None, after_stop=None,
|
after_start=None, before_stop=None, after_stop=None,
|
||||||
debug=False, request_timeout=60, sock=None,
|
debug=False, request_timeout=60, sock=None,
|
||||||
request_max_size=None, reuse_port=False, loop=None):
|
request_max_size=None, reuse_port=False, loop=None):
|
||||||
"""
|
"""
|
||||||
|
@ -240,6 +255,7 @@ def serve(host, port, request_handler, before_start=None, after_start=None,
|
||||||
connections=connections,
|
connections=connections,
|
||||||
signal=signal,
|
signal=signal,
|
||||||
request_handler=request_handler,
|
request_handler=request_handler,
|
||||||
|
error_handler=error_handler,
|
||||||
request_timeout=request_timeout,
|
request_timeout=request_timeout,
|
||||||
request_max_size=request_max_size,
|
request_max_size=request_max_size,
|
||||||
), host, port, reuse_port=reuse_port, sock=sock)
|
), host, port, reuse_port=reuse_port, sock=sock)
|
||||||
|
|
40
tests/test_request_timeout.py
Normal file
40
tests/test_request_timeout.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
from sanic import Sanic
|
||||||
|
import asyncio
|
||||||
|
from sanic.response import text
|
||||||
|
from sanic.exceptions import RequestTimeout
|
||||||
|
from sanic.utils import sanic_endpoint_test
|
||||||
|
from sanic.config import Config
|
||||||
|
|
||||||
|
Config.REQUEST_TIMEOUT = 1
|
||||||
|
request_timeout_app = Sanic('test_request_timeout')
|
||||||
|
request_timeout_default_app = Sanic('test_request_timeout_default')
|
||||||
|
|
||||||
|
|
||||||
|
@request_timeout_app.route('/1')
|
||||||
|
async def handler_1(request):
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
return text('OK')
|
||||||
|
|
||||||
|
|
||||||
|
@request_timeout_app.exception(RequestTimeout)
|
||||||
|
def handler_exception(request, exception):
|
||||||
|
return text('Request Timeout from error_handler.', 408)
|
||||||
|
|
||||||
|
|
||||||
|
def test_server_error_request_timeout():
|
||||||
|
request, response = sanic_endpoint_test(request_timeout_app, uri='/1')
|
||||||
|
assert response.status == 408
|
||||||
|
assert response.text == 'Request Timeout from error_handler.'
|
||||||
|
|
||||||
|
|
||||||
|
@request_timeout_default_app.route('/1')
|
||||||
|
async def handler_2(request):
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
return text('OK')
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_server_error_request_timeout():
|
||||||
|
request, response = sanic_endpoint_test(
|
||||||
|
request_timeout_default_app, uri='/1')
|
||||||
|
assert response.status == 408
|
||||||
|
assert response.text == 'Error: Request Timeout'
|
Loading…
Reference in New Issue
Block a user