Fix RequestBuilder::headers to include multiple values
`RequestBuilder::headers` will always overwrite any existing header with the same name, but will now correctly append extra values from the *new* header map. Closes #407
This commit is contained in:
		| @@ -7,5 +7,5 @@ pub use self::response::{Response, ResponseBuilderExt}; | ||||
| pub mod body; | ||||
| pub mod client; | ||||
| pub mod decoder; | ||||
| mod request; | ||||
| pub(crate) mod request; | ||||
| mod response; | ||||
|   | ||||
| @@ -121,14 +121,13 @@ impl RequestBuilder { | ||||
|         } | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Add a set of Headers to the existing ones on this Request. | ||||
|     /// | ||||
|     /// The headers will be merged in to any already set. | ||||
|     pub fn headers(mut self, headers: ::header::HeaderMap) -> RequestBuilder { | ||||
|         if let Ok(ref mut req) = self.request { | ||||
|             for (key, value) in headers.iter() { | ||||
|                 req.headers_mut().insert(key, value.clone()); | ||||
|             } | ||||
|             replace_headers(req.headers_mut(), headers); | ||||
|         } | ||||
|         self | ||||
|     } | ||||
| @@ -312,6 +311,33 @@ fn fmt_request_fields<'a, 'b>(f: &'a mut fmt::DebugStruct<'a, 'b>, req: &Request | ||||
|         .field("headers", &req.headers) | ||||
| } | ||||
|  | ||||
| pub(crate) fn replace_headers(dst: &mut HeaderMap, src: HeaderMap) { | ||||
|  | ||||
|     // IntoIter of HeaderMap yields (Option<HeaderName>, HeaderValue). | ||||
|     // The first time a name is yielded, it will be Some(name), and if | ||||
|     // there are more values with the same name, the next yield will be | ||||
|     // None. | ||||
|     // | ||||
|     // TODO: a complex exercise would be to optimize this to only | ||||
|     // require 1 hash/lookup of the key, but doing something fancy | ||||
|     // with header::Entry... | ||||
|  | ||||
|     let mut prev_name = None; | ||||
|     for (key, value) in src { | ||||
|         match key { | ||||
|             Some(key) => { | ||||
|                 dst.insert(key.clone(), value); | ||||
|                 prev_name = Some(key); | ||||
|             }, | ||||
|             None => match prev_name { | ||||
|                 Some(ref key) => { | ||||
|                     dst.append(key.clone(), value); | ||||
|                 }, | ||||
|                 None => unreachable!("HeaderMap::into_iter yielded None first"), | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
| @@ -379,6 +405,35 @@ mod tests { | ||||
|         assert_eq!(req.url().query(), Some("foo=bar&qux=three")); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_replace_headers() { | ||||
|         use http::HeaderMap; | ||||
|  | ||||
|         let mut headers = HeaderMap::new(); | ||||
|         headers.insert("foo", "bar".parse().unwrap()); | ||||
|         headers.append("foo", "baz".parse().unwrap()); | ||||
|  | ||||
|         let client = Client::new(); | ||||
|         let req = client | ||||
|             .get("https://hyper.rs") | ||||
|             .header("im-a", "keeper") | ||||
|             .header("foo", "pop me") | ||||
|             .headers(headers) | ||||
|             .build() | ||||
|             .expect("request build"); | ||||
|  | ||||
|         assert_eq!(req.headers()["im-a"], "keeper"); | ||||
|  | ||||
|         let foo = req | ||||
|             .headers() | ||||
|             .get_all("foo") | ||||
|             .iter() | ||||
|             .collect::<Vec<_>>(); | ||||
|         assert_eq!(foo.len(), 2); | ||||
|         assert_eq!(foo[0], "bar"); | ||||
|         assert_eq!(foo[1], "baz"); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     use {body, Method}; | ||||
|     use super::Client; | ||||
|   | ||||
| @@ -188,9 +188,7 @@ impl RequestBuilder { | ||||
|     /// ``` | ||||
|     pub fn headers(mut self, headers: ::header::HeaderMap) -> RequestBuilder { | ||||
|         if let Ok(ref mut req) = self.request { | ||||
|             for (key, value) in headers.iter() { | ||||
|                 req.headers_mut().insert(key, value.clone()); | ||||
|             } | ||||
|             async_impl::request::replace_headers(req.headers_mut(), headers); | ||||
|         } | ||||
|         self | ||||
|     } | ||||
| @@ -810,4 +808,33 @@ mod tests { | ||||
|         let json_data = MyStruct; | ||||
|         assert!(r.json(&json_data).build().unwrap_err().is_serialization()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_replace_headers() { | ||||
|         use http::HeaderMap; | ||||
|  | ||||
|         let mut headers = HeaderMap::new(); | ||||
|         headers.insert("foo", "bar".parse().unwrap()); | ||||
|         headers.append("foo", "baz".parse().unwrap()); | ||||
|  | ||||
|         let client = Client::new(); | ||||
|         let req = client | ||||
|             .get("https://hyper.rs") | ||||
|             .header("im-a", "keeper") | ||||
|             .header("foo", "pop me") | ||||
|             .headers(headers) | ||||
|             .build() | ||||
|             .expect("request build"); | ||||
|  | ||||
|         assert_eq!(req.headers()["im-a"], "keeper"); | ||||
|  | ||||
|         let foo = req | ||||
|             .headers() | ||||
|             .get_all("foo") | ||||
|             .iter() | ||||
|             .collect::<Vec<_>>(); | ||||
|         assert_eq!(foo.len(), 2); | ||||
|         assert_eq!(foo[0], "bar"); | ||||
|         assert_eq!(foo[1], "baz"); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user