2017-09-11 08:17:33 +01:00
|
|
|
from json import JSONDecodeError
|
2017-09-13 01:18:36 +01:00
|
|
|
|
2016-11-26 04:55:45 +00:00
|
|
|
from sanic import Sanic
|
|
|
|
import asyncio
|
|
|
|
from sanic.response import text
|
|
|
|
from sanic.config import Config
|
2017-09-11 08:17:33 +01:00
|
|
|
import aiohttp
|
2018-09-29 18:54:47 +01:00
|
|
|
from aiohttp import TCPConnector
|
2018-10-09 03:40:36 +01:00
|
|
|
from sanic.testing import SanicTestClient, HOST
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
# direct use
|
|
|
|
import packaging
|
|
|
|
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
|
|
|
|
2018-08-16 06:00:23 +01:00
|
|
|
|
|
|
|
aiohttp_version = version.parse(aiohttp.__version__)
|
2016-11-26 04:55:45 +00:00
|
|
|
|
2018-09-29 18:54:47 +01:00
|
|
|
|
2017-09-11 08:17:33 +01:00
|
|
|
class DelayableTCPConnector(TCPConnector):
|
2017-09-13 01:18:36 +01:00
|
|
|
|
|
|
|
class RequestContextManager(object):
|
2017-09-11 08:17:33 +01:00
|
|
|
def __new__(cls, req, delay):
|
2017-09-13 01:18:36 +01:00
|
|
|
cls = super(DelayableTCPConnector.RequestContextManager, cls).\
|
2017-09-11 08:17:33 +01:00
|
|
|
__new__(cls)
|
|
|
|
cls.req = req
|
2017-09-13 01:18:36 +01:00
|
|
|
cls.send_task = None
|
|
|
|
cls.resp = None
|
|
|
|
cls.orig_send = getattr(req, 'send')
|
|
|
|
cls.orig_start = None
|
2017-09-11 08:17:33 +01:00
|
|
|
cls.delay = delay
|
2017-09-13 01:18:36 +01:00
|
|
|
cls._acting_as = req
|
2017-09-11 08:17:33 +01:00
|
|
|
return cls
|
2016-11-26 04:55:45 +00:00
|
|
|
|
2017-09-11 08:17:33 +01:00
|
|
|
def __getattr__(self, item):
|
2017-09-13 01:18:36 +01:00
|
|
|
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):
|
2017-09-13 01:18:36 +01:00
|
|
|
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
|
2017-09-13 01:18:36 +01:00
|
|
|
self.send_task = None
|
|
|
|
self.resp = resp
|
|
|
|
self._acting_as = self.resp
|
|
|
|
self.orig_start = getattr(resp, 'start')
|
|
|
|
|
|
|
|
try:
|
2018-08-16 06:00:23 +01:00
|
|
|
if aiohttp_version >= version.parse("3.3.0"):
|
|
|
|
ret = await self.orig_start(connection)
|
|
|
|
else:
|
2018-10-09 03:40:36 +01:00
|
|
|
ret = await self.orig_start(connection, read_until_eof)
|
2017-09-13 01:18:36 +01:00
|
|
|
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):
|
2017-09-13 01:18:36 +01:00
|
|
|
req = self.req
|
2017-09-11 08:17:33 +01:00
|
|
|
if self.delay and self.delay > 0:
|
2018-10-09 03:40:36 +01:00
|
|
|
# sync_sleep(self.delay)
|
2018-03-02 06:35:58 +00:00
|
|
|
await asyncio.sleep(self.delay)
|
2017-09-13 01:18:36 +01:00
|
|
|
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
|
2018-08-16 06:00:23 +01:00
|
|
|
|
2018-10-09 03:40:36 +01:00
|
|
|
try:
|
|
|
|
return await self.orig_send(*args, **kwargs)
|
|
|
|
except Exception as e:
|
|
|
|
if aiohttp_version < version.parse("3.1.0"):
|
2018-03-29 02:54:59 +01:00
|
|
|
return aiohttp.ClientResponse(req.method, req.url)
|
2018-10-09 03:40:36 +01:00
|
|
|
kw = dict(
|
|
|
|
writer=None,
|
|
|
|
continue100=None,
|
|
|
|
timer=None,
|
|
|
|
request_info=None,
|
|
|
|
traces=[],
|
|
|
|
loop=req.loop,
|
|
|
|
session=None
|
|
|
|
)
|
|
|
|
if aiohttp_version < version.parse("3.3.0"):
|
|
|
|
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
|
2017-09-13 01:18:36 +01:00
|
|
|
|
2018-08-16 06:00:23 +01:00
|
|
|
if aiohttp_version >= version.parse("3.1.0"):
|
2018-03-29 02:54:59 +01:00
|
|
|
# aiohttp changed the request.send method to async
|
|
|
|
async def send(self, *args, **kwargs):
|
2018-10-09 03:40:36 +01:00
|
|
|
return self._send(*args, **kwargs)
|
2018-03-29 02:54:59 +01:00
|
|
|
else:
|
2018-10-09 03:40:36 +01:00
|
|
|
send = _send
|
2016-11-26 04:55:45 +00:00
|
|
|
|
2017-09-11 08:17:33 +01:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
_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
|
2016-11-26 04:55:45 +00:00
|
|
|
|
2018-10-09 03:40:36 +01:00
|
|
|
async def connect(self, req, *args, **kwargs):
|
|
|
|
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:
|
|
|
|
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
|
2016-11-26 04:55:45 +00:00
|
|
|
|
2017-09-11 08:17:33 +01:00
|
|
|
|
|
|
|
class DelayableSanicTestClient(SanicTestClient):
|
2017-09-13 01:18:36 +01:00
|
|
|
def __init__(self, app, loop, request_delay=1):
|
2018-03-16 04:28:52 +00:00
|
|
|
super(DelayableSanicTestClient, self).__init__(app)
|
2017-09-11 08:17:33 +01:00
|
|
|
self._request_delay = request_delay
|
2017-09-13 01:18:36 +01:00
|
|
|
self._loop = None
|
2017-09-11 08:17:33 +01:00
|
|
|
|
|
|
|
async def _local_request(self, method, uri, cookies=None, *args,
|
|
|
|
**kwargs):
|
2017-09-13 01:18:36 +01:00
|
|
|
if self._loop is None:
|
|
|
|
self._loop = asyncio.get_event_loop()
|
2017-09-11 08:17:33 +01:00
|
|
|
if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://' '//')):
|
|
|
|
url = uri
|
|
|
|
else:
|
|
|
|
url = 'http://{host}:{port}{uri}'.format(
|
2017-11-27 18:56:05 +00:00
|
|
|
host=HOST, port=self.port, uri=uri)
|
2017-09-11 08:17:33 +01:00
|
|
|
conn = DelayableTCPConnector(pre_request_delay=self._request_delay,
|
2017-09-13 01:18:36 +01:00
|
|
|
verify_ssl=False, loop=self._loop)
|
|
|
|
async with aiohttp.ClientSession(cookies=cookies, connector=conn,
|
|
|
|
loop=self._loop) as session:
|
2017-09-11 08:17:33 +01:00
|
|
|
# Insert a delay after creating the connection
|
|
|
|
# But before sending the request.
|
|
|
|
|
|
|
|
async with getattr(session, method.lower())(
|
|
|
|
url, *args, **kwargs) as response:
|
|
|
|
try:
|
|
|
|
response.text = await response.text()
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
response.text = None
|
|
|
|
|
|
|
|
try:
|
|
|
|
response.json = await response.json()
|
|
|
|
except (JSONDecodeError,
|
|
|
|
UnicodeDecodeError,
|
|
|
|
aiohttp.ClientResponseError):
|
|
|
|
response.json = None
|
|
|
|
|
|
|
|
response.body = await response.read()
|
|
|
|
return response
|
|
|
|
|
|
|
|
|
2017-09-13 01:18:36 +01:00
|
|
|
Config.REQUEST_TIMEOUT = 2
|
2017-09-11 08:17:33 +01:00
|
|
|
request_timeout_default_app = Sanic('test_request_timeout_default')
|
2017-09-13 01:18:36 +01:00
|
|
|
request_no_timeout_app = Sanic('test_request_no_timeout')
|
2016-11-26 04:55:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
@request_timeout_default_app.route('/1')
|
2017-09-13 01:18:36 +01:00
|
|
|
async def handler1(request):
|
|
|
|
return text('OK')
|
|
|
|
|
|
|
|
|
|
|
|
@request_no_timeout_app.route('/1')
|
|
|
|
async def handler2(request):
|
2016-11-26 04:55:45 +00:00
|
|
|
return text('OK')
|
|
|
|
|
|
|
|
|
|
|
|
def test_default_server_error_request_timeout():
|
2017-09-13 01:18:36 +01:00
|
|
|
client = DelayableSanicTestClient(request_timeout_default_app, None, 3)
|
2017-09-11 08:17:33 +01:00
|
|
|
request, response = client.get('/1')
|
2016-11-26 04:55:45 +00:00
|
|
|
assert response.status == 408
|
|
|
|
assert response.text == 'Error: Request Timeout'
|
2017-09-13 01:18:36 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_default_server_error_request_dont_timeout():
|
|
|
|
client = DelayableSanicTestClient(request_no_timeout_app, None, 1)
|
|
|
|
request, response = client.get('/1')
|
|
|
|
assert response.status == 200
|
|
|
|
assert response.text == 'OK'
|