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:
Sean McArthur
2019-01-03 12:28:03 -08:00
parent dd5b95fc06
commit 9c0c5ca42d
3 changed files with 89 additions and 7 deletions

View File

@@ -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;