pub trait Host: Unpin + 'static {
fn hostname(&self) -> &str;
fn port(&self) -> Option<u16> {
None
}
}
impl Host for String {
fn hostname(&self) -> &str {
split_host(self).0
}
fn port(&self) -> Option<u16> {
split_host(self).1.and_then(|port| port.parse().ok())
}
}
impl Host for &'static str {
fn hostname(&self) -> &str {
split_host(self).0
}
fn port(&self) -> Option<u16> {
split_host(self).1.and_then(|port| port.parse().ok())
}
}
fn split_host(host: &str) -> (&str, Option<&str>) {
if host.starts_with('[') {
if let Some(end) = host.find(']') {
let (hostname, rest) = host.split_at(end + 1);
if rest.is_empty() {
return (hostname, None);
}
if let Some(port) = rest.strip_prefix(':') {
return (hostname, Some(port));
}
}
}
match host.split_once(':') {
Some((hostname, port)) => (hostname, Some(port)),
None => (host, None),
}
}
#[cfg(test)]
mod tests {
use std::net::SocketAddr;
use actix_service::Service as _;
use super::*;
use crate::connect::{ConnectInfo, Resolver};
macro_rules! assert_connection_info_eq {
($req:expr, $hostname:expr, $port:expr) => {{
assert_eq!($req.hostname(), $hostname);
assert_eq!($req.port(), $port);
}};
}
#[test]
fn host_parsing() {
assert_connection_info_eq!("example.com", "example.com", None);
assert_connection_info_eq!("example.com:8080", "example.com", Some(8080));
assert_connection_info_eq!("example.com:8080".to_owned(), "example.com", Some(8080));
assert_connection_info_eq!("example:8080", "example", Some(8080));
assert_connection_info_eq!("example.com:false", "example.com", None);
assert_connection_info_eq!("example.com:false:false", "example.com", None);
}
#[test]
fn bracketed_ipv6_hosts() {
assert_connection_info_eq!("[::1]:8080", "[::1]", Some(8080));
assert_connection_info_eq!("[::1]:8080".to_owned(), "[::1]", Some(8080));
assert_connection_info_eq!("[::1]", "[::1]", None);
assert_connection_info_eq!("[::1]".to_owned(), "[::1]", None);
}
#[actix_rt::test]
async fn bracketed_ipv6_host_resolves_to_socket_address() {
let request = ConnectInfo::new("[::1]:8080");
let resolved = Resolver::default().service().call(request).await.unwrap();
let expected = "[::1]:8080".parse::<SocketAddr>().unwrap();
assert_eq!(resolved.addrs().collect::<Vec<_>>(), vec![expected]);
}
}