fix(client): allow calling Destination::set_host with IPv6 addresses
Closes #1661
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
use std::error::Error as StdError;
|
||||
use std::mem;
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use futures::Future;
|
||||
use http::{uri, Uri};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
@@ -131,13 +131,20 @@ impl Destination {
|
||||
///
|
||||
/// Returns an error if the string is not a valid hostname.
|
||||
pub fn set_host(&mut self, host: &str) -> ::Result<()> {
|
||||
if host.contains(&['@',':'][..]) {
|
||||
// Prevent any userinfo setting, it's bad!
|
||||
if host.contains('@') {
|
||||
return Err(::error::Parse::Uri.into());
|
||||
}
|
||||
let auth = if let Some(port) = self.port() {
|
||||
format!("{}:{}", host, port).parse().map_err(::error::Parse::from)?
|
||||
let bytes = Bytes::from(format!("{}:{}", host, port));
|
||||
uri::Authority::from_shared(bytes)
|
||||
.map_err(::error::Parse::from)?
|
||||
} else {
|
||||
host.parse().map_err(::error::Parse::from)?
|
||||
let auth = host.parse::<uri::Authority>().map_err(::error::Parse::from)?;
|
||||
if auth.port().is_some() {
|
||||
return Err(::error::Parse::Uri.into());
|
||||
}
|
||||
auth
|
||||
};
|
||||
self.update_uri(move |parts| {
|
||||
parts.authority = Some(auth);
|
||||
@@ -305,6 +312,30 @@ mod tests {
|
||||
assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
|
||||
assert_eq!(dst.port(), None, "error doesn't modify dst");
|
||||
|
||||
// Check port isn't snuck into `set_host`.
|
||||
dst.set_host("seanmonstar.com:3030").expect_err("set_host sneaky port");
|
||||
assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
|
||||
assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
|
||||
assert_eq!(dst.port(), None, "error doesn't modify dst");
|
||||
|
||||
// Check userinfo isn't snuck into `set_host`.
|
||||
dst.set_host("sean@nope").expect_err("set_host sneaky userinfo");
|
||||
assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
|
||||
assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
|
||||
assert_eq!(dst.port(), None, "error doesn't modify dst");
|
||||
|
||||
// Allow IPv6 hosts
|
||||
dst.set_host("[::1]").expect("set_host with IPv6");
|
||||
assert_eq!(dst.host(), "::1");
|
||||
assert_eq!(dst.port(), None, "IPv6 didn't affect port");
|
||||
|
||||
// However, IPv6 with a port is rejected.
|
||||
dst.set_host("[::2]:1337").expect_err("set_host with IPv6 and sneaky port");
|
||||
assert_eq!(dst.host(), "::1");
|
||||
assert_eq!(dst.port(), None);
|
||||
|
||||
// -----------------
|
||||
|
||||
// Also test that an exist port is set correctly.
|
||||
let mut dst = Destination {
|
||||
uri: "http://hyper.rs:8080".parse().expect("initial parse 2"),
|
||||
@@ -335,6 +366,16 @@ mod tests {
|
||||
assert_eq!(dst.scheme(), "http", "error doesn't modify dst");
|
||||
assert_eq!(dst.host(), "seanmonstar.com", "error doesn't modify dst");
|
||||
assert_eq!(dst.port(), Some(8080), "error doesn't modify dst");
|
||||
|
||||
// Allow IPv6 hosts
|
||||
dst.set_host("[::1]").expect("set_host with IPv6");
|
||||
assert_eq!(dst.host(), "::1");
|
||||
assert_eq!(dst.port(), Some(8080), "IPv6 didn't affect port");
|
||||
|
||||
// However, IPv6 with a port is rejected.
|
||||
dst.set_host("[::2]:1337").expect_err("set_host with IPv6 and sneaky port");
|
||||
assert_eq!(dst.host(), "::1");
|
||||
assert_eq!(dst.port(), Some(8080));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -371,6 +371,12 @@ impl From<http::uri::InvalidUri> for Parse {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<http::uri::InvalidUriBytes> for Parse {
|
||||
fn from(_: http::uri::InvalidUriBytes) -> Parse {
|
||||
Parse::Uri
|
||||
}
|
||||
}
|
||||
|
||||
impl From<http::uri::InvalidUriParts> for Parse {
|
||||
fn from(_: http::uri::InvalidUriParts) -> Parse {
|
||||
Parse::Uri
|
||||
|
||||
Reference in New Issue
Block a user