@@ -276,12 +276,13 @@ pub enum PublisherResponse {
276276 } ,
277277 /// Non-processable 2xx response (images, fonts, video). The caller must:
278278 /// 1. Call `finalize_response()` on the response
279- /// 2. Call `response.stream_to_client()` to get a `StreamingBody`
280- /// 3. Copy body bytes directly via `io::copy(&mut body, &mut streaming_body)`
281- /// 4. Call `StreamingBody::finish()`
279+ /// 2. Reattach the body via `response.set_body(body)`
280+ /// 3. Call `response.send_to_client()`
282281 ///
283- /// `Content-Length` is removed because `stream_to_client()` uses chunked
284- /// transfer encoding. The body content is unmodified.
282+ /// `Content-Length` is preserved — the body is unmodified. Using
283+ /// `send_to_client()` instead of `stream_to_client()` avoids chunked
284+ /// encoding overhead. Fastly streams the body from its internal buffer
285+ /// without copying into WASM memory.
285286 PassThrough {
286287 /// Response with all headers set but body not yet written.
287288 response : Response ,
@@ -462,12 +463,12 @@ pub fn handle_publisher_request(
462463
463464 // Stream non-processable 2xx responses directly to avoid buffering
464465 // large binaries (images, fonts, video) in memory.
465- // Exclude 204 No Content — it must not have a message body, and
466- // stream_to_client() would add chunked Transfer-Encoding.
466+ // Content-Length is preserved — the body is unmodified, so the
467+ // browser knows the exact size for progress/layout.
468+ // Exclude 204 No Content — it must not have a message body.
467469 let status = response. get_status ( ) ;
468470 if status. is_success ( ) && status != StatusCode :: NO_CONTENT && !should_process {
469471 let body = response. take_body ( ) ;
470- response. remove_header ( header:: CONTENT_LENGTH ) ;
471472 return Ok ( PublisherResponse :: PassThrough { response, body } ) ;
472473 }
473474
@@ -830,33 +831,34 @@ mod tests {
830831 }
831832
832833 #[ test]
833- fn pass_through_preserves_body_and_removes_content_length ( ) {
834- // Simulate the PassThrough path: take body, remove Content-Length,
835- // io::copy to output. Verify byte-for-byte identity.
834+ fn pass_through_preserves_body_and_content_length ( ) {
835+ // Simulate the PassThrough path: take body, reattach, send.
836+ // Verify byte-for-byte identity and Content-Length preservation .
836837 let image_bytes: Vec < u8 > = ( 0 ..=255 ) . cycle ( ) . take ( 4096 ) . collect ( ) ;
837838
838839 let mut response = Response :: from_status ( StatusCode :: OK ) ;
839840 response. set_header ( "content-type" , "image/png" ) ;
840841 response. set_header ( "content-length" , image_bytes. len ( ) . to_string ( ) ) ;
841842 response. set_body ( Body :: from ( image_bytes. clone ( ) ) ) ;
842843
843- // Simulate PassThrough: take body, remove Content-Length
844- // (stream_to_client uses chunked encoding)
845- let mut body = response. take_body ( ) ;
846- response. remove_header ( header:: CONTENT_LENGTH ) ;
847-
848- // io::copy into a Vec (simulating StreamingBody)
849- let mut output = Vec :: new ( ) ;
850- std:: io:: copy ( & mut body, & mut output) . expect ( "should copy body" ) ;
844+ // Simulate PassThrough: take body then reattach
845+ let body = response. take_body ( ) ;
846+ // Body is unmodified — Content-Length stays correct
847+ assert_eq ! (
848+ response
849+ . get_header_str( "content-length" )
850+ . expect( "should have content-length" ) ,
851+ "4096" ,
852+ "Content-Length should be preserved for pass-through"
853+ ) ;
851854
855+ // Reattach and verify body content
856+ response. set_body ( body) ;
857+ let output = response. into_body ( ) . into_bytes ( ) ;
852858 assert_eq ! (
853859 output, image_bytes,
854860 "pass-through should preserve body byte-for-byte"
855861 ) ;
856- assert ! (
857- response. get_header( header:: CONTENT_LENGTH ) . is_none( ) ,
858- "Content-Length should be removed for streaming pass-through"
859- ) ;
860862 }
861863
862864 #[ test]
0 commit comments