use crate::config::Config;
use crate::dns::DnsResolver;
use crate::error::{DnsError, Error};
use crate::parser::uri::Uri;
use crate::transport::connection;
use crate::transport::tests::mock_socket::MockSocket;
use crate::util::IpAddr;
use alloc::vec;
use alloc::vec::Vec;
use core::net::Ipv6Addr;
use core::time::Duration;
struct MockDns {
addresses: Vec<IpAddr>,
}
impl MockDns {
fn new(addresses: Vec<IpAddr>) -> Self {
Self { addresses }
}
fn empty() -> Self {
Self { addresses: Vec::new() }
}
}
impl DnsResolver for MockDns {
fn resolve(
&self,
_hostname: &str,
) -> Result<Vec<IpAddr>, DnsError> {
if self.addresses.is_empty() {
return Err(DnsError::ResolutionFailed(0));
}
Ok(self.addresses.clone())
}
}
#[test]
fn connector_resolves_dns_and_connects() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let uri = Uri::parse("http://example.com").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
assert!(result.is_ok());
assert!(socket.connected_addr.is_some());
assert_eq!(socket.connected_host.as_deref(), Some("example.com"));
}
#[test]
fn connector_passes_hostname_for_sni() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let uri = Uri::parse("https://Api.Example.COM:8443/").unwrap();
let _ = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
assert_eq!(socket.connected_host.as_deref(), Some("Api.Example.COM"));
}
#[test]
fn connector_uses_default_http_port_80() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let uri = Uri::parse("http://example.com").unwrap();
let _result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
let addr = socket.connected_addr.unwrap();
assert!(addr.contains(":80"), "Should use port 80 for HTTP");
}
#[test]
fn connector_uses_default_https_port_443() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let uri = Uri::parse("https://example.com").unwrap();
let _result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
let addr = socket.connected_addr.unwrap();
assert!(addr.contains(":443"), "Should use port 443 for HTTPS");
}
#[test]
fn connector_uses_explicit_port() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let uri = Uri::parse("http://example.com:8080").unwrap();
let _result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
let addr = socket.connected_addr.unwrap();
assert!(addr.contains(":8080"), "Should use explicit port 8080");
}
#[test]
fn connector_connects_literal_ipv4() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![]);
let uri = Uri::parse("http://192.168.1.1").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
assert!(result.is_ok());
assert_eq!(socket.connected_addr.as_deref(), Some("192.168.1.1:80"));
}
#[test]
fn connector_sets_read_timeout() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let config = Config::builder()
.timeout_read(Some(Duration::from_secs(5)))
.build();
let uri = Uri::parse("http://example.com").unwrap();
let _result = connection::connect(&mut socket, &dns, &uri, &config, false);
assert_eq!(socket.read_timeout, Some(5000));
}
#[test]
fn connector_sets_connect_timeout_not_write() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let config = Config::builder()
.timeout_connect(Some(Duration::from_secs(3)))
.timeout_write(None)
.timeout_read(None)
.build();
let uri = Uri::parse("http://example.com").unwrap();
let _result = connection::connect(&mut socket, &dns, &uri, &config, false);
assert_eq!(socket.connect_timeout, Some(3000));
assert_eq!(socket.write_timeout, Some(0));
}
#[test]
fn connector_sets_read_and_write_timeouts() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let config = Config::builder()
.timeout_read(Some(Duration::from_secs(10)))
.timeout_write(Some(Duration::from_secs(10)))
.build();
let uri = Uri::parse("http://example.com").unwrap();
let _result = connection::connect(&mut socket, &dns, &uri, &config, false);
assert_eq!(socket.read_timeout, Some(10000));
assert_eq!(socket.write_timeout, Some(10000));
}
#[test]
fn connector_returns_error_on_dns_failure() {
let mut socket = MockSocket::empty();
let dns = MockDns::empty();
let uri = Uri::parse("http://example.com").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
assert!(result.is_err());
if let Err(err) = result {
assert!(matches!(err, Error::Dns(_)));
}
}
#[test]
fn connector_returns_error_on_socket_connect_failure() {
let mut socket = MockSocket::with_connect_failure();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let uri = Uri::parse("http://example.com").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
assert!(result.is_err());
if let Err(err) = result {
assert!(matches!(err, Error::Socket(_)));
}
}
#[test]
fn connector_returns_error_on_no_addresses() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![]);
let uri = Uri::parse("http://example.com").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
assert!(result.is_err());
}
#[test]
fn connector_uses_first_resolved_address() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1]), IpAddr::from([192, 168, 1, 1])]);
let uri = Uri::parse("http://example.com").unwrap();
let _result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
let addr = socket.connected_addr.unwrap();
assert!(addr.contains("127.0.0.1"), "Should use first resolved address");
}
#[test]
fn connector_tries_next_address_on_connect_failure() {
let mut socket = MockSocket::with_fail_first_n(1);
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1]), IpAddr::from([192, 168, 1, 1])]);
let uri = Uri::parse("http://example.com").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
assert!(result.is_ok());
assert_eq!(socket.connected_addr.as_deref(), Some("192.168.1.1:80"));
}
#[test]
fn connector_creates_connection_with_config() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let config = Config::builder().max_response_header_size(16384).build();
let uri = Uri::parse("http://example.com").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &config, false);
assert!(result.is_ok());
}
#[test]
fn connector_handles_ipv6_addresses() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::V6(Ipv6Addr::LOCALHOST)]);
let uri = Uri::parse("http://example.com").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
assert!(result.is_ok());
}
#[test]
fn connector_timeout_conversion_handles_large_values() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let config = Config::builder()
.timeout_read(Some(Duration::from_secs(100)))
.timeout_write(Some(Duration::from_secs(100)))
.build();
let uri = Uri::parse("http://example.com").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &config, false);
assert!(result.is_ok());
assert_eq!(socket.read_timeout, Some(100_000));
}
#[test]
fn connector_applies_default_timeouts() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let uri = Uri::parse("http://example.com").unwrap();
let _result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
assert_eq!(socket.connect_timeout, Some(10_000));
assert_eq!(socket.read_timeout, Some(30_000));
assert_eq!(socket.write_timeout, Some(30_000));
}
#[test]
fn connector_clears_prior_timeout_when_explicitly_none() {
let mut socket = MockSocket::empty();
socket.read_timeout = Some(5_000);
socket.write_timeout = Some(5_000);
socket.connect_timeout = Some(5_000);
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
let config = Config::builder()
.timeout_connect(None)
.timeout_read(None)
.timeout_write(None)
.build();
let uri = Uri::parse("http://example.com").unwrap();
let _ = connection::connect(&mut socket, &dns, &uri, &config, true).unwrap();
assert_eq!(socket.read_timeout, Some(0));
assert_eq!(socket.write_timeout, Some(0));
}
#[test]
fn connector_borrows_socket_and_dns() {
let mut socket = MockSocket::empty();
let dns = MockDns::new(vec![IpAddr::from([127, 0, 0, 1])]);
{
let uri = Uri::parse("http://example.com").unwrap();
let _result = connection::connect(&mut socket, &dns, &uri, &Config::default(), false);
}
assert!(socket.connected_addr.is_some());
}
#[test]
fn connector_skips_dns_when_reused() {
let mut socket = MockSocket::empty();
let dns = MockDns::empty();
let uri = Uri::parse("http://example.com").unwrap();
let result = connection::connect(&mut socket, &dns, &uri, &Config::default(), true);
assert!(result.is_ok());
assert!(socket.connected_addr.is_none());
}