sanic/tests/test_request_timeout.py

234 lines
7.6 KiB
Python
Raw Normal View History

from json import JSONDecodeError
from sanic import Sanic
import asyncio
from sanic.response import text
from sanic.config import Config
import aiohttp
2018-09-29 18:54:47 +01:00
from aiohttp import TCPConnector
from sanic.testing import SanicTestClient, HOST
try:
try:
# direct use
import packaging
2018-12-30 11:18:06 +00:00
version = packaging.version
except (ImportError, AttributeError):
# setuptools v39.0 and above.
try:
from setuptools.extern import packaging
except ImportError:
# Before setuptools v39.0
from pkg_resources.extern import packaging
version = packaging.version
except ImportError:
raise RuntimeError("The 'packaging' library is missing.")
2018-09-29 18:54:47 +01:00
aiohttp_version = version.parse(aiohttp.__version__)
2018-09-29 18:54:47 +01:00
class DelayableTCPConnector(TCPConnector):
class RequestContextManager(object):
def __new__(cls, req, delay):
2018-12-30 11:18:06 +00:00
cls = super(
DelayableTCPConnector.RequestContextManager, cls
).__new__(cls)
cls.req = req
cls.send_task = None
cls.resp = None
2018-12-30 11:18:06 +00:00
cls.orig_send = getattr(req, "send")
cls.orig_start = None
cls.delay = delay
cls._acting_as = req
return cls
def __getattr__(self, item):
acting_as = self._acting_as
return getattr(acting_as, item)
2018-03-02 06:35:58 +00:00
async def start(self, connection, read_until_eof=False):
if self.send_task is None:
raise RuntimeError("do a send() before you do a start()")
2018-03-02 06:35:58 +00:00
resp = await self.send_task
self.send_task = None
self.resp = resp
self._acting_as = self.resp
2018-12-30 11:18:06 +00:00
self.orig_start = getattr(resp, "start")
try:
if aiohttp_version >= version.parse("3.3.0"):
ret = await self.orig_start(connection)
else:
ret = await self.orig_start(connection, read_until_eof)
except Exception as e:
raise e
return ret
def close(self):
if self.resp is not None:
self.resp.close()
if self.send_task is not None:
self.send_task.cancel()
2018-03-02 06:35:58 +00:00
async def delayed_send(self, *args, **kwargs):
req = self.req
if self.delay and self.delay > 0:
# sync_sleep(self.delay)
2018-03-02 06:35:58 +00:00
await asyncio.sleep(self.delay)
t = req.loop.time()
print("sending at {}".format(t), flush=True)
2018-10-22 21:25:38 +01:00
next(iter(args)) # first arg is connection
try:
return await self.orig_send(*args, **kwargs)
except Exception as e:
if aiohttp_version < version.parse("3.1.0"):
return aiohttp.ClientResponse(req.method, req.url)
kw = dict(
writer=None,
continue100=None,
timer=None,
request_info=None,
traces=[],
loop=req.loop,
2018-12-30 11:18:06 +00:00
session=None,
)
if aiohttp_version < version.parse("3.3.0"):
2018-12-30 11:18:06 +00:00
kw["auto_decompress"] = None
return aiohttp.ClientResponse(req.method, req.url, **kw)
def _send(self, *args, **kwargs):
gen = self.delayed_send(*args, **kwargs)
task = self.req.loop.create_task(gen)
self.send_task = task
self._acting_as = task
return self
if aiohttp_version >= version.parse("3.1.0"):
# aiohttp changed the request.send method to async
async def send(self, *args, **kwargs):
return self._send(*args, **kwargs)
2018-12-30 11:18:06 +00:00
else:
send = _send
def __init__(self, *args, **kwargs):
2018-12-30 11:18:06 +00:00
_post_connect_delay = kwargs.pop("post_connect_delay", 0)
_pre_request_delay = kwargs.pop("pre_request_delay", 0)
super(DelayableTCPConnector, self).__init__(*args, **kwargs)
self._post_connect_delay = _post_connect_delay
self._pre_request_delay = _pre_request_delay
async def connect(self, req, *args, **kwargs):
2018-12-30 11:18:06 +00:00
d_req = DelayableTCPConnector.RequestContextManager(
req, self._pre_request_delay
)
conn = await super(DelayableTCPConnector, self).connect(
req, *args, **kwargs
)
if self._post_connect_delay and self._post_connect_delay > 0:
2018-12-30 11:18:06 +00:00
await asyncio.sleep(self._post_connect_delay, loop=self._loop)
req.send = d_req.send
t = req.loop.time()
print("Connected at {}".format(t), flush=True)
return conn
class DelayableSanicTestClient(SanicTestClient):
def __init__(self, app, loop, request_delay=1):
2018-03-16 04:28:52 +00:00
super(DelayableSanicTestClient, self).__init__(app)
self._request_delay = request_delay
self._loop = None
2018-12-30 11:18:06 +00:00
async def _local_request(self, method, uri, cookies=None, *args, **kwargs):
if self._loop is None:
self._loop = asyncio.get_event_loop()
2018-12-30 11:18:06 +00:00
if uri.startswith(("http:", "https:", "ftp:", "ftps://" "//")):
url = uri
else:
2018-12-30 11:18:06 +00:00
url = "http://{host}:{port}{uri}".format(
host=HOST, port=self.port, uri=uri
)
conn = DelayableTCPConnector(
pre_request_delay=self._request_delay,
verify_ssl=False,
loop=self._loop,
)
async with aiohttp.ClientSession(
cookies=cookies, connector=conn, loop=self._loop
) as session:
# Insert a delay after creating the connection
# But before sending the request.
async with getattr(session, method.lower())(
2018-12-30 11:18:06 +00:00
url, *args, **kwargs
) as response:
try:
response.text = await response.text()
except UnicodeDecodeError:
response.text = None
try:
response.json = await response.json()
2018-12-30 11:18:06 +00:00
except (
JSONDecodeError,
UnicodeDecodeError,
aiohttp.ClientResponseError,
):
response.json = None
response.body = await response.read()
return response
Config.REQUEST_TIMEOUT = 0.6
2018-12-30 11:18:06 +00:00
request_timeout_default_app = Sanic("test_request_timeout_default")
request_no_timeout_app = Sanic("test_request_no_timeout")
2018-12-30 11:18:06 +00:00
@request_timeout_default_app.route("/1")
async def handler1(request):
2018-12-30 11:18:06 +00:00
return text("OK")
2018-12-30 11:18:06 +00:00
@request_no_timeout_app.route("/1")
async def handler2(request):
2018-12-30 11:18:06 +00:00
return text("OK")
2018-12-30 11:18:06 +00:00
@request_timeout_default_app.websocket("/ws1")
async def ws_handler1(request, ws):
2018-12-30 11:18:06 +00:00
await ws.send("OK")
def test_default_server_error_request_timeout():
client = DelayableSanicTestClient(request_timeout_default_app, None, 2)
2018-12-30 11:18:06 +00:00
request, response = client.get("/1")
assert response.status == 408
2018-12-30 11:18:06 +00:00
assert response.text == "Error: Request Timeout"
def test_default_server_error_request_dont_timeout():
client = DelayableSanicTestClient(request_no_timeout_app, None, 0.2)
2018-12-30 11:18:06 +00:00
request, response = client.get("/1")
assert response.status == 200
2018-12-30 11:18:06 +00:00
assert response.text == "OK"
def test_default_server_error_websocket_request_timeout():
2018-12-30 11:18:06 +00:00
headers = {
"Upgrade": "websocket",
"Connection": "upgrade",
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
"Sec-WebSocket-Version": "13",
}
client = DelayableSanicTestClient(request_timeout_default_app, None, 2)
2018-12-30 11:18:06 +00:00
request, response = client.get("/ws1", headers=headers)
assert response.status == 408
2018-12-30 11:18:06 +00:00
assert response.text == "Error: Request Timeout"