diff --git a/esi/src/lib.rs b/esi/src/lib.rs index 72330f3..499df4d 100644 --- a/esi/src/lib.rs +++ b/esi/src/lib.rs @@ -338,7 +338,7 @@ impl ElementHandler for DocumentHandler<'_, W> { let dispatcher = self.dispatch_fragment_request; let resp_handler = self.fragment_response_handler; let mut isolated_processor = Processor::new( - Some(self.processor.ctx.get_request().clone_without_body()), + Some(fragment.req.clone_without_body()), self.processor.configuration.clone(), ); isolated_processor.include_depth = self.processor.include_depth + 1; @@ -1760,6 +1760,7 @@ impl Processor { body_bytes, dca_mode, &fragment_url, + &fragment.req, output_writer, dispatch_fragment_request, process_fragment_response, @@ -1792,6 +1793,7 @@ impl Processor { body_bytes, dca_mode, &String::from_utf8_lossy(&alt_src), + &alt_req, output_writer, dispatch_fragment_request, process_fragment_response, @@ -1850,6 +1852,7 @@ impl Processor { body_bytes: Vec, dca_mode: DcaMode, url: &str, + fragment_request: &Request, output_writer: &mut impl Write, dispatcher: &FragmentRequestDispatcher, process_fragment_response: Option<&FragmentResponseProcessor>, @@ -1882,7 +1885,7 @@ impl Processor { // separate Processor also gives us a clean queue, preventing // nested includes from escaping to the parent's slot scope. let mut isolated_processor = Processor::new( - Some(self.ctx.get_request().clone_without_body()), + Some(fragment_request.clone_without_body()), self.configuration.clone(), ); isolated_processor.include_depth = self.include_depth + 1; diff --git a/esi/tests/dca_tests.rs b/esi/tests/dca_tests.rs index 9e3c735..0bc4cf4 100644 --- a/esi/tests/dca_tests.rs +++ b/esi/tests/dca_tests.rs @@ -14,12 +14,24 @@ use std::sync::Arc; // --------------------------------------------------------------------------- fn run(input: &str, config: Configuration, dispatcher: &F) -> esi::Result +where + F: Fn(Request, Option) -> esi::Result + 'static, +{ + run_with_request(input, None, config, dispatcher) +} + +fn run_with_request( + input: &str, + request: Option, + config: Configuration, + dispatcher: &F, +) -> esi::Result where F: Fn(Request, Option) -> esi::Result + 'static, { let reader = std::io::BufReader::new(std::io::Cursor::new(input.as_bytes())); let mut output = Vec::new(); - let mut processor = Processor::new(None, config); + let mut processor = Processor::new(request, config); processor.process_stream( reader, &mut output, @@ -486,3 +498,50 @@ fn test_inherit_on_eval_subtree() -> esi::Result<()> { assert_eq!(result, "E0[88]"); Ok(()) } + +// =========================================================================== +// 5. REQUEST_PATH / QUERY_STRING in nested dca=esi (issue #49) +// =========================================================================== + +/// $(REQUEST_PATH) in a nested dca=esi fragment should reflect the fragment's +/// URL, not the top-level client request path. +#[test] +fn test_request_path_reflects_fragment_url_in_nested_dca_esi() -> esi::Result<()> { + let d = static_body(r#"$(REQUEST_PATH)"#); + let result = run_with_request( + r#""#, + Some(Request::get("http://localhost/original")), + Configuration::default(), + &d, + )?; + assert_eq!(result, "/path/to/fragment"); + Ok(()) +} + +/// $(QUERY_STRING) in a nested dca=esi fragment should reflect the fragment's +/// query string when the include src specifies one. +#[test] +fn test_query_string_reflects_fragment_url_in_nested_dca_esi() -> esi::Result<()> { + let d = static_body(r#"$(QUERY_STRING{key})"#); + let result = run_with_request( + r#""#, + Some(Request::get("http://localhost/original?key=from_parent")), + Configuration::default(), + &d, + )?; + assert_eq!(result, "from_fragment"); + Ok(()) +} + +/// $(REQUEST_PATH) at top level still reflects the client request. +#[test] +fn test_request_path_top_level_unchanged() -> esi::Result<()> { + let result = run_with_request( + r#"$(REQUEST_PATH)"#, + Some(Request::get("http://localhost/original")), + Configuration::default(), + &|_req, _maxwait| unreachable!(), + )?; + assert_eq!(result, "/original"); + Ok(()) +}