use isahc::{Request, error::ErrorKind, net::IpVersion, prelude::*};
use std::{
io::{self, Read, Write},
net::{Ipv4Addr, Ipv6Addr, Shutdown, TcpListener, TcpStream, ToSocketAddrs},
thread,
};
use testserver::mock;
#[macro_use]
mod utils;
fn is_ipv6_supported() -> bool {
if let Ok(addrs) = "localhost:0".to_socket_addrs() {
for addr in addrs {
if addr.is_ipv6() {
return true;
}
}
}
false
}
#[test]
fn local_addr_returns_expected_address() {
let m = mock!();
let response = isahc::get(m.url()).unwrap();
assert_eq!(m.requests_received(), 1);
assert_eq!(response.local_addr().unwrap().ip(), Ipv4Addr::LOCALHOST);
assert!(response.local_addr().unwrap().port() > 0);
}
#[test]
fn remote_addr_returns_expected_address() {
let m = mock!();
let response = isahc::get(m.url()).unwrap();
assert_eq!(m.requests_received(), 1);
assert_eq!(response.remote_addr(), Some(m.addr()));
}
#[test]
fn local_and_remote_addr_returns_expected_addresses_on_error() {
let server = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
let addr = server.local_addr().unwrap();
thread::spawn(move || {
let (mut client, _) = server.accept().unwrap();
client.write_all(b"foobar").unwrap();
client.flush().unwrap();
});
let error = isahc::get(format!("http://localhost:{}", addr.port())).unwrap_err();
assert_eq!(error.remote_addr(), Some(addr));
assert_eq!(error.local_addr().unwrap().ip(), Ipv4Addr::LOCALHOST);
assert!(error.local_addr().unwrap().port() > 0);
}
#[test]
fn ipv4_only_will_not_connect_to_ipv6() {
if !is_ipv6_supported() {
eprintln!("skipping test because host does not support IPv6");
return;
}
let server = TcpListener::bind((Ipv6Addr::LOCALHOST, 0)).unwrap();
let port = server.local_addr().unwrap().port();
let result = Request::get(format!("http://localhost:{}", port))
.ip_version(IpVersion::V4)
.body(())
.unwrap()
.send();
assert_matches!(result, Err(e) if e == ErrorKind::ConnectionFailed);
}
#[test]
fn ipv6_only_will_not_connect_to_ipv4() {
if !is_ipv6_supported() {
eprintln!("skipping test because host does not support IPv6");
return;
}
let server = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
let port = server.local_addr().unwrap().port();
let result = Request::get(format!("http://localhost:{}", port))
.ip_version(IpVersion::V6)
.body(())
.unwrap()
.send();
assert_matches!(result, Err(e) if e == ErrorKind::ConnectionFailed);
}
#[test]
fn any_ip_version_uses_ipv4_or_ipv6() {
let server_v4 = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
let port = server_v4.local_addr().unwrap().port();
let server_v6 = TcpListener::bind((Ipv6Addr::LOCALHOST, port)).unwrap();
fn respond(client: &mut TcpStream, response: &[u8]) -> io::Result<()> {
let _ = client.read(&mut [0; 8192])?;
client.write_all(response)?;
client.flush()?;
client.shutdown(Shutdown::Both)
}
thread::spawn(move || {
let (mut client, _) = server_v4.accept().unwrap();
respond(
&mut client,
b"HTTP/1.1 200 OK\r\ncontent-length:4\r\n\r\nipv4",
)
.unwrap();
});
thread::spawn(move || {
let (mut client, _) = server_v6.accept().unwrap();
respond(
&mut client,
b"HTTP/1.1 200 OK\r\ncontent-length:4\r\n\r\nipv6",
)
.unwrap();
});
let mut response = Request::get(format!("http://localhost:{}", port))
.ip_version(IpVersion::Any)
.body(())
.unwrap()
.send()
.unwrap();
let text = response.text().unwrap();
assert_matches!(text.as_str(), "ipv4" | "ipv6");
if text == "ipv6" {
assert!(response.local_addr().unwrap().is_ipv6());
} else {
assert!(response.local_addr().unwrap().is_ipv4());
}
}