diff --git a/router/src/hooks.rs b/router/src/hooks.rs index e19bc7d9bc..effdb51621 100644 --- a/router/src/hooks.rs +++ b/router/src/hooks.rs @@ -101,8 +101,9 @@ where let get = Memo::new({ let key = key.clone_inplace(); move |_| { - query_map - .with(|map| map.get(&key).and_then(|value| value.parse().ok())) + query_map.with(|map| { + map.get_str(&key).and_then(|value| value.parse().ok()) + }) } }); diff --git a/router/src/params.rs b/router/src/params.rs index e117cc1cd7..f2ede59912 100644 --- a/router/src/params.rs +++ b/router/src/params.rs @@ -1,8 +1,8 @@ use crate::location::{unescape, Url}; -use std::{borrow::Cow, mem, str::FromStr, sync::Arc}; +use std::{borrow::Cow, str::FromStr, sync::Arc}; use thiserror::Error; -type ParamsMapInner = Vec<(Cow<'static, str>, String)>; +type ParamsMapInner = Vec<(Cow<'static, str>, Vec)>; #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] pub struct ParamsMap(ParamsMapInner); @@ -21,34 +21,39 @@ impl ParamsMap { } /// Inserts a value into the map. - pub fn insert(&mut self, key: String, value: String) -> Option { + pub fn insert(&mut self, key: String, value: String) { let value = unescape(&value); if let Some(prev) = self.0.iter().position(|(k, _)| k == &key) { - return Some(mem::replace(&mut self.0[prev].1, value)); + self.0[prev].1.push(value); + } else { + self.0.push((key.into(), vec![value])); } - - self.0.push((key.into(), value)); - None } /// Gets an owned value from the map. - pub fn get(&self, key: &str) -> Option { + pub fn get(&self, key: &str) -> Option> { self.0 .iter() .find_map(|(k, v)| (k == key).then_some(v.to_owned())) } - /// Gets a referenc to a value from the map. + /// Gets a reference to a value from the map. pub fn get_str(&self, key: &str) -> Option<&str> { - self.0 - .iter() - .find_map(|(k, v)| (k == key).then_some(v.as_str())) + self.0.iter().find_map(|(k, v)| { + if k == key { + // NOTE: gets the last value which should behave like it did when it was map + // that replaced values + v.last().map(|i| i.as_str()) + } else { + None + } + }) } /// Removes a value from the map. #[inline(always)] - pub fn remove(&mut self, key: &str) -> Option { + pub fn remove(&mut self, key: &str) -> Option> { for i in 0..self.0.len() { if self.0[i].0 == key { return Some(self.0.swap_remove(i).1); @@ -62,11 +67,13 @@ impl ParamsMap { let mut buf = String::new(); if !self.0.is_empty() { buf.push('?'); - for (k, v) in &self.0 { - buf.push_str(&Url::escape(k)); - buf.push('='); - buf.push_str(&Url::escape(v)); - buf.push('&'); + for (k, vs) in &self.0 { + for v in vs { + buf.push_str(&Url::escape(k)); + buf.push('='); + buf.push_str(&Url::escape(v)); + buf.push('&'); + } } if buf.len() > 1 { buf.pop(); @@ -84,7 +91,7 @@ where fn from_iter>(iter: T) -> Self { Self( iter.into_iter() - .map(|(k, v)| (k.into(), v.into())) + .map(|(k, v)| (k.into(), vec![v.into()])) .collect(), ) } @@ -95,13 +102,21 @@ impl IntoIterator for ParamsMap { type IntoIter = ParamsMapIter; fn into_iter(self) -> Self::IntoIter { - ParamsMapIter(self.0.into_iter()) + let inner = self.0.into_iter().fold(vec![], |mut c, (k, vs)| { + for v in vs { + c.push((k.clone(), v)); + } + c + }); + ParamsMapIter(inner.into_iter()) } } /// An iterator over the keys and values of a [`ParamsMap`]. #[derive(Debug)] -pub struct ParamsMapIter(::IntoIter); +pub struct ParamsMapIter( + , String)> as IntoIterator>::IntoIter, +); impl Iterator for ParamsMapIter { type Item = (Cow<'static, str>, String); @@ -201,3 +216,20 @@ impl PartialEq for ParamsError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn paramsmap_to_query_string() { + let mut map = ParamsMap::new(); + let key = "param".to_string(); + let value1 = "a".to_string(); + let value2 = "b".to_string(); + map.insert(key.clone(), value1); + map.insert(key, value2); + let query_string = map.to_query_string(); + assert_eq!(&query_string, "?param=a¶m=b") + } +}