From bd28da0abc16bb37384f72d6b45ac8c4c34ce35c Mon Sep 17 00:00:00 2001 From: Channel Cat Date: Fri, 28 Oct 2016 02:56:32 -0700 Subject: [PATCH 1/3] Keep-alive requests stay open if communicating --- sanic/server.py | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/sanic/server.py b/sanic/server.py index e4bff6fc..94f59b37 100644 --- a/sanic/server.py +++ b/sanic/server.py @@ -1,4 +1,5 @@ import asyncio +from functools import partial from inspect import isawaitable from signal import SIGINT, SIGTERM @@ -16,7 +17,6 @@ from .request import Request class Signal: stopped = False - class HttpProtocol(asyncio.Protocol): __slots__ = ( # event loop, connection @@ -26,7 +26,7 @@ class HttpProtocol(asyncio.Protocol): # request config 'request_handler', 'request_timeout', 'request_max_size', # connection management - '_total_request_size', '_timeout_handler') + '_total_request_size', '_timeout_handler', '_last_communication_time') def __init__(self, *, loop, request_handler, signal=Signal(), connections={}, request_timeout=60, @@ -44,6 +44,7 @@ class HttpProtocol(asyncio.Protocol): self.request_max_size = request_max_size self._total_request_size = 0 self._timeout_handler = None + self._last_request_time = None # -------------------------------------------- # @@ -55,6 +56,7 @@ class HttpProtocol(asyncio.Protocol): self._timeout_handler = self.loop.call_later( self.request_timeout, self.connection_timeout) self.transport = transport + self._last_request_time = current_time def connection_lost(self, exc): del self.connections[self] @@ -62,7 +64,14 @@ class HttpProtocol(asyncio.Protocol): self.cleanup() def connection_timeout(self): - self.bail_out("Request timed out, connection closed") + # Check if + time_elapsed = current_time - self._last_request_time + if time_elapsed < self.request_timeout: + time_left = self.request_timeout - time_elapsed + self._timeout_handler = \ + self.loop.call_later(time_left, self.connection_timeout) + else: + self.bail_out("Request timed out, connection closed") # -------------------------------------------- # @@ -133,13 +142,15 @@ class HttpProtocol(asyncio.Protocol): if not keep_alive: self.transport.close() else: + # Record that we received data + self._last_request_time = current_time self.cleanup() except Exception as e: self.bail_out( "Writing request failed, connection closed {}".format(e)) def bail_out(self, message): - log.error(message) + log.debug(message) self.transport.close() def cleanup(self): @@ -159,6 +170,19 @@ class HttpProtocol(asyncio.Protocol): return True return False +# Keep check on the current time +current_time = None +def update_current_time(loop): + """ + Caches the current time, since it is needed + at the end of every keep-alive request to update the request timeout time + :param loop: + :return: + """ + global current_time + current_time = loop.time() + loop.call_later(0.5, partial(update_current_time, loop)) + def trigger_events(events, loop): """ @@ -173,7 +197,6 @@ def trigger_events(events, loop): if isawaitable(result): loop.run_until_complete(result) - def serve(host, port, request_handler, before_start=None, after_start=None, before_stop=None, after_stop=None, debug=False, request_timeout=60, sock=None, @@ -214,6 +237,10 @@ def serve(host, port, request_handler, before_start=None, after_start=None, request_max_size=request_max_size, ), host, port, reuse_port=reuse_port, sock=sock) + # Instead of pulling time at the end of every request, + # pull it once per minute + loop.call_soon(partial(update_current_time, loop)) + try: http_server = loop.run_until_complete(server_coroutine) except Exception: From c44b5551bcc0b7726fe695dbcd03ece5123a4905 Mon Sep 17 00:00:00 2001 From: Channel Cat Date: Fri, 28 Oct 2016 03:13:03 -0700 Subject: [PATCH 2/3] time.time faster than loop.time? --- sanic/server.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sanic/server.py b/sanic/server.py index 94f59b37..5440b706 100644 --- a/sanic/server.py +++ b/sanic/server.py @@ -2,6 +2,7 @@ import asyncio from functools import partial from inspect import isawaitable from signal import SIGINT, SIGTERM +from time import time import httptools @@ -180,8 +181,8 @@ def update_current_time(loop): :return: """ global current_time - current_time = loop.time() - loop.call_later(0.5, partial(update_current_time, loop)) + current_time = time() + loop.call_later(1, partial(update_current_time, loop)) def trigger_events(events, loop): From 707c55fbe71a5e1fc8aad7b1ff41cd0733a8f909 Mon Sep 17 00:00:00 2001 From: Channel Cat Date: Fri, 28 Oct 2016 03:35:30 -0700 Subject: [PATCH 3/3] Fix flake8 --- sanic/server.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sanic/server.py b/sanic/server.py index 5440b706..9c2ea749 100644 --- a/sanic/server.py +++ b/sanic/server.py @@ -18,6 +18,10 @@ from .request import Request class Signal: stopped = False + +current_time = None + + class HttpProtocol(asyncio.Protocol): __slots__ = ( # event loop, connection @@ -171,8 +175,7 @@ class HttpProtocol(asyncio.Protocol): return True return False -# Keep check on the current time -current_time = None + def update_current_time(loop): """ Caches the current time, since it is needed @@ -198,6 +201,7 @@ def trigger_events(events, loop): if isawaitable(result): loop.run_until_complete(result) + def serve(host, port, request_handler, before_start=None, after_start=None, before_stop=None, after_stop=None, debug=False, request_timeout=60, sock=None,