From 706e7d1cec6c902382c0e11413f0f4c24563b09a Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 26 Mar 2026 11:34:49 -0600 Subject: [PATCH 1/3] feat: handle merged WWW-Authenticate challenges --- src/protocol/core/headers.rs | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/protocol/core/headers.rs b/src/protocol/core/headers.rs index c24027cd..d8140eab 100644 --- a/src/protocol/core/headers.rs +++ b/src/protocol/core/headers.rs @@ -278,6 +278,13 @@ pub fn parse_www_authenticate(header: &str) -> Result { /// ]; /// let challenges = parse_www_authenticate_all(headers); /// assert_eq!(challenges.len(), 2); +/// +/// // Merged into a single header value +/// let merged = vec![ +/// "Payment id=\"abc\", realm=\"api\", method=\"tempo\", intent=\"charge\", request=\"e30\", Payment id=\"def\", realm=\"api\", method=\"base\", intent=\"charge\", request=\"e30\"", +/// ]; +/// let challenges = parse_www_authenticate_all(merged); +/// assert_eq!(challenges.len(), 2); /// ``` /// /// ``` @@ -592,6 +599,37 @@ mod tests { assert_eq!(second.id, "b"); } + #[test] + fn test_parse_www_authenticate_all_merged() { + // Two Payment schemes in a single comma-separated header value + let merged = r#"Payment id="a", realm="api", method="tempo", intent="charge", request="e30", Payment id="b", realm="api", method="stripe", intent="charge", request="e30""#; + let results = parse_www_authenticate_all(vec![merged]); + assert_eq!(results.len(), 2); + assert_eq!(results[0].as_ref().unwrap().id, "a"); + assert_eq!(results[0].as_ref().unwrap().method.as_str(), "tempo"); + assert_eq!(results[1].as_ref().unwrap().id, "b"); + assert_eq!(results[1].as_ref().unwrap().method.as_str(), "stripe"); + } + + #[test] + fn test_split_payment_schemes_ignores_quoted() { + // "Payment" inside a quoted value should NOT be treated as a boundary + let header = r#"Payment id="a", realm="api", method="tempo", intent="charge", request="e30", description="Payment required""#; + let schemes = split_payment_schemes(header); + assert_eq!(schemes.len(), 1); + } + + #[test] + fn test_split_payment_schemes_leading_whitespace() { + // Headers with leading whitespace must still be recognized + let header = + r#" Payment id="a", realm="api", method="tempo", intent="charge", request="e30""#; + let schemes = split_payment_schemes(header); + assert_eq!(schemes.len(), 1); + let challenge = parse_www_authenticate(schemes[0]).unwrap(); + assert_eq!(challenge.id, "a"); + } + #[test] fn test_format_www_authenticate_many() { let c1 = test_challenge(); From e1f3aa139fa8bfa35f04048aa8ce4300105ff259 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 6 Apr 2026 20:51:19 -0600 Subject: [PATCH 2/3] fix: rename split_payment_schemes to split_payment_challenges and handle leading whitespace Amp-Thread-ID: https://ampcode.com/threads/T-019d65cc-0e1d-7279-a3c2-1b98ac866fbf Co-authored-by: Amp --- src/protocol/core/headers.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/protocol/core/headers.rs b/src/protocol/core/headers.rs index d8140eab..eb6b517f 100644 --- a/src/protocol/core/headers.rs +++ b/src/protocol/core/headers.rs @@ -315,7 +315,9 @@ pub fn parse_www_authenticate_all<'a>( /// returns the individual challenge strings. fn split_payment_challenges(header: &str) -> Vec<&str> { fn is_valid_start(header: &str, pos: usize) -> bool { - pos == 0 || header[..pos].bytes().rfind(|b| !b.is_ascii_whitespace()) == Some(b',') + pos == 0 + || header[..pos].bytes().all(|b| b.is_ascii_whitespace()) + || header[..pos].bytes().rfind(|b| !b.is_ascii_whitespace()) == Some(b',') } let lower = header.to_ascii_lowercase(); @@ -615,7 +617,7 @@ mod tests { fn test_split_payment_schemes_ignores_quoted() { // "Payment" inside a quoted value should NOT be treated as a boundary let header = r#"Payment id="a", realm="api", method="tempo", intent="charge", request="e30", description="Payment required""#; - let schemes = split_payment_schemes(header); + let schemes = split_payment_challenges(header); assert_eq!(schemes.len(), 1); } @@ -624,7 +626,7 @@ mod tests { // Headers with leading whitespace must still be recognized let header = r#" Payment id="a", realm="api", method="tempo", intent="charge", request="e30""#; - let schemes = split_payment_schemes(header); + let schemes = split_payment_challenges(header); assert_eq!(schemes.len(), 1); let challenge = parse_www_authenticate(schemes[0]).unwrap(); assert_eq!(challenge.id, "a"); From 75cb43eb125fcfb63a4bcd928541c8f4666f9f81 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 6 Apr 2026 20:54:39 -0600 Subject: [PATCH 3/3] changelog: add 0.9.1 entry for split_payment_challenges fix Amp-Thread-ID: https://ampcode.com/threads/T-019d65cc-0e1d-7279-a3c2-1b98ac866fbf Co-authored-by: Amp --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e68913a..8a7d0dcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.9.1 (2026-04-06) + +### Patch Changes + +- Fixed `split_payment_challenges` to handle leading whitespace in header values. Added tests for merged comma-separated challenges and quoted `"Payment"` boundaries. (by @stevencartavia, [#170](https://github.com/tempoxyz/mpp-rs/pull/170)) + ## 0.9.0 (2026-04-07) ### Minor Changes