Skip to content

Commit

Permalink
Make HTTP connections start in IDLE stage, avoiding delays and error …
Browse files Browse the repository at this point in the history
…messages (#2268)

* Make all new connections start in IDLE stage, and switch to REQUEST stage only once any bytes are received from client. This makes new connections without any request obey keepalive timeout rather than request timeout like they currently do.

* Revert typo

* Remove request timeout endpoint test which is no longer working (still tested by mocking). Fix mock timeout test setup.

Co-authored-by: L. Karkkainen <tronic@users.noreply.github.com>
  • Loading branch information
Tronic and Tronic committed Nov 16, 2021
1 parent cde02b5 commit b731a6b
Show file tree
Hide file tree
Showing 3 changed files with 9 additions and 123 deletions.
22 changes: 8 additions & 14 deletions sanic/http.py
Expand Up @@ -105,7 +105,6 @@ def __init__(self, protocol):
self.keep_alive = True
self.stage: Stage = Stage.IDLE
self.dispatch = self.protocol.app.dispatch
self.init_for_request()

def init_for_request(self):
"""Init/reset all per-request variables."""
Expand All @@ -129,14 +128,20 @@ async def http1(self):
"""
HTTP 1.1 connection handler
"""
while True: # As long as connection stays keep-alive
# Handle requests while the connection stays reusable
while self.keep_alive and self.stage is Stage.IDLE:
self.init_for_request()
# Wait for incoming bytes (in IDLE stage)
if not self.recv_buffer:
await self._receive_more()
self.stage = Stage.REQUEST
try:
# Receive and handle a request
self.stage = Stage.REQUEST
self.response_func = self.http1_response_header

await self.http1_request_header()

self.stage = Stage.HANDLER
self.request.conn_info = self.protocol.conn_info
await self.protocol.request_handler(self.request)

Expand Down Expand Up @@ -187,16 +192,6 @@ async def http1(self):
if self.response:
self.response.stream = None

# Exit and disconnect if no more requests can be taken
if self.stage is not Stage.IDLE or not self.keep_alive:
break

self.init_for_request()

# Wait for the next request
if not self.recv_buffer:
await self._receive_more()

async def http1_request_header(self): # no cov
"""
Receive and parse request header into self.request.
Expand Down Expand Up @@ -299,7 +294,6 @@ async def http1_request_header(self): # no cov

# Remove header and its trailing CRLF
del buf[: pos + 4]
self.stage = Stage.HANDLER
self.request, request.stream = request, self
self.protocol.state["requests_count"] += 1

Expand Down
109 changes: 0 additions & 109 deletions tests/test_request_timeout.py

This file was deleted.

1 change: 1 addition & 0 deletions tests/test_timeout_logic.py
Expand Up @@ -26,6 +26,7 @@ def protocol(app, mock_transport):
protocol = HttpProtocol(loop=loop, app=app)
protocol.connection_made(mock_transport)
protocol._setup_connection()
protocol._http.init_for_request()
protocol._task = Mock(spec=asyncio.Task)
protocol._task.cancel = Mock()
return protocol
Expand Down

0 comments on commit b731a6b

Please sign in to comment.