@@ -334,6 +334,8 @@ impl PendingFragmentContent {
334334struct Fragment {
335335 /// Index in the chunks array where this fragment should be inserted
336336 chunk_index : usize ,
337+ /// The original request for this fragment (needed for process_fragment_response callback)
338+ request : Request ,
337339 /// The pending fragment content (request or already completed)
338340 pending_content : PendingFragmentContent ,
339341 /// Alternative source URL if main request fails (built lazily only if needed)
@@ -502,8 +504,8 @@ impl Processor {
502504 self ,
503505 mut src_document : impl BufRead ,
504506 output_writer : & mut impl Write ,
505- _dispatch_fragment_request : Option < & FragmentRequestDispatcher > ,
506- _process_fragment_response : Option < & FragmentResponseProcessor > ,
507+ dispatch_fragment_request : Option < & FragmentRequestDispatcher > ,
508+ process_fragment_response : Option < & FragmentResponseProcessor > ,
507509 ) -> Result < ( ) > {
508510 // If there is a source request to mimic, copy its metadata, otherwise use a default request.
509511 let original_request_metadata = self . original_request_metadata . as_ref ( ) . map_or_else (
@@ -543,7 +545,10 @@ impl Processor {
543545 // This allows us to dispatch all requests concurrently
544546 let mut fragments = Vec :: new ( ) ;
545547
546- if let Some ( dispatcher) = _dispatch_fragment_request {
548+ // Use the provided dispatcher or fall back to the default
549+ let dispatcher = dispatch_fragment_request. unwrap_or ( & default_fragment_dispatcher) ;
550+
551+ if true {
547552 for ( idx, chunk) in chunks. iter ( ) . enumerate ( ) {
548553 if let parser_types:: Chunk :: Esi ( parser_types:: Tag :: Include {
549554 src,
@@ -561,29 +566,73 @@ impl Processor {
561566 self . configuration . is_escaped_content ,
562567 ) ?;
563568
564- match dispatcher ( req) {
569+ match dispatcher ( req. clone_without_body ( ) ) {
565570 Ok ( pending_content) => {
566571 fragments. push ( Fragment {
567572 chunk_index : idx,
573+ request : req,
568574 pending_content,
569575 alt : alt. clone ( ) ,
570576 continue_on_error : * continue_on_error,
571577 } ) ;
572578 }
573579 Err ( err) => {
574- if !continue_on_error {
575- return Err ( ESIError :: ExpressionError ( format ! (
576- "Fragment dispatch failed: {}" ,
577- err
578- ) ) ) ;
580+ // Main request failed during dispatch
581+ // Try alt if available
582+ if let Some ( alt_src) = alt {
583+ let interpolated_alt =
584+ try_evaluate_interpolated_string ( alt_src, & mut ctx) ?;
585+ let alt_req = build_fragment_request (
586+ original_request_metadata. clone_without_body ( ) ,
587+ & interpolated_alt,
588+ self . configuration . is_escaped_content ,
589+ ) ?;
590+
591+ match dispatcher ( alt_req. clone_without_body ( ) ) {
592+ Ok ( alt_pending) => {
593+ fragments. push ( Fragment {
594+ chunk_index : idx,
595+ request : alt_req,
596+ pending_content : alt_pending,
597+ alt : None , // Alt already used
598+ continue_on_error : * continue_on_error,
599+ } ) ;
600+ }
601+ Err ( _) => {
602+ // Both main and alt failed
603+ if * continue_on_error {
604+ fragments. push ( Fragment {
605+ chunk_index : idx,
606+ request : req,
607+ pending_content : PendingFragmentContent :: NoContent ,
608+ alt : None ,
609+ continue_on_error : * continue_on_error,
610+ } ) ;
611+ } else {
612+ return Err ( ESIError :: ExpressionError ( format ! (
613+ "Fragment dispatch failed: {}" ,
614+ err
615+ ) ) ) ;
616+ }
617+ }
618+ }
619+ } else {
620+ // No alt, check if we should continue
621+ if * continue_on_error {
622+ fragments. push ( Fragment {
623+ chunk_index : idx,
624+ request : req,
625+ pending_content : PendingFragmentContent :: NoContent ,
626+ alt : None ,
627+ continue_on_error : * continue_on_error,
628+ } ) ;
629+ } else {
630+ return Err ( ESIError :: ExpressionError ( format ! (
631+ "Fragment dispatch failed: {}" ,
632+ err
633+ ) ) ) ;
634+ }
579635 }
580- // If continue_on_error, we'll handle this during output phase
581- fragments. push ( Fragment {
582- chunk_index : idx,
583- pending_content : PendingFragmentContent :: NoContent ,
584- alt : alt. clone ( ) ,
585- continue_on_error : * continue_on_error,
586- } ) ;
587636 }
588637 }
589638 }
@@ -595,38 +644,54 @@ impl Processor {
595644 let mut fragment_responses: std:: collections:: HashMap < usize , Result < Vec < u8 > > > =
596645 std:: collections:: HashMap :: new ( ) ;
597646
598- for fragment_info in fragments {
647+ for mut fragment_info in fragments {
599648 let response_result = fragment_info. pending_content . wait ( ) ;
600649
650+ // Apply the fragment response processor if provided
651+ let processed_response = match response_result {
652+ Ok ( resp) => {
653+ if let Some ( processor) = process_fragment_response {
654+ processor ( & mut fragment_info. request , resp)
655+ } else {
656+ Ok ( resp)
657+ }
658+ }
659+ Err ( e) => Err ( e) ,
660+ } ;
661+
601662 // If main request failed and we have an alt, try the alt
602- let final_response = if response_result. is_err ( ) && fragment_info. alt . is_some ( ) {
603- if let Some ( dispatcher) = _dispatch_fragment_request {
604- let alt = fragment_info. alt . unwrap ( ) ;
605- let interpolated_alt = try_evaluate_interpolated_string ( & alt, & mut ctx) ?;
606- let alt_req = build_fragment_request (
607- original_request_metadata. clone_without_body ( ) ,
608- & interpolated_alt,
609- self . configuration . is_escaped_content ,
610- ) ?;
663+ let final_response = if processed_response. is_err ( ) && fragment_info. alt . is_some ( ) {
664+ let alt = fragment_info. alt . unwrap ( ) ;
665+ let interpolated_alt = try_evaluate_interpolated_string ( & alt, & mut ctx) ?;
666+ let mut alt_req = build_fragment_request (
667+ original_request_metadata. clone_without_body ( ) ,
668+ & interpolated_alt,
669+ self . configuration . is_escaped_content ,
670+ ) ?;
611671
612- match dispatcher ( alt_req) {
613- Ok ( alt_pending) => alt_pending. wait ( ) ,
614- Err ( e) => {
615- if fragment_info. continue_on_error {
616- Ok ( Response :: from_status ( StatusCode :: NO_CONTENT ) )
617- } else {
618- Err ( ESIError :: ExpressionError ( format ! (
619- "Alt fragment failed: {}" ,
620- e
621- ) ) )
622- }
672+ match dispatcher ( alt_req. clone_without_body ( ) ) {
673+ Ok ( alt_pending) => {
674+ let alt_resp = alt_pending. wait ( ) ?;
675+ // Also process the alt response
676+ if let Some ( processor) = process_fragment_response {
677+ processor ( & mut alt_req, alt_resp)
678+ } else {
679+ Ok ( alt_resp)
680+ }
681+ }
682+ Err ( e) => {
683+ if fragment_info. continue_on_error {
684+ Ok ( Response :: from_status ( StatusCode :: NO_CONTENT ) )
685+ } else {
686+ Err ( ESIError :: ExpressionError ( format ! (
687+ "Alt fragment failed: {}" ,
688+ e
689+ ) ) )
623690 }
624691 }
625- } else {
626- response_result
627692 }
628693 } else {
629- response_result
694+ processed_response
630695 } ;
631696
632697 // Convert response to bytes immediately
@@ -692,22 +757,12 @@ fn default_fragment_dispatcher(req: Request) -> Result<PendingFragmentContent> {
692757 Ok ( PendingFragmentContent :: PendingRequest ( pending_req) )
693758}
694759
695- // Simple HTML entity decoder for common entities
696- fn decode_html_entities ( input : & str ) -> String {
697- input
698- . replace ( "<" , "<" )
699- . replace ( ">" , ">" )
700- . replace ( "&" , "&" )
701- . replace ( """ , "\" " )
702- . replace ( "'" , "'" )
703- }
704-
705760// Helper function to build a fragment request from a URL
706761// For HTML content the URL is unescaped if it's escaped (default).
707762// It can be disabled in the processor configuration for a non-HTML content.
708763fn build_fragment_request ( mut request : Request , url : & str , is_escaped : bool ) -> Result < Request > {
709764 let escaped_url = if is_escaped {
710- decode_html_entities ( url)
765+ html_escape :: decode_html_entities ( url) . into_owned ( )
711766 } else {
712767 url. to_string ( )
713768 } ;
0 commit comments