Skip to content

[L01.01.11.35] Decide and implement WebSocket support (RFC 6455 framing over Upgrade and extended CONNECT) #765

Description

@dotnetcadet

Summary

Captured 2026-07-03 during the Http/Web requirements audit; every claim below was adversarially verified against the codebase before filing.

Acceptance Criteria

  • A new Assimalign.Cohesion.Http.WebSockets package implements an RFC 6455 frame codec: FIN/RSV bits, all opcodes, 7/16/64-bit payload lengths, client-to-server mask enforcement (unmasked client frames fail the connection with close code 1002), with round-trip encode/decode unit tests including fragmented messages and interleaved control frames.
  • HTTP/1.1 opening handshake is implemented over IHttpProtocolUpgrade: Sec-WebSocket-Accept is computed as Base64(SHA-1(key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11)), Sec-WebSocket-Version: 13 is required (426 with supported version advertised otherwise), and subprotocol negotiation uses the correct Sec-WebSocket-Protocol header name — including fixing the misnamed 'Sec-WebSocket-ProtocolType' entries in HttpHeaderKey.cs (lines 258-259 and 315-316).
  • HTTP/2 and HTTP/3 bootstrap accepts a valid extended CONNECT with :protocol = websocket via IHttpExtendedConnectFeature per RFC 8441/RFC 9220 (closing [L01.01.11.14.02] Implement RFC 8441 bootstrapping for WebSockets over HTTP/2 #381/[L01.01.11.14.03] Implement RFC 9220 bootstrapping for WebSockets over HTTP/3 #382), omitting Sec-WebSocket-Key/Accept as RFC 8441 section 5 requires, with tests for valid bootstrap, missing enablement settings, and malformed pseudo-headers.
  • Control-frame semantics are covered: ping answered with pong echoing payload, close handshake with close-code validation (protocol-reserved codes rejected), control frames over 125 bytes or fragmented fail with 1002, and invalid UTF-8 in text messages fails with 1007 — each with unit tests.
  • permessage-deflate (RFC 7692) is either implemented (Sec-WebSocket-Extensions negotiation, client/server_no_context_takeover and max_window_bits parameters, RSV1 compressed-message flag, DeflateStream-based, with compression round-trip tests) or cleanly rejected during negotiation so unnegotiated RSV1 frames fail with 1002 — no silent acceptance.
  • The package builds with IsAotCompatible=true, uses no reflection or runtime codegen, passes trim analysis, and follows repo standards (interface-first public surface with internal implementations, file-scoped namespaces, CohesionProjectReference, XML docs on all public APIs).

Standards and Compliance

  • RFC 6455 (frame codec: FIN/RSV/opcodes/masking, close-code semantics, Sec-WebSocket-Accept SHA-1 handshake, UTF-8 validation)
  • RFC 8441 / RFC 9220 (bootstrapping over h2/h3 extended CONNECT — no Sec-WebSocket-Key/Accept there)
  • RFC 7692 (permessage-deflate) — implement or cleanly reject during negotiation
  • Suggested project family: Assimalign.Cohesion.Http.WebSockets (framing + handshake), internal impls, interface-first, AOT-safe span codec

Implementation notes (from the audit)

The absence is a recorded package-level design decision, not an oversight: both transport-adjacent packages (Http.Connections, Http.ExtendedConnect) declare RFC 6455 framing and the post-accept tunnel a non-goal, on the rationale that Cohesion currently retains no WebSocket API surface. Open issues #380-382 keep only the conditional h2/h3 bootstrap question alive. If WebSocket support is ever brought in scope, the change lands as a NEW standalone package (e.g., libraries/Http/Assimalign.Cohesion.Http.WebSockets) layered on the two existing seams — IHttpProtocolUpgrade.AcceptAsync (HTTP/1.1 101 + raw stream surrender) and IHttpExtendedConnectFeature (h2/h3 :protocol recognition) — leaving the transports untouched per their documented boundaries. Prerequisites: (1) fix HttpHeaderKey.cs lines 258-259/315-316 where SecWebSocketProtocol and WebSocketSubProtocols both emit the non-existent header "Sec-WebSocket-ProtocolType" instead of "Sec-WebSocket-Protocol"; (2) for HTTP/3 bootstrap (#382), the server-side unidirectional control stream must be implemented so SETTINGS_ENABLE_CONNECT_PROTOCOL can be advertised (Connections DESIGN.md lines 631-640); (3) issue #380 (define the extension boundary) should be resolved first since #381/#382 are explicitly conditional on the surface being retained. Boundaries: interface-first (implementations internal), file-scoped namespaces, CohesionProjectReference only, no Microsoft.Extensions.*, IsAotCompatible=true — the frame codec must be reflection-free (byte/span manipulation) and permessage-deflate should use BCL System.IO.Compression.DeflateStream, both AOT/trim-safe. Any new issue should either supersede or explicitly link #380-382 to avoid double-tracking the bootstrap plumbing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions