Fixed error where the RequestTimeout test wasn't actually testing the correct behaviour
Fixed error where KeepAliveTimeout wasn't being triggered in the test suite, when using uvloop Fixed test cases when using other asyncio loops such as uvloop Fixed Flake8 linting errors
This commit is contained in:
parent
173f94216a
commit
8eb59ad4dc
|
@ -129,7 +129,7 @@ class Config(dict):
|
||||||
self.KEEP_ALIVE = keep_alive
|
self.KEEP_ALIVE = keep_alive
|
||||||
# Apache httpd server default keepalive timeout = 5 seconds
|
# Apache httpd server default keepalive timeout = 5 seconds
|
||||||
# Nginx server default keepalive timeout = 75 seconds
|
# Nginx server default keepalive timeout = 75 seconds
|
||||||
# Nginx performance tuning guidelines uses keepalive timeout = 15 seconds
|
# Nginx performance tuning guidelines uses keepalive = 15 seconds
|
||||||
# IE client hard keepalive limit = 60 seconds
|
# IE client hard keepalive limit = 60 seconds
|
||||||
# Firefox client hard keepalive limit = 115 seconds
|
# Firefox client hard keepalive limit = 115 seconds
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,6 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
log.info('KeepAlive Timeout. Closing connection.')
|
log.info('KeepAlive Timeout. Closing connection.')
|
||||||
self.transport.close()
|
self.transport.close()
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
# Parsing
|
# Parsing
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
|
|
|
@ -20,10 +20,11 @@ class ReuseableTCPConnector(TCPConnector):
|
||||||
new_conn = yield from super(ReuseableTCPConnector, self)\
|
new_conn = yield from super(ReuseableTCPConnector, self)\
|
||||||
.connect(req)
|
.connect(req)
|
||||||
if self.old_proto is not None:
|
if self.old_proto is not None:
|
||||||
if self.old_proto != new_conn.protocol:
|
if self.old_proto != new_conn._protocol:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"We got a new connection, wanted the same one!")
|
"We got a new connection, wanted the same one!")
|
||||||
self.old_proto = new_conn.protocol
|
print(new_conn.__dict__)
|
||||||
|
self.old_proto = new_conn._protocol
|
||||||
return new_conn
|
return new_conn
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +65,8 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
**request_kwargs)
|
**request_kwargs)
|
||||||
results[-1] = response
|
results[-1] = response
|
||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
|
import traceback
|
||||||
|
traceback.print_tb(e2.__traceback__)
|
||||||
exceptions.append(e2)
|
exceptions.append(e2)
|
||||||
#Don't stop here! self.app.stop()
|
#Don't stop here! self.app.stop()
|
||||||
|
|
||||||
|
@ -80,6 +83,8 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
loop._stopping = False
|
loop._stopping = False
|
||||||
http_server = loop.run_until_complete(_server_co)
|
http_server = loop.run_until_complete(_server_co)
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
|
import traceback
|
||||||
|
traceback.print_tb(e1.__traceback__)
|
||||||
raise e1
|
raise e1
|
||||||
self._server = _server = http_server
|
self._server = _server = http_server
|
||||||
server.trigger_events(
|
server.trigger_events(
|
||||||
|
@ -93,7 +98,9 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
loop.run_until_complete(_server.wait_closed())
|
loop.run_until_complete(_server.wait_closed())
|
||||||
self.app.stop()
|
self.app.stop()
|
||||||
except Exception as e3:
|
except Exception as e3:
|
||||||
exceptions.append(e3)
|
import traceback
|
||||||
|
traceback.print_tb(e3.__traceback__)
|
||||||
|
exceptions.append(e3)
|
||||||
if exceptions:
|
if exceptions:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Exception during request: {}".format(exceptions))
|
"Exception during request: {}".format(exceptions))
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
import asyncio
|
import asyncio
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
from sanic.exceptions import RequestTimeout
|
|
||||||
from sanic.config import Config
|
from sanic.config import Config
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from aiohttp import TCPConnector
|
from aiohttp import TCPConnector
|
||||||
|
@ -10,21 +10,68 @@ from sanic.testing import SanicTestClient, HOST, PORT
|
||||||
|
|
||||||
|
|
||||||
class DelayableTCPConnector(TCPConnector):
|
class DelayableTCPConnector(TCPConnector):
|
||||||
class DelayableHttpRequest(object):
|
|
||||||
|
class RequestContextManager(object):
|
||||||
def __new__(cls, req, delay):
|
def __new__(cls, req, delay):
|
||||||
cls = super(DelayableTCPConnector.DelayableHttpRequest, cls).\
|
cls = super(DelayableTCPConnector.RequestContextManager, cls).\
|
||||||
__new__(cls)
|
__new__(cls)
|
||||||
cls.req = req
|
cls.req = req
|
||||||
|
cls.send_task = None
|
||||||
|
cls.resp = None
|
||||||
|
cls.orig_send = getattr(req, 'send')
|
||||||
|
cls.orig_start = None
|
||||||
cls.delay = delay
|
cls.delay = delay
|
||||||
|
cls._acting_as = req
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item):
|
||||||
return getattr(self.req, item)
|
acting_as = self._acting_as
|
||||||
|
return getattr(acting_as, item)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def start(self, connection, read_until_eof=False):
|
||||||
|
if self.send_task is None:
|
||||||
|
raise RuntimeError("do a send() before you do a start()")
|
||||||
|
resp = yield from self.send_task
|
||||||
|
self.send_task = None
|
||||||
|
self.resp = resp
|
||||||
|
self._acting_as = self.resp
|
||||||
|
self.orig_start = getattr(resp, 'start')
|
||||||
|
|
||||||
|
try:
|
||||||
|
ret = yield from 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()
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def delayed_send(self, *args, **kwargs):
|
||||||
|
req = self.req
|
||||||
|
if self.delay and self.delay > 0:
|
||||||
|
#sync_sleep(self.delay)
|
||||||
|
_ = yield from asyncio.sleep(self.delay)
|
||||||
|
t = req.loop.time()
|
||||||
|
print("sending at {}".format(t), flush=True)
|
||||||
|
conn = next(iter(args)) # first arg is connection
|
||||||
|
try:
|
||||||
|
delayed_resp = self.orig_send(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
return aiohttp.ClientResponse(req.method, req.url)
|
||||||
|
return delayed_resp
|
||||||
|
|
||||||
def send(self, *args, **kwargs):
|
def send(self, *args, **kwargs):
|
||||||
if self.delay and self.delay > 0:
|
gen = self.delayed_send(*args, **kwargs)
|
||||||
_ = yield from asyncio.sleep(self.delay)
|
task = self.req.loop.create_task(gen)
|
||||||
self.req.send(*args, **kwargs)
|
self.send_task = task
|
||||||
|
self._acting_as = task
|
||||||
|
return self
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
_post_connect_delay = kwargs.pop('post_connect_delay', 0)
|
_post_connect_delay = kwargs.pop('post_connect_delay', 0)
|
||||||
|
@ -35,31 +82,37 @@ class DelayableTCPConnector(TCPConnector):
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def connect(self, req):
|
def connect(self, req):
|
||||||
req = DelayableTCPConnector.\
|
d_req = DelayableTCPConnector.\
|
||||||
DelayableHttpRequest(req, self._pre_request_delay)
|
RequestContextManager(req, self._pre_request_delay)
|
||||||
conn = yield from super(DelayableTCPConnector, self).connect(req)
|
conn = yield from super(DelayableTCPConnector, self).connect(req)
|
||||||
if self._post_connect_delay and self._post_connect_delay > 0:
|
if self._post_connect_delay and self._post_connect_delay > 0:
|
||||||
_ = yield from asyncio.sleep(self._post_connect_delay)
|
_ = yield from 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
|
return conn
|
||||||
|
|
||||||
|
|
||||||
class DelayableSanicTestClient(SanicTestClient):
|
class DelayableSanicTestClient(SanicTestClient):
|
||||||
def __init__(self, app, request_delay=1):
|
def __init__(self, app, loop, request_delay=1):
|
||||||
super(DelayableSanicTestClient, self).__init__(app)
|
super(DelayableSanicTestClient, self).__init__(app)
|
||||||
self._request_delay = request_delay
|
self._request_delay = request_delay
|
||||||
|
self._loop = None
|
||||||
|
|
||||||
async def _local_request(self, method, uri, cookies=None, *args,
|
async def _local_request(self, method, uri, cookies=None, *args,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
if self._loop is None:
|
||||||
|
self._loop = asyncio.get_event_loop()
|
||||||
if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://' '//')):
|
if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://' '//')):
|
||||||
url = uri
|
url = uri
|
||||||
else:
|
else:
|
||||||
url = 'http://{host}:{port}{uri}'.format(
|
url = 'http://{host}:{port}{uri}'.format(
|
||||||
host=HOST, port=PORT, uri=uri)
|
host=HOST, port=PORT, uri=uri)
|
||||||
|
|
||||||
conn = DelayableTCPConnector(pre_request_delay=self._request_delay,
|
conn = DelayableTCPConnector(pre_request_delay=self._request_delay,
|
||||||
verify_ssl=False)
|
verify_ssl=False, loop=self._loop)
|
||||||
async with aiohttp.ClientSession(
|
async with aiohttp.ClientSession(cookies=cookies, connector=conn,
|
||||||
cookies=cookies, connector=conn) as session:
|
loop=self._loop) as session:
|
||||||
# Insert a delay after creating the connection
|
# Insert a delay after creating the connection
|
||||||
# But before sending the request.
|
# But before sending the request.
|
||||||
|
|
||||||
|
@ -81,17 +134,30 @@ class DelayableSanicTestClient(SanicTestClient):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
Config.REQUEST_TIMEOUT = 1
|
Config.REQUEST_TIMEOUT = 2
|
||||||
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_timeout_default_app.route('/1')
|
@request_timeout_default_app.route('/1')
|
||||||
async def handler(request):
|
async def handler1(request):
|
||||||
|
return text('OK')
|
||||||
|
|
||||||
|
|
||||||
|
@request_no_timeout_app.route('/1')
|
||||||
|
async def handler2(request):
|
||||||
return text('OK')
|
return text('OK')
|
||||||
|
|
||||||
|
|
||||||
def test_default_server_error_request_timeout():
|
def test_default_server_error_request_timeout():
|
||||||
client = DelayableSanicTestClient(request_timeout_default_app, 2)
|
client = DelayableSanicTestClient(request_timeout_default_app, None, 3)
|
||||||
request, response = client.get('/1')
|
request, response = client.get('/1')
|
||||||
assert response.status == 408
|
assert response.status == 408
|
||||||
assert response.text == 'Error: Request Timeout'
|
assert response.text == 'Error: Request Timeout'
|
||||||
|
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user