@@ -32,6 +32,7 @@ pub struct StreamingResponse {
3232 pub status_code : u16 ,
3333 pub headers : Headers ,
3434 pub content_generator : Py < PyAny > ,
35+ pub media_type : String ,
3536}
3637
3738#[ derive( Debug ) ]
@@ -86,11 +87,17 @@ impl Responder for Response {
8687}
8788
8889impl StreamingResponse {
89- pub fn new ( status_code : u16 , headers : Headers , content_generator : Py < PyAny > ) -> Self {
90+ pub fn new (
91+ status_code : u16 ,
92+ headers : Headers ,
93+ content_generator : Py < PyAny > ,
94+ media_type : String ,
95+ ) -> Self {
9096 Self {
9197 status_code,
9298 headers,
9399 content_generator,
100+ media_type,
94101 }
95102 }
96103}
@@ -105,13 +112,15 @@ impl Responder for StreamingResponse {
105112
106113 apply_hashmap_headers ( & mut response_builder, & self . headers ) ;
107114
108- // Optimized headers for SSE streaming
109- response_builder
110- . append_header ( ( "Connection" , "keep-alive" ) )
111- . append_header ( ( "X-Accel-Buffering" , "no" ) ) // Disable nginx buffering
112- . append_header ( ( "Cache-Control" , "no-cache, no-store, must-revalidate" ) )
113- . append_header ( ( "Pragma" , "no-cache" ) )
114- . append_header ( ( "Expires" , "0" ) ) ;
115+ // Only add SSE-specific headers for event-stream responses
116+ if self . media_type == "text/event-stream" {
117+ response_builder
118+ . append_header ( ( "Connection" , "keep-alive" ) )
119+ . append_header ( ( "X-Accel-Buffering" , "no" ) ) // Disable nginx buffering
120+ . append_header ( ( "Cache-Control" , "no-cache, no-store, must-revalidate" ) )
121+ . append_header ( ( "Pragma" , "no-cache" ) )
122+ . append_header ( ( "Expires" , "0" ) ) ;
123+ }
115124
116125 // Create the optimized stream from the Python generator
117126 let stream = create_python_stream ( self . content_generator ) ;
@@ -142,18 +151,29 @@ fn create_python_stream(
142151 // Try to get the next value from the generator (sync)
143152 match gen. call_method0 ( "__next__" ) {
144153 Ok ( value) => {
145- match value. extract :: < String > ( ) {
146- Ok ( string_value) => {
147- debug ! ( "Generator yielded: {}" , string_value) ;
148- Some ( ( string_value, generator) )
149- }
150- Err ( extract_err) => {
151- debug ! (
152- "Failed to extract string from generator value: {}" ,
153- extract_err
154- ) ;
155- None // End of stream
156- }
154+ // Try bytes first (common for binary file streaming),
155+ // then fall back to string extraction
156+ if let Ok ( py_bytes) = value. downcast :: < PyBytes > ( ) {
157+ let data = py_bytes. as_bytes ( ) . to_vec ( ) ;
158+ debug ! ( "Generator yielded {} bytes" , data. len( ) ) ;
159+ Some ( ( data, generator) )
160+ } else if let Ok ( string_value) = value. extract :: < String > ( ) {
161+ debug ! (
162+ "Generator yielded string of len {}" ,
163+ string_value. len( )
164+ ) ;
165+ Some ( ( string_value. into_bytes ( ) , generator) )
166+ } else {
167+ let type_name = value
168+ . get_type ( )
169+ . name ( )
170+ . map ( |n| n. to_string ( ) )
171+ . unwrap_or_else ( |_| "unknown" . to_string ( ) ) ;
172+ debug ! (
173+ "Generator yielded unsupported type: {}" ,
174+ type_name
175+ ) ;
176+ None // End of stream
157177 }
158178 }
159179 Err ( call_err) => {
@@ -171,7 +191,7 @@ fn create_python_stream(
171191 } )
172192 . await
173193 {
174- Ok ( Some ( ( string_value , generator) ) ) => Some ( ( Ok ( Bytes :: from ( string_value ) ) , generator) ) ,
194+ Ok ( Some ( ( data , generator) ) ) => Some ( ( Ok ( Bytes :: from ( data ) ) , generator) ) ,
175195 Ok ( None ) => None ,
176196 Err ( join_err) => {
177197 debug ! (
@@ -583,6 +603,6 @@ impl FromPyObject<'_, '_> for StreamingResponse {
583603 "Successfully extracted StreamingResponse with status {} from type {}" ,
584604 status_code, type_name
585605 ) ;
586- Ok ( StreamingResponse :: new ( status_code, headers, content) )
606+ Ok ( StreamingResponse :: new ( status_code, headers, content, media_type ) )
587607 }
588608}
0 commit comments