|
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