Make client-side cancellation work over the 2026 transports (#3046)
This commit is contained in:
parent
bf4402725d
commit
53117cb3a9
1 changed files with 1213 additions and 102 deletions
|
|
@ -474,6 +474,25 @@ REQUIREMENTS: dict[str, Requirement] = {
|
|||
"never reused within the session."
|
||||
),
|
||||
),
|
||||
"protocol:request-id:caller-supplied": Requirement(
|
||||
source="sdk",
|
||||
behavior=(
|
||||
"A caller can supply the id of a request it sends, so the id is known before any response "
|
||||
"arrives; subscriptions/listen streams are demultiplexed by exactly that id."
|
||||
),
|
||||
note=(
|
||||
f"The demux-by-listen-request-id obligation is the spec's "
|
||||
f"({SPEC_2026_BASE_URL}/basic/patterns/subscriptions#receiving-notifications); supplying the "
|
||||
"id up front is the SDK surface that makes it satisfiable."
|
||||
),
|
||||
added_in="2026-07-28",
|
||||
deferred=(
|
||||
"No public API surface yet: the capability exists at the dispatcher seam "
|
||||
"(CallOptions['request_id'], unit-tested there), but ClientSession.send_request does not "
|
||||
"expose it. The public consumer arrives with the client-side listen driver (Client.listen), "
|
||||
"whose interaction tests will exercise it end to end."
|
||||
),
|
||||
),
|
||||
"protocol:notifications:no-response": Requirement(
|
||||
source=f"{SPEC_BASE_URL}/basic#notifications",
|
||||
behavior=(
|
||||
|
|
@ -484,14 +503,32 @@ REQUIREMENTS: dict[str, Requirement] = {
|
|||
"protocol:cancel:abort-signal": Requirement(
|
||||
source=f"{SPEC_BASE_URL}/basic/utilities/cancellation#cancellation-flow",
|
||||
behavior=(
|
||||
"Cancelling an in-flight request through the client API sends notifications/cancelled with "
|
||||
"the request id and fails the local call."
|
||||
"Abandoning an in-flight request client-side (cancelling the task awaiting it) cancels the "
|
||||
"request itself: the server-side handler stops and the session serves later requests "
|
||||
"normally."
|
||||
),
|
||||
deferred=(
|
||||
"Not implemented in the SDK: there is no public client-side API to cancel an in-flight "
|
||||
"request; cancellation requires hand-constructing the notification (which is how "
|
||||
"protocol:cancel:in-flight exercises the receiving side)."
|
||||
note=(
|
||||
"The per-transport wire spelling (frame vs response-stream close) is pinned separately by "
|
||||
"protocol:cancel:stream-frame and the client-transport:http:cancel-* pair."
|
||||
),
|
||||
arm_exclusions=(
|
||||
ArmExclusion(
|
||||
reason="requires-session",
|
||||
transport="streamable-http-stateless",
|
||||
note=(
|
||||
"The 2025-era cancel frame POSTs on a fresh per-request transport that shares no "
|
||||
"in-flight state with the blocked request, so the handler is never interrupted."
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
"protocol:cancel:abort-scoped": Requirement(
|
||||
source=f"{SPEC_BASE_URL}/basic/utilities/cancellation#behavior-requirements",
|
||||
behavior=(
|
||||
"Abandoning one in-flight request cancels only that request: a concurrent request on the "
|
||||
"same connection keeps running and returns its result."
|
||||
),
|
||||
arm_exclusions=(ArmExclusion(reason="requires-session", transport="streamable-http-stateless"),),
|
||||
),
|
||||
"protocol:cancel:handler-abort-propagates": Requirement(
|
||||
source=f"{SPEC_BASE_URL}/basic/utilities/cancellation#behavior-requirements",
|
||||
|
|
@ -550,6 +587,16 @@ REQUIREMENTS: dict[str, Requirement] = {
|
|||
ArmExclusion(reason="server-initiated-request", spec_version="2026-07-28"),
|
||||
),
|
||||
),
|
||||
"protocol:cancel:stream-frame": Requirement(
|
||||
source=f"{SPEC_2026_BASE_URL}/basic/patterns/cancellation#transport-specific-cancellation",
|
||||
behavior=(
|
||||
"On stream (stdio-shaped) wires at 2026-07-28, abandoning an in-flight request sends exactly "
|
||||
"one notifications/cancelled naming its request id - streams keep the frame spelling of "
|
||||
"cancellation that streamable HTTP dropped."
|
||||
),
|
||||
added_in="2026-07-28",
|
||||
note="Exercised over the in-memory stream pair, the same dual-era wire stdio serves.",
|
||||
),
|
||||
"protocol:cancel:unknown-id-ignored": Requirement(
|
||||
source=f"{SPEC_BASE_URL}/basic/utilities/cancellation#error-handling",
|
||||
behavior=(
|
||||
|
|
@ -3256,6 +3303,30 @@ REQUIREMENTS: dict[str, Requirement] = {
|
|||
transports=("streamable-http",),
|
||||
note="Only observable over HTTP: Accept is an HTTP request header.",
|
||||
),
|
||||
"client-transport:http:cancel-closes-stream": Requirement(
|
||||
source=f"{SPEC_2026_BASE_URL}/basic/transports/streamable-http#cancellation",
|
||||
behavior=(
|
||||
"At 2026-07-28, abandoning an in-flight request closes that request's own POST stream and "
|
||||
"posts nothing further: no notifications/cancelled reaches the server (the revision defines "
|
||||
"no client-to-server notifications), and the server treats the disconnect as cancellation "
|
||||
"of exactly that request."
|
||||
),
|
||||
transports=("streamable-http",),
|
||||
added_in="2026-07-28",
|
||||
supersedes=("client-transport:http:cancel-posts-frame",),
|
||||
note="HTTP-only by nature: the response stream that closing constitutes the signal is an HTTP exchange.",
|
||||
),
|
||||
"client-transport:http:cancel-posts-frame": Requirement(
|
||||
source=f"{SPEC_BASE_URL}/basic/utilities/cancellation#cancellation-flow",
|
||||
behavior=(
|
||||
"At 2025-era revisions, abandoning an in-flight request POSTs exactly one "
|
||||
"notifications/cancelled naming its request id."
|
||||
),
|
||||
transports=("streamable-http",),
|
||||
removed_in="2026-07-28",
|
||||
superseded_by="client-transport:http:cancel-closes-stream",
|
||||
note="HTTP-only by nature: pins that the frame travels as its own POST on the legacy HTTP wire.",
|
||||
),
|
||||
"client-transport:http:concurrent-streams": Requirement(
|
||||
source="sdk",
|
||||
behavior="Multiple concurrent POST-initiated SSE streams each deliver their response to the right caller.",
|
||||
|
|
|
|||
Loading…
Reference in a new issue