Skip to content

Commit f1a1bff

Browse files
kazuki-maKludex
andauthored
Unset the keep-alive timer when upgrading to WebSocket (#3107)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
1 parent 63971ed commit f1a1bff

3 files changed

Lines changed: 21 additions & 10 deletions

File tree

‎tests/protocols/test_http.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,21 @@ async def test_keepalive_timeout_with_pipelined_requests(http_protocol_cls: type
451451
assert protocol.timeout_keep_alive_task is not None
452452

453453

454+
async def test_keepalive_timeout_with_pipelined_websocket_upgrade(
455+
http_protocol_cls: type[HTTPProtocol], ws_protocol_cls: type[WSProtocol]
456+
):
457+
if http_protocol_cls.__name__ == "HttpToolsProtocol":
458+
pytest.skip("httptools never upgrades with the keep-alive timer armed")
459+
460+
app = Response("Hello, world", media_type="text/plain")
461+
462+
protocol = get_connected_protocol(app, http_protocol_cls, ws=ws_protocol_cls)
463+
protocol.data_received(SIMPLE_GET_REQUEST + UPGRADE_REQUEST)
464+
await protocol.loop.run_one()
465+
466+
assert protocol.timeout_keep_alive_task is None
467+
468+
454469
async def test_close(http_protocol_cls: type[HTTPProtocol]):
455470
app = Response(b"", status_code=204, headers={"connection": "close"})
456471

‎uvicorn/protocols/http/h11_impl.py‎

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ def handle_events(self) -> None:
197197
break
198198

199199
elif isinstance(event, h11.Request):
200+
# Pipelined HTTP requests and WebSocket upgrades may be processed after the keep-alive timer is armed.
201+
self._unset_keepalive_if_required()
202+
200203
self.headers = [(key.lower(), value) for key, value in event.headers]
201204
raw_path, _, query_string = event.target.partition(b"?")
202205
path = unquote(raw_path.decode("ascii"))
@@ -231,14 +234,6 @@ def handle_events(self) -> None:
231234
else:
232235
app = self.app
233236

234-
# When starting to process a request, disable the keep-alive
235-
# timeout. Normally we disable this when receiving data from
236-
# client and set back when finishing processing its request.
237-
# However, for pipelined requests processing finishes after
238-
# already receiving the next request and thus the timer may
239-
# be set here, which we don't want.
240-
self._unset_keepalive_if_required()
241-
242237
self.cycle = RequestResponseCycle(
243238
scope=self.scope,
244239
conn=self.conn,

‎uvicorn/protocols/http/zttp_impl.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ def data_received(self, data: bytes) -> None:
145145
def handle_events(self, event: zttp.Event) -> None:
146146
while event is not zttp.NEED_DATA:
147147
if isinstance(event, zttp.Request):
148+
# Pipelined HTTP requests and WebSocket upgrades may be processed after the keep-alive timer is armed.
149+
self._unset_keepalive_if_required()
150+
148151
assert isinstance(event.headers, zttp.HeaderBlock)
149152
self.headers = event.headers.to_list(lowercase_names=True)
150153
path = event.path.decode("ascii")
@@ -181,8 +184,6 @@ def handle_events(self, event: zttp.Event) -> None:
181184
else:
182185
app = self.app
183186

184-
self._unset_keepalive_if_required()
185-
186187
self.cycle = RequestResponseCycle(
187188
scope=self.scope,
188189
conn=self.conn,

0 commit comments

Comments
 (0)