diff --git a/README.md b/README.md index f35631c..6a5626d 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ - [x] Transfer-Encoding: chunked - [x] Ergonomic JSON handling - [x] Test harness for end-to-end tests -- [ ] Limit read length on Content-Size -- [ ] Auth headers +- [x] Limit read length on Content-Size +- [x] Auth headers +- [x] Repeated headers - [ ] Cookie jar in agent - [ ] Forms with application/x-www-form-urlencoded - [ ] multipart/form-data diff --git a/src/agent.rs b/src/agent.rs index c77cf53..43bc5ba 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -53,7 +53,9 @@ impl Agent { K: Into, V: Into, { - add_agent_header(self, header.into(), value.into()); + let s = format!("{}: {}", header.into(), value.into()); + let header = s.parse::
().expect("Failed to parse header"); + add_header(header, &mut self.headers); self } @@ -87,7 +89,9 @@ impl Agent { I: IntoIterator, { for (k, v) in headers.into_iter() { - add_agent_header(self, k.into(), v.into()); + let s = format!("{}: {}", k.into(), v.into()); + let header = s.parse::
().expect("Failed to parse header"); + add_header(header, &mut self.headers); } self } diff --git a/src/header.rs b/src/header.rs index 81c2900..662d9f6 100644 --- a/src/header.rs +++ b/src/header.rs @@ -56,3 +56,11 @@ impl FromStr for Header { Ok(Header { line, index }) } } + +pub fn add_header(header: Header, headers: &mut Vec
) { + if !header.name().to_lowercase().starts_with("x-") { + let name = header.name(); + headers.retain(|h| h.name() != name); + } + headers.push(header); +} diff --git a/src/request.rs b/src/request.rs index 0232bad..38fb915 100644 --- a/src/request.rs +++ b/src/request.rs @@ -177,7 +177,9 @@ impl Request { K: Into, V: Into, { - add_request_header(self, header.into(), value.into()); + let s = format!("{}: {}", header.into(), value.into()); + let header = s.parse::
().expect("Failed to parse header"); + add_header(header, &mut self.headers); self } @@ -208,6 +210,26 @@ impl Request { self.get(name).is_some() } + /// All headers corresponding values for the give name, or empty vector. + /// + /// ``` + /// let req = ureq::get("/my_page") + /// .set("X-Forwarded-For", "1.2.3.4") + /// .set("X-Forwarded-For", "2.3.4.5") + /// .build(); + /// assert_eq!(req.get_all("x-forwarded-for"), vec![ + /// "1.2.3.4", + /// "2.3.4.5", + /// ]); + /// ``` + pub fn get_all<'a>(&self, name: &'a str) -> Vec<&str> { + self.headers + .iter() + .filter(|h| h.is_name(name)) + .map(|h| h.value()) + .collect() + } + /// Set many headers. /// /// ``` @@ -234,7 +256,9 @@ impl Request { I: IntoIterator, { for (k, v) in headers.into_iter() { - add_request_header(self, k.into(), v.into()); + let s = format!("{}: {}", k.into(), v.into()); + let header = s.parse::
().expect("Failed to parse header"); + add_header(header, &mut self.headers); } self } diff --git a/src/test/simple.rs b/src/test/simple.rs index cf1e16e..e6259ea 100644 --- a/src/test/simple.rs +++ b/src/test/simple.rs @@ -16,6 +16,38 @@ fn header_passing() { assert_eq!(resp.get("X-Bar").unwrap(), "foo"); } +#[test] +fn repeat_non_x_header() { + test::set_handler("/repeat_non_x_header", |req, _url| { + assert!(req.has("Accept")); + assert_eq!(req.get("Accept").unwrap(), "baz"); + test::make_stream(200, "OK", vec![], vec![]) + }); + let resp = get("test://host/repeat_non_x_header") + .set("Accept", "bar") + .set("Accept", "baz") + .call(); + assert_eq!(*resp.status(), 200); +} + +#[test] +fn repeat_x_header() { + test::set_handler("/repeat_x_header", |req, _url| { + assert!(req.has("X-Forwarded-For")); + assert_eq!(req.get("X-Forwarded-For").unwrap(), "130.240.19.2"); + assert_eq!(req.get_all("X-Forwarded-For"), vec![ + "130.240.19.2", + "130.240.19.3", + ]); + test::make_stream(200, "OK", vec![], vec![]) + }); + let resp = get("test://host/repeat_x_header") + .set("X-Forwarded-For", "130.240.19.2") + .set("X-Forwarded-For", "130.240.19.3") + .call(); + assert_eq!(*resp.status(), 200); +} + #[test] fn body_as_text() { test::set_handler("/body_as_text", |_req, _url| {