fix(gateway): send CORS headers on session chat/stream and run-events SSE - #90673
Open
gaomind wants to merge 1 commit into
Open
fix(gateway): send CORS headers on session chat/stream and run-events SSE#90673gaomind wants to merge 1 commit into
gaomind wants to merge 1 commit into
Conversation
… SSE
The CORS middleware cannot amend a StreamResponse after prepare() flushes
its headers, so SSE handlers must resolve CORS up front.
_write_sse_chat_completion and _write_sse_responses already do this (with a
comment explaining why), but two SSE exits were missed:
* POST /api/sessions/{session_id}/chat/stream
* GET /v1/runs/{run_id}/events
A cross-origin browser client therefore received a 200 it was forbidden to
read: the request succeeded, the agent ran (and billed tokens), but the
caller saw only a generic fetch error. Both surfaces are advertised to
browser clients via /v1/capabilities (session_chat_streaming,
run_events_sse), so they should actually be browser-readable.
Same _cors_headers_for_origin policy as every other endpoint — configured
allowlist only, and callers without an Origin header keep exactly the old
header surface (pinned by a negative test).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Thanks for the triage link-up — to make the relationship explicit for reviewers:
Credit to @Tranquil-Flow and @kawanoii for identifying the per-endpoint gaps first. If maintainers prefer landing either of those, I'm happy to rebase this down to the remaining endpoint — otherwise this can land as the single omnibus fix. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Two SSE endpoints return
200 text/event-streamwithout anyAccess-Control-*headers, so a cross-origin browser client is forbidden from reading the response body:POST /api/sessions/{session_id}/chat/streamGET /v1/runs/{run_id}/eventsThe failure mode is nasty: the request succeeds server-side — the agent runs and consumes model tokens — but the browser surfaces only a generic
Failed to fetch. Both surfaces are advertised to browser clients via/v1/capabilities(session_chat_streaming,run_events_sse), so as shipped the capability announcement is not honoured for cross-origin callers.Root cause
The CORS middleware cannot inject headers into a
web.StreamResponseafterprepare()flushes them._write_sse_chat_completionand_write_sse_responsesalready resolve CORS up front for exactly this reason (the comment is in the file), but these two exits were missed.Fix
Apply the same idiom at both sites: resolve
self._cors_headers_for_origin(origin)before constructing theStreamResponse. Same configured-allowlist policy as every other endpoint — nothing is opened up that the non-streaming endpoints don't already allow, and callers without anOriginheader keep exactly the old header surface.Tests
Three tests added to
tests/gateway/test_session_api.py:Access-Control-Allow-Originfor an allowed originOriginadds no CORS headers (negative — non-browser callers unchanged)Access-Control-Allow-OriginAll three fail without the fix; full
tests/gateway/test_session_api.pysuite passes with it.Found while integrating a cross-origin SPA that talks directly to the gateway.
🤖 Generated with Claude Code