Skip to content

Streamable HTTP: pre-existing hardening items from the #2547 review #2562

Description

@felixweinberger

Three pre-existing Streamable HTTP transport items surfaced during the #2547 review that are independent of the keep-alive and delivery work there. Collecting them here so they don't get lost; each is small enough to fix individually.

  1. Protocol-version gate accepts unvalidated strings on initialize. The priming/resumability gate compares protocolVersion >= '2025-11-25' lexicographically, and for initialize requests the string comes verbatim from the request body without being checked against SUPPORTED_PROTOCOL_VERSIONS (non-initialize requests are header-validated). Unsupported strings that sort above the threshold (draft, 2026-01-01) enable priming events and the polling callbacks for the initialize stream even though the negotiated version in the response may be lower. Gate on the negotiated version instead.

  2. Concurrent DELETE requests can fire onsessionclosed twice. While the first DELETE is parked on the callback await, _closed is still false, so a second DELETE passes the guards and fires the callback again for the same session. A flag set before invoking the callback closes the window.

  3. The EventStore contract has no session scoping for the standalone stream. Every transport stores standalone server-initiated messages under the fixed id _GET_stream, so a store instance shared across sessions (module-level store in ssePollingExample, or any multi-node shared store) mixes sessions on standalone resume: one session's resume can replay another session's notifications. The contract needs a session dimension, or documented single-session store usage.

Applies to both main and v1.x.

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