Skip to content

Preserve actual close code during shutdown in websockets implementation#2865

Closed
hrv-dys wants to merge 2 commits intoKludex:mainfrom
hrv-dys:fix/websockets-impl-shutdown-close-code
Closed

Preserve actual close code during shutdown in websockets implementation#2865
hrv-dys wants to merge 2 commits intoKludex:mainfrom
hrv-dys:fix/websockets-impl-shutdown-close-code

Conversation

@hrv-dys
Copy link
Copy Markdown
Contributor

@hrv-dys hrv-dys commented Mar 18, 2026

Summary

In websockets_impl.py, when a ConnectionClosed exception is caught during
asgi_receive() and the server is shutting down (ws_server.closing is True),
the disconnect event always reports code 1012 ("Service Restart"), regardless
of the actual close code from the connection.

This means if a client sends a normal close (1000) that races with the
server's shutdown close (1012), the app always sees 1012 — losing the
information that the client actually closed cleanly.

This fix uses the actual close_code when available, falling back to 1012
only when no close code was received from the connection. In practice:

  • Server-initiated shutdown → fail_connection(1012) → code = 1012 ✓
  • Client closed normally during shutdown → code = 1000 (preserved) ✓
  • Transport lost with no close frame → code = 1012 (fallback) ✓

Belief that guided this fix: don't use a single flag
(ws_server.closing) to overwrite all connection state during shutdown,
because it causes send() and receive() to lose information about what
actually happened on the wire.

Test plan

  • tests/protocols/test_websocket.py passes

When a ConnectionClosed exception is caught in asgi_receive() and the
server is shutting down, use the actual close_code from the connection
if it represents a real close frame (not 1005/1006), falling back to
1012 only when no close frame was received.

Co-Authored-By: Harsha Vardhan <harsha@example.com>
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 18, 2026

Merging this PR will not alter performance

✅ 24 untouched benchmarks


Comparing hrv-dys:fix/websockets-impl-shutdown-close-code (e1c5653) with main (02bed6f)

Open in CodSpeed

The `not in` check doesn't narrow `int | None` to `int` for mypy.
Use isinstance(close_code, int) to properly narrow the type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Kludex Kludex closed this Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants