@@ -58,8 +58,13 @@ const EDGEZERO_ENABLED_KEY: &str = "edgezero_enabled";
5858///
5959/// The streaming arm keeps the publisher body out of WASM heap until it is written directly
6060/// to the client via [`fastly::Response::stream_to_client`]. All other legacy routes are buffered.
61+ ///
62+ /// [`AuthChallenge`](HandlerOutcome::AuthChallenge) marks responses produced by this server's
63+ /// own `enforce_basic_auth` so the geo-lookup gate can distinguish them from origin-forwarded
64+ /// 401s, which should still carry geo headers.
6165enum HandlerOutcome {
6266 Buffered ( HttpResponse ) ,
67+ AuthChallenge ( HttpResponse ) ,
6368 Streaming {
6469 response : HttpResponse ,
6570 body : EdgeBody ,
@@ -68,9 +73,10 @@ enum HandlerOutcome {
6873}
6974
7075impl HandlerOutcome {
76+ #[ cfg( test) ]
7177 fn status ( & self ) -> edgezero_core:: http:: StatusCode {
7278 match self {
73- HandlerOutcome :: Buffered ( resp) => resp. status ( ) ,
79+ HandlerOutcome :: Buffered ( resp) | HandlerOutcome :: AuthChallenge ( resp ) => resp. status ( ) ,
7480 HandlerOutcome :: Streaming { response, .. } => response. status ( ) ,
7581 }
7682 }
@@ -296,8 +302,10 @@ fn legacy_main(mut req: FastlyRequest) {
296302 ) )
297303 . unwrap_or_else ( |e| HandlerOutcome :: Buffered ( http_error_response ( & e) ) ) ;
298304
299- // Skip geo lookup for 401s: avoids exposing geo headers to unauthenticated callers.
300- let geo_info = if outcome. status ( ) == edgezero_core:: http:: StatusCode :: UNAUTHORIZED {
305+ // Skip geo lookup for our own auth challenges: avoids exposing geo headers to
306+ // unauthenticated callers. Origin-forwarded 401s are not AuthChallenge and
307+ // do receive geo headers — the client already reached the origin anyway.
308+ let geo_info = if matches ! ( outcome, HandlerOutcome :: AuthChallenge ( _) ) {
301309 None
302310 } else {
303311 runtime_services
@@ -310,7 +318,7 @@ fn legacy_main(mut req: FastlyRequest) {
310318 } ;
311319
312320 match outcome {
313- HandlerOutcome :: Buffered ( mut response) => {
321+ HandlerOutcome :: Buffered ( mut response) | HandlerOutcome :: AuthChallenge ( mut response ) => {
314322 finalize_response ( & state. settings , geo_info. as_ref ( ) , & mut response) ;
315323 compat:: to_fastly_response ( response) . send_to_client ( ) ;
316324 }
@@ -336,9 +344,9 @@ fn legacy_main(mut req: FastlyRequest) {
336344 }
337345 Err ( e) => {
338346 log:: error!( "streaming processing failed: {e:?}" ) ;
339- if let Err ( finish_err ) = streaming_body . finish ( ) {
340- log :: error! ( "failed to finish streaming body after error: {finish_err}" ) ;
341- }
347+ // Headers already committed. Drop the body so the client sees a
348+ // truncated response (EOF mid-stream) — standard proxy behavior.
349+ drop ( streaming_body ) ;
342350 }
343351 }
344352 }
@@ -398,7 +406,7 @@ async fn route_request(
398406 // Keep this fallback so manually-constructed or otherwise unprepared
399407 // settings still become an error response instead of panicking.
400408 match enforce_basic_auth ( settings, & req) {
401- Ok ( Some ( response) ) => return Ok ( HandlerOutcome :: Buffered ( response) ) ,
409+ Ok ( Some ( response) ) => return Ok ( HandlerOutcome :: AuthChallenge ( response) ) ,
402410 Ok ( None ) => { }
403411 Err ( e) => return Err ( e) ,
404412 }
0 commit comments