@@ -18,6 +18,9 @@ use crate::streaming_processor::{Compression, PipelineConfig, StreamProcessor, S
1818/// Chunk size used for streaming content through the rewrite pipeline.
1919const STREAMING_CHUNK_SIZE : usize = 8192 ;
2020
21+ /// Fallback `Content-Type` for image-like responses missing an origin type.
22+ const IMAGE_FALLBACK_CONTENT_TYPE : & str = "application/octet-stream" ;
23+
2124#[ derive( Deserialize ) ]
2225struct ProxySignReq {
2326 url : String ,
@@ -256,7 +259,7 @@ fn finalize_proxied_response(
256259 ) ;
257260 }
258261
259- // Image handling: set generic content- type if missing and log pixel heuristics
262+ // Image handling: set a valid fallback content type if missing and log pixel heuristics
260263 let req_accept_images = req
261264 . get_header ( HEADER_ACCEPT )
262265 . and_then ( |h| h. to_str ( ) . ok ( ) )
@@ -265,7 +268,7 @@ fn finalize_proxied_response(
265268
266269 if ct. starts_with ( "image/" ) || req_accept_images {
267270 if beresp. get_header ( header:: CONTENT_TYPE ) . is_none ( ) {
268- beresp. set_header ( header:: CONTENT_TYPE , "image/*" ) ;
271+ beresp. set_header ( header:: CONTENT_TYPE , IMAGE_FALLBACK_CONTENT_TYPE ) ;
269272 }
270273
271274 // Heuristics to log likely tracking pixels without altering response
@@ -344,7 +347,7 @@ fn finalize_proxied_response_streaming(
344347
345348 if ct. starts_with ( "image/" ) || req_accept_images {
346349 if beresp. get_header ( header:: CONTENT_TYPE ) . is_none ( ) {
347- beresp. set_header ( header:: CONTENT_TYPE , "image/*" ) ;
350+ beresp. set_header ( header:: CONTENT_TYPE , IMAGE_FALLBACK_CONTENT_TYPE ) ;
348351 }
349352
350353 let mut is_pixel = false ;
@@ -685,9 +688,9 @@ async fn proxy_with_redirects(
685688/// - Proxies the decoded URL via a dynamic backend derived from scheme/host/port.
686689/// - If the response `Content-Type` contains `text/html`, rewrites the HTML creative
687690/// (img/srcset/iframe to first-party) before returning `text/html; charset=utf-8`.
688- /// - If the response is an image or the request `Accept` indicates images, ensures a
689- /// generic `image/* ` content type if origin omitted it, and logs likely 1×1 pixels
690- /// using simple size/URL heuristics. No special response (still proxied).
691+ /// - If the response is an image or the request `Accept` indicates images, ensures an
692+ /// `application/octet-stream ` content type if origin omitted it, and logs likely 1×1
693+ /// pixels using simple size/URL heuristics. No special response (still proxied).
691694///
692695/// # Errors
693696///
@@ -1168,7 +1171,7 @@ mod tests {
11681171 copy_proxy_forward_headers, handle_first_party_click, handle_first_party_proxy,
11691172 handle_first_party_proxy_rebuild, handle_first_party_proxy_sign, is_host_allowed,
11701173 rebuild_response_with_body, reconstruct_and_validate_signed_target, redirect_is_permitted,
1171- ProxyRequestConfig , SUPPORTED_ENCODINGS ,
1174+ ProxyRequestConfig , IMAGE_FALLBACK_CONTENT_TYPE , SUPPORTED_ENCODINGS ,
11721175 } ;
11731176 use crate :: error:: { IntoHttpResponse , TrustedServerError } ;
11741177 use crate :: test_support:: tests:: create_test_settings;
@@ -1625,8 +1628,9 @@ mod tests {
16251628
16261629 // --- Finalization path tests (no network) ---
16271630
1628- // Access the finalize function within the crate for testing
1631+ // Access the finalize helpers within the crate for testing
16291632 use super :: finalize_proxied_response as finalize;
1633+ use super :: finalize_proxied_response_streaming as finalize_streaming;
16301634
16311635 #[ test]
16321636 fn html_response_is_rewritten_and_content_type_set ( ) {
@@ -1728,20 +1732,36 @@ mod tests {
17281732 }
17291733
17301734 #[ test]
1731- fn image_accept_sets_generic_content_type_when_missing ( ) {
1735+ fn image_accept_sets_fallback_content_type_when_missing ( ) {
17321736 let settings = create_test_settings ( ) ;
17331737 let beresp = Response :: from_status ( StatusCode :: OK ) . with_body ( "PNG" ) ;
17341738 let mut req = Request :: new ( Method :: GET , "https://edge.example/first-party/proxy" ) ;
17351739 req. set_header ( HEADER_ACCEPT , "image/*" ) ;
17361740 let out = finalize ( & settings, & req, "https://cdn.example/pixel.gif" , beresp)
17371741 . expect ( "finalize should succeed" ) ;
1738- // Since CT was missing and Accept indicates image, it should set generic image/*
1742+ // Since CT was missing and Accept indicates image, it should set a valid fallback.
17391743 let ct = out
17401744 . get_header ( header:: CONTENT_TYPE )
17411745 . expect ( "Content-Type header should be present" )
17421746 . to_str ( )
17431747 . expect ( "Content-Type should be valid UTF-8" ) ;
1744- assert_eq ! ( ct, "image/*" ) ;
1748+ assert_eq ! ( ct, IMAGE_FALLBACK_CONTENT_TYPE ) ;
1749+ }
1750+
1751+ #[ test]
1752+ fn streaming_image_accept_sets_fallback_content_type_when_missing ( ) {
1753+ let beresp = Response :: from_status ( StatusCode :: OK ) . with_body ( "GIF" ) ;
1754+ let mut req = Request :: new ( Method :: GET , "https://edge.example/first-party/proxy" ) ;
1755+ req. set_header ( HEADER_ACCEPT , "image/*" ) ;
1756+
1757+ let out = finalize_streaming ( & req, "https://cdn.example/pixel.gif" , beresp) ;
1758+ let ct = out
1759+ . get_header ( header:: CONTENT_TYPE )
1760+ . expect ( "Content-Type header should be present" )
1761+ . to_str ( )
1762+ . expect ( "Content-Type should be valid UTF-8" ) ;
1763+
1764+ assert_eq ! ( ct, IMAGE_FALLBACK_CONTENT_TYPE ) ;
17451765 }
17461766
17471767 #[ test]
0 commit comments