Deprecate the legacy websockets implementation and default `auto` to websockets-sansio

Merged
KL
5mo ago
+275-221

Moves --ws auto away from the legacy websockets implementation, in preparation for removing it entirely.

  • --ws auto now resolves to WebSocketsSansIOProtocol when websockets is installed (wsproto fallback unchanged).
  • Importing the legacy implementation (via --ws websockets or directly) emits a UvicornDeprecationWarning - a new UserWarning subclass exported from the package root so it is visible by default, following the Starlette approach.
  • The legacy WebSocketProtocol is no longer tested: removed from the ws_protocol_cls matrix and excluded from coverage.
  • Tests migrated from the deprecated websockets.client API to websockets.asyncio.client, allowing the dev pin to move from websockets==13.1 to websockets==16.0 and dropping the four websockets filterwarnings ignores.
  • uvicorn[standard] floor bumped to websockets>=11.0, the first version with the sans-io ServerProtocol that the default implementation now requires.

Notes from the migration:

  • The sansio implementation rejects oversized frames from the frame header instead of buffering the whole message first (better memory behavior than legacy). The abrupt close means a mid-send client can observe 1006 instead of 1009, so the ws_max_size test now asserts the close code the server delivers to the app via websocket.disconnect.
  • Found an upstream websockets edge case while migrating: with permessage-deflate, a compressed message whose decompressed size barely exceeds max_size (~1-2 bytes) is silently truncated to max_size instead of rejected with 1009 - the unconsumed_tail check misses it when all compressed input was consumed. Larger overages are caught. Will report upstream; the affected test disables compression so wire sizes are what is being tested.

Next steps (separate PRs): point the websockets string at WebSocketsSansIOProtocol, alias WebSocketProtocol to it, and deprecate --ws-max-queue (the sansio implementation applies backpressure via pause_reading instead of a bounded queue).

AI Disclaimer

This PR was developed with the assistance of either Claude or Codex. I've reviewed and verified the changes.