fix: support bytes in StreamingResponse generators#1308
fix: support bytes in StreamingResponse generators#1308
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds byte-capable streaming across Python and Rust layers: StreamingResponse/SSEResponse now accept generators yielding bytes or str; StreamingResponse carries media_type to conditionally emit SSE headers; three new streaming endpoints and integration tests for binary, file, and text streaming were added. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Router as Robyn Router
participant Handler as Rust Streaming Handler
participant PyGen as Python Generator
Client->>Router: GET /stream/bytes
Router->>Handler: invoke endpoint -> StreamingResponse(media_type, generator)
Handler->>Client: send response headers (status, Content-Type)
Handler->>PyGen: start generator (sync/async)
loop per yield
PyGen->>Handler: yield (bytes or str)
Handler->>Handler: if str -> encode to bytes
Handler->>Client: stream bytes chunk
end
PyGen->>Handler: StopIteration / end
Handler->>Client: close stream
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
3e5bb99 to
8d81bad
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/types/response.rs (1)
562-577:⚠️ Potential issue | 🟡 MinorFallback to
"text/event-stream"on extraction failure could mask bugs.If
media_typeextraction fails (lines 570, 575), the code silently falls back to"text/event-stream", which would cause SSE headers to be injected for what might be a binary streaming response. Consider returning an error instead, since a missing or non-extractablemedia_typeon an object that passed thehas_media_typecheck (line 510) would indicate a real problem.
🤖 Fix all issues with AI agents
In `@src/types/response.rs`:
- Around line 115-123: The SSE-specific headers are being added twice: once
during extraction in FromPyObject (where Headers may get "Cache-Control" and
"Connection") and again unconditionally in respond_to after
apply_hashmap_headers; update respond_to (the method that calls
apply_hashmap_headers and then appends SSE headers) to only append each SSE
header if it is not already present on self.headers (or use whatever lookup
method the Headers type provides), so avoid duplicate "Connection",
"Cache-Control", "X-Accel-Buffering", "Pragma", and "Expires" values; reference
respond_to, apply_hashmap_headers, FromPyObject, and the Headers struct to
locate and implement the conditional checks.
🧹 Nitpick comments (2)
integration_tests/base_routes.py (1)
1359-1372: RedundantContent-Typeheader alongsidemedia_type.The
Content-Typeis specified both inheadersand implicitly viamedia_type. Currently this works becausemedia_typeonly auto-sets headers for"text/event-stream", but if that logic changes, these could conflict. Consider setting onlymedia_typeand letting the framework derive theContent-Typeheader, or document why both are needed.This same pattern repeats in the
/stream/bytes_file(line 1391-1394) and/stream/mixed_text(line 1411) endpoints.robyn/responses.py (1)
124-151: Consider auto-settingContent-Typefrommedia_typefor non-SSE streaming responses.Currently,
StreamingResponse.__init__only setsContent-Typewhenmedia_type == "text/event-stream"(line 149). For other media types (e.g.,"application/octet-stream"), users must manually pass theContent-Typeheader themselves. This creates a subtle API pitfall wheremedia_typecontrols SSE header injection but does not set the response'sContent-Typefor non-SSE types.Suggested improvement
# Set default SSE headers if media_type == "text/event-stream": self.headers.set("Content-Type", "text/event-stream") # Cache-Control and Connection headers are set by Rust layer with optimized headers + else: + # Set Content-Type from media_type if not already provided + if not self.headers.contains("Content-Type"): + self.headers.set("Content-Type", media_type)
Merging this PR will not alter performance
Performance Changes
Comparing |
018bf11 to
79eef64
Compare
5fb08e0 to
0472e42
Compare
3ca21d3 to
2af6d3f
Compare
6c240d4 to
e69d5c4
Compare
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/types/response.rs (1)
564-580:⚠️ Potential issue | 🟡 MinorRisky default: falling back to
"text/event-stream"on extraction error.If
media_typeis present but has an unexpected type (passes thehasattrcheck on line 513 but failsextract::<String>), this silently defaults to"text/event-stream". That would inject SSE headers into a binary streaming response — contrary to the user's intent.Consider returning the extraction error instead of silently defaulting:
Proposed fix
let media_type: String = match obj.getattr("media_type") { Ok(attr) => match attr.extract() { Ok(media_type) => { debug!("Successfully extracted media_type: {}", media_type); media_type } Err(e) => { debug!("Failed to extract media_type: {}", e); - "text/event-stream".to_string() + return Err(e); } }, Err(e) => { debug!("Failed to get media_type attribute: {}", e); - "text/event-stream".to_string() + return Err(e); } };
Description
This PR fixes #1236
Summary
This PR
PR Checklist
Please ensure that:
Pre-Commit Instructions:
Summary by CodeRabbit
New Features
Bug Fixes
Tests