|
8 | 8 | use std::error::Error as StdError; |
9 | 9 | use std::mem; |
10 | 10 |
|
11 | | -use bytes::{BufMut, BytesMut}; |
| 11 | +use bytes::{BufMut, Bytes, BytesMut}; |
12 | 12 | use futures::Future; |
13 | 13 | use http::{uri, Uri}; |
14 | 14 | use tokio_io::{AsyncRead, AsyncWrite}; |
@@ -131,13 +131,20 @@ impl Destination { |
131 | 131 | /// |
132 | 132 | /// Returns an error if the string is not a valid hostname. |
133 | 133 | pub fn set_host(&mut self, host: &str) -> ::Result<()> { |
134 | | - if host.contains(&['@',':'][..]) { |
| 134 | + // Prevent any userinfo setting, it's bad! |
| 135 | + if host.contains('@') { |
135 | 136 | return Err(::error::Parse::Uri.into()); |
136 | 137 | } |
137 | 138 | let auth = if let Some(port) = self.port() { |
138 | | - format!("{}:{}", host, port).parse().map_err(::error::Parse::from)? |
| 139 | + let bytes = Bytes::from(format!("{}:{}", host, port)); |
| 140 | + uri::Authority::from_shared(bytes) |
| 141 | + .map_err(::error::Parse::from)? |
139 | 142 | } else { |
140 | | - host.parse().map_err(::error::Parse::from)? |
| 143 | + let auth = host.parse::<uri::Authority>().map_err(::error::Parse::from)?; |
| 144 | + if auth.port().is_some() { |
| 145 | + return Err(::error::Parse::Uri.into()); |
| 146 | + } |
| 147 | + auth |
141 | 148 | }; |
142 | 149 | self.update_uri(move |parts| { |
143 | 150 | parts.authority = Some(auth); |
@@ -305,6 +312,30 @@ mod tests { |
305 | 312 | assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst"); |
306 | 313 | assert_eq!(dst.port(), None, "error doesn't modify dst"); |
307 | 314 |
|
| 315 | + // Check port isn't snuck into `set_host`. |
| 316 | + dst.set_host("seanmonstar.com:3030").expect_err("set_host sneaky port"); |
| 317 | + assert_eq!(dst.scheme(), "http", "error doesn't modify dst"); |
| 318 | + assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst"); |
| 319 | + assert_eq!(dst.port(), None, "error doesn't modify dst"); |
| 320 | + |
| 321 | + // Check userinfo isn't snuck into `set_host`. |
| 322 | + dst.set_host("sean@nope").expect_err("set_host sneaky userinfo"); |
| 323 | + assert_eq!(dst.scheme(), "http", "error doesn't modify dst"); |
| 324 | + assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst"); |
| 325 | + assert_eq!(dst.port(), None, "error doesn't modify dst"); |
| 326 | + |
| 327 | + // Allow IPv6 hosts |
| 328 | + dst.set_host("[::1]").expect("set_host with IPv6"); |
| 329 | + assert_eq!(dst.host(), "::1"); |
| 330 | + assert_eq!(dst.port(), None, "IPv6 didn't affect port"); |
| 331 | + |
| 332 | + // However, IPv6 with a port is rejected. |
| 333 | + dst.set_host("[::2]:1337").expect_err("set_host with IPv6 and sneaky port"); |
| 334 | + assert_eq!(dst.host(), "::1"); |
| 335 | + assert_eq!(dst.port(), None); |
| 336 | + |
| 337 | + // ----------------- |
| 338 | + |
308 | 339 | // Also test that an exist port is set correctly. |
309 | 340 | let mut dst = Destination { |
310 | 341 | uri: "http://hyper.rs:8080".parse().expect("initial parse 2"), |
@@ -335,6 +366,16 @@ mod tests { |
335 | 366 | assert_eq!(dst.scheme(), "http", "error doesn't modify dst"); |
336 | 367 | assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst"); |
337 | 368 | assert_eq!(dst.port(), Some(8080), "error doesn't modify dst"); |
| 369 | + |
| 370 | + // Allow IPv6 hosts |
| 371 | + dst.set_host("[::1]").expect("set_host with IPv6"); |
| 372 | + assert_eq!(dst.host(), "::1"); |
| 373 | + assert_eq!(dst.port(), Some(8080), "IPv6 didn't affect port"); |
| 374 | + |
| 375 | + // However, IPv6 with a port is rejected. |
| 376 | + dst.set_host("[::2]:1337").expect_err("set_host with IPv6 and sneaky port"); |
| 377 | + assert_eq!(dst.host(), "::1"); |
| 378 | + assert_eq!(dst.port(), Some(8080)); |
338 | 379 | } |
339 | 380 |
|
340 | 381 | #[test] |
|
0 commit comments