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::error::Error as StdError;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use http::{uri, Uri};
|
use http::{uri, Uri};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
@@ -131,13 +131,20 @@ impl Destination {
|
|||||||
///
|
///
|
||||||
/// Returns an error if the string is not a valid hostname.
|
/// Returns an error if the string is not a valid hostname.
|
||||||
pub fn set_host(&mut self, host: &str) -> ::Result<()> {
|
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());
|
return Err(::error::Parse::Uri.into());
|
||||||
}
|
}
|
||||||
let auth = if let Some(port) = self.port() {
|
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 {
|
} 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| {
|
self.update_uri(move |parts| {
|
||||||
parts.authority = Some(auth);
|
parts.authority = Some(auth);
|
||||||
@@ -305,6 +312,30 @@ mod tests {
|
|||||||
assert_eq!(dst.host(), "seanmonstar.com", "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");
|
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.
|
// Also test that an exist port is set correctly.
|
||||||
let mut dst = Destination {
|
let mut dst = Destination {
|
||||||
uri: "http://hyper.rs:8080".parse().expect("initial parse 2"),
|
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.scheme(), "http", "error doesn't modify dst");
|
||||||
assert_eq!(dst.host(), "seanmonstar.com", "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");
|
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]
|
#[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 {
|
impl From<http::uri::InvalidUriParts> for Parse {
|
||||||
fn from(_: http::uri::InvalidUriParts) -> Parse {
|
fn from(_: http::uri::InvalidUriParts) -> Parse {
|
||||||
Parse::Uri
|
Parse::Uri
|
||||||
|
|||||||
Reference in New Issue
Block a user