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:
Ashley Sommer 2017-09-13 10:18:36 +10:00
parent 173f94216a
commit 8eb59ad4dc
4 changed files with 95 additions and 23 deletions

View File

@ -129,7 +129,7 @@ class Config(dict):
self.KEEP_ALIVE = keep_alive
# Apache httpd server default keepalive timeout = 5 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
# Firefox client hard keepalive limit = 115 seconds

View File

@ -193,7 +193,6 @@ class HttpProtocol(asyncio.Protocol):
log.info('KeepAlive Timeout. Closing connection.')
self.transport.close()
# -------------------------------------------- #
# Parsing
# -------------------------------------------- #

View File

@ -20,10 +20,11 @@ class ReuseableTCPConnector(TCPConnector):
new_conn = yield from super(ReuseableTCPConnector, self)\
.connect(req)
if self.old_proto is not None:
if self.old_proto != new_conn.protocol:
if self.old_proto != new_conn._protocol:
raise RuntimeError(
"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
@ -64,6 +65,8 @@ class ReuseableSanicTestClient(SanicTestClient):
**request_kwargs)
results[-1] = response
except Exception as e2:
import traceback
traceback.print_tb(e2.__traceback__)
exceptions.append(e2)
#Don't stop here! self.app.stop()
@ -80,6 +83,8 @@ class ReuseableSanicTestClient(SanicTestClient):
loop._stopping = False
http_server = loop.run_until_complete(_server_co)
except Exception as e1:
import traceback
traceback.print_tb(e1.__traceback__)
raise e1
self._server = _server = http_server
server.trigger_events(
@ -93,7 +98,9 @@ class ReuseableSanicTestClient(SanicTestClient):
loop.run_until_complete(_server.wait_closed())
self.app.stop()
except Exception as e3:
exceptions.append(e3)
import traceback
traceback.print_tb(e3.__traceback__)
exceptions.append(e3)
if exceptions:
raise ValueError(
"Exception during request: {}".format(exceptions))

View File

@ -1,8 +1,8 @@
from json import JSONDecodeError
from sanic import Sanic
import asyncio
from sanic.response import text
from sanic.exceptions import RequestTimeout
from sanic.config import Config
import aiohttp
from aiohttp import TCPConnector
@ -10,21 +10,68 @@ from sanic.testing import SanicTestClient, HOST, PORT
class DelayableTCPConnector(TCPConnector):
class DelayableHttpRequest(object):
class RequestContextManager(object):
def __new__(cls, req, delay):
cls = super(DelayableTCPConnector.DelayableHttpRequest, cls).\
cls = super(DelayableTCPConnector.RequestContextManager, cls).\
__new__(cls)
cls.req = req
cls.send_task = None
cls.resp = None
cls.orig_send = getattr(req, 'send')
cls.orig_start = None
cls.delay = delay
cls._acting_as = req
return cls
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):
if self.delay and self.delay > 0:
_ = yield from asyncio.sleep(self.delay)
self.req.send(*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
def __init__(self, *args, **kwargs):
_post_connect_delay = kwargs.pop('post_connect_delay', 0)
@ -35,31 +82,37 @@ class DelayableTCPConnector(TCPConnector):
@asyncio.coroutine
def connect(self, req):
req = DelayableTCPConnector.\
DelayableHttpRequest(req, self._pre_request_delay)
d_req = DelayableTCPConnector.\
RequestContextManager(req, self._pre_request_delay)
conn = yield from super(DelayableTCPConnector, self).connect(req)
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
class DelayableSanicTestClient(SanicTestClient):
def __init__(self, app, request_delay=1):
def __init__(self, app, loop, request_delay=1):
super(DelayableSanicTestClient, self).__init__(app)
self._request_delay = request_delay
self._loop = None
async def _local_request(self, method, uri, cookies=None, *args,
**kwargs):
if self._loop is None:
self._loop = asyncio.get_event_loop()
if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://' '//')):
url = uri
else:
url = 'http://{host}:{port}{uri}'.format(
host=HOST, port=PORT, uri=uri)
conn = DelayableTCPConnector(pre_request_delay=self._request_delay,
verify_ssl=False)
async with aiohttp.ClientSession(
cookies=cookies, connector=conn) as session:
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.
@ -81,17 +134,30 @@ class DelayableSanicTestClient(SanicTestClient):
return response
Config.REQUEST_TIMEOUT = 1
Config.REQUEST_TIMEOUT = 2
request_timeout_default_app = Sanic('test_request_timeout_default')
request_no_timeout_app = Sanic('test_request_no_timeout')
@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')
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')
assert response.status == 408
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'