| @@ -30,7 +30,7 @@ use native_tls::TlsConnector; | |||||||
| use super::request::{Request, RequestBuilder}; | use super::request::{Request, RequestBuilder}; | ||||||
| use super::response::Response; | use super::response::Response; | ||||||
| use connect::Connector; | use connect::Connector; | ||||||
| use into_url::to_uri; | use into_url::{expect_uri, try_uri}; | ||||||
| use redirect::{self, RedirectPolicy, remove_sensitive_headers}; | use redirect::{self, RedirectPolicy, remove_sensitive_headers}; | ||||||
| use {IntoUrl, Method, Proxy, StatusCode, Url}; | use {IntoUrl, Method, Proxy, StatusCode, Url}; | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| @@ -520,7 +520,7 @@ impl Client { | |||||||
|             headers.insert(ACCEPT_ENCODING, HeaderValue::from_static("gzip")); |             headers.insert(ACCEPT_ENCODING, HeaderValue::from_static("gzip")); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let uri = to_uri(&url); |         let uri = expect_uri(&url); | ||||||
|  |  | ||||||
|         let (reusable, body) = match body { |         let (reusable, body) = match body { | ||||||
|             Some(body) => { |             Some(body) => { | ||||||
| @@ -709,6 +709,17 @@ impl Future for PendingRequest { | |||||||
|                             self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok() |                             self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok() | ||||||
|                         })(); |                         })(); | ||||||
|  |  | ||||||
|  |                         // Check that the `url` is also a valid `http::Uri`. | ||||||
|  |                         // | ||||||
|  |                         // If not, just log it and skip the redirect. | ||||||
|  |                         let loc = loc.and_then(|url| { | ||||||
|  |                             if try_uri(&url).is_some() { | ||||||
|  |                                 Some(url) | ||||||
|  |                             } else { | ||||||
|  |                                 None | ||||||
|  |                             } | ||||||
|  |                         }); | ||||||
|  |  | ||||||
|                         if loc.is_none() { |                         if loc.is_none() { | ||||||
|                             debug!("Location header had invalid URI: {:?}", val); |                             debug!("Location header had invalid URI: {:?}", val); | ||||||
|                         } |                         } | ||||||
| @@ -733,7 +744,7 @@ impl Future for PendingRequest { | |||||||
|  |  | ||||||
|                             remove_sensitive_headers(&mut self.headers, &self.url, &self.urls); |                             remove_sensitive_headers(&mut self.headers, &self.url, &self.urls); | ||||||
|                             debug!("redirecting to {:?} '{}'", self.method, self.url); |                             debug!("redirecting to {:?} '{}'", self.method, self.url); | ||||||
|                             let uri = to_uri(&self.url); |                             let uri = expect_uri(&self.url); | ||||||
|                             let body = match self.body { |                             let body = match self.body { | ||||||
|                                 Some(Some(ref body)) => ::hyper::Body::from(body.clone()), |                                 Some(Some(ref body)) => ::hyper::Body::from(body.clone()), | ||||||
|                                 _ => ::hyper::Body::empty(), |                                 _ => ::hyper::Body::empty(), | ||||||
|   | |||||||
| @@ -38,10 +38,14 @@ impl<'a> PolyfillTryInto for &'a String { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn to_uri(url: &Url) -> ::hyper::Uri { | pub(crate) fn expect_uri(url: &Url) -> ::hyper::Uri { | ||||||
|     url.as_str().parse().expect("a parsed Url should always be a valid Uri") |     url.as_str().parse().expect("a parsed Url should always be a valid Uri") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub(crate) fn try_uri(url: &Url) -> Option<::hyper::Uri> { | ||||||
|  |     url.as_str().parse().ok() | ||||||
|  | } | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use super::*; |     use super::*; | ||||||
|   | |||||||
| @@ -56,7 +56,7 @@ impl Proxy { | |||||||
|     /// # fn main() {} |     /// # fn main() {} | ||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn http<U: IntoUrl>(url: U) -> ::Result<Proxy> { |     pub fn http<U: IntoUrl>(url: U) -> ::Result<Proxy> { | ||||||
|         let uri = ::into_url::to_uri(&url.into_url()?); |         let uri = ::into_url::expect_uri(&url.into_url()?); | ||||||
|         Ok(Proxy::new(Intercept::Http(uri))) |         Ok(Proxy::new(Intercept::Http(uri))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -75,7 +75,7 @@ impl Proxy { | |||||||
|     /// # fn main() {} |     /// # fn main() {} | ||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn https<U: IntoUrl>(url: U) -> ::Result<Proxy> { |     pub fn https<U: IntoUrl>(url: U) -> ::Result<Proxy> { | ||||||
|         let uri = ::into_url::to_uri(&url.into_url()?); |         let uri = ::into_url::expect_uri(&url.into_url()?); | ||||||
|         Ok(Proxy::new(Intercept::Https(uri))) |         Ok(Proxy::new(Intercept::Https(uri))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -94,7 +94,7 @@ impl Proxy { | |||||||
|     /// # fn main() {} |     /// # fn main() {} | ||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn all<U: IntoUrl>(url: U) -> ::Result<Proxy> { |     pub fn all<U: IntoUrl>(url: U) -> ::Result<Proxy> { | ||||||
|         let uri = ::into_url::to_uri(&url.into_url()?); |         let uri = ::into_url::expect_uri(&url.into_url()?); | ||||||
|         Ok(Proxy::new(Intercept::All(uri))) |         Ok(Proxy::new(Intercept::All(uri))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -200,7 +200,7 @@ impl Proxy { | |||||||
|                         .parse() |                         .parse() | ||||||
|                         .expect("should be valid Url") |                         .expect("should be valid Url") | ||||||
|                 ) |                 ) | ||||||
|                     .map(|u| into_url::to_uri(&u) ) |                     .map(|u| into_url::expect_uri(&u) ) | ||||||
|             }, |             }, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -358,3 +358,32 @@ fn test_referer_is_not_set_if_disabled() { | |||||||
|         .send() |         .send() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_invalid_location_stops_redirect_gh484() { | ||||||
|  |     let server = server! { | ||||||
|  |         request: b"\ | ||||||
|  |             GET /yikes HTTP/1.1\r\n\ | ||||||
|  |             user-agent: $USERAGENT\r\n\ | ||||||
|  |             accept: */*\r\n\ | ||||||
|  |             accept-encoding: gzip\r\n\ | ||||||
|  |             host: $HOST\r\n\ | ||||||
|  |             \r\n\ | ||||||
|  |             ", | ||||||
|  |         response: b"\ | ||||||
|  |             HTTP/1.1 302 Found\r\n\ | ||||||
|  |             Server: test-yikes\r\n\ | ||||||
|  |             Location: http://www.yikes{KABOOM}\r\n\ | ||||||
|  |             Content-Length: 0\r\n\ | ||||||
|  |             \r\n\ | ||||||
|  |             " | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let url = format!("http://{}/yikes", server.addr()); | ||||||
|  |  | ||||||
|  |     let res = reqwest::get(&url).unwrap(); | ||||||
|  |  | ||||||
|  |     assert_eq!(res.url().as_str(), url); | ||||||
|  |     assert_eq!(res.status(), reqwest::StatusCode::FOUND); | ||||||
|  |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-yikes"); | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user