use super::{parse_graceful, parse_strict};
use crate::address::{Domain, Host, UninterpretedHost};
use crate::uri::{ParseError, Uri};
fn uninterpreted(uri: &Uri) -> UninterpretedHost {
uri.host()
.and_then(|h| match h.into_owned() {
Host::Uninterpreted(u) => Some(u),
_ => None,
})
.expect("expected Host::Uninterpreted for this URI")
}
#[test]
fn pct_encoded_reg_name_preserved_in_strict() {
let uri = parse_strict("http://exa%6Dple.com/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "exa%6Dple.com");
}
#[test]
fn pct_encoded_reg_name_decodes_to_domain_on_demand() {
let uri = parse_strict("http://exa%6Dple.com/").unwrap();
let host = uninterpreted(&uri);
assert!(!host.is_bracketed());
let d = Domain::try_from(&host).unwrap();
assert_eq!(d.as_str(), "example.com");
}
#[cfg(feature = "idna")]
#[test]
fn pct_encoded_utf8_reg_name_decodes_with_idn() {
let uri = parse_strict("http://%E4%B8%AD%E6%96%87.com/").unwrap();
let host = uninterpreted(&uri);
let d = Domain::try_from(&host).unwrap();
assert!(d.as_str().starts_with("xn--"), "got {d}");
assert!(d.as_str().ends_with(".com"), "got {d}");
}
#[test]
fn pct_encoded_ipv4_decodes_to_address() {
let uri = parse_strict("http://%31%32%37.0.0.1/").unwrap();
let host = uninterpreted(&uri);
let ip: core::net::IpAddr = (&host).try_into().unwrap();
assert_eq!(ip, "127.0.0.1".parse::<core::net::IpAddr>().unwrap());
}
#[test]
fn sub_delim_reg_name_preserved() {
let uri = parse_graceful("http://tag,with,commas/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "tag,with,commas");
let host = uninterpreted(&uri);
Domain::try_from(&host).unwrap_err();
}
#[test]
fn malformed_pct_escape_rejected() {
parse_graceful("http://%6/").unwrap_err();
parse_graceful("http://%6Z/").unwrap_err();
parse_graceful("http://example.com%/").unwrap_err();
}
#[test]
fn pct_decoded_control_byte_rejected_as_smuggling_vector() {
let err = parse_graceful("http://exa%00ple.com/").unwrap_err();
assert!(
matches!(err, ParseError::ControlCharInUri { byte: 0x00, .. }),
"got {err:?}"
);
let err = parse_graceful("http://exa%0Dple.com/").unwrap_err();
assert!(matches!(
err,
ParseError::ControlCharInUri { byte: 0x0D, .. }
));
parse_graceful("http://exa%09ple.com/").unwrap_err();
}
#[test]
fn illegal_ascii_chars_in_reg_name_rejected() {
parse_graceful("http://exa[ple.com/").unwrap_err();
parse_graceful("http://exa]ple.com/").unwrap_err();
parse_graceful("http://exa<ple.com/").unwrap_err();
parse_graceful("http://exa\"ple.com/").unwrap_err();
parse_graceful("http://exa\\ple.com/").unwrap_err();
}
#[test]
fn ipvfuture_literal_preserved_in_strict() {
let uri = parse_strict("http://[v1.fe80::a]/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "[v1.fe80::a]");
let host = uninterpreted(&uri);
assert!(host.is_bracketed());
assert_eq!(host.as_bytes(), b"v1.fe80::a");
}
#[test]
fn ipvfuture_uppercase_v_accepted() {
let uri = parse_strict("http://[V7.foo:bar]/").unwrap();
let host = uninterpreted(&uri);
assert!(host.is_bracketed());
assert_eq!(host.as_bytes(), b"V7.foo:bar");
}
#[test]
fn ipvfuture_with_port() {
let uri = parse_strict("http://[v1.fe80::a]:443/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "[v1.fe80::a]");
assert_eq!(uri.port_u16(), Some(443));
}
#[test]
fn ipvfuture_domain_conversion_fails_with_typed_error() {
let uri = parse_strict("http://[v1.fe80::a]/").unwrap();
let host = uninterpreted(&uri);
let err = Domain::try_from(&host).unwrap_err();
assert!(
format!("{err}").contains("bracketed IP-literal"),
"got: {err}"
);
}
#[test]
fn ipvfuture_grammar_rejects_invalid_shapes() {
parse_graceful("http://[v.foo]/").unwrap_err();
parse_graceful("http://[v1foo]/").unwrap_err();
parse_graceful("http://[v1.]/").unwrap_err();
parse_graceful("http://[vZ.foo]/").unwrap_err();
}
#[test]
fn ipv6_still_parses_as_typed_address_not_uninterpreted() {
let uri = parse_strict("http://[::1]/").unwrap();
let owned = uri.host().unwrap().into_owned();
assert!(matches!(owned, Host::Address(_)));
assert!(!matches!(owned, Host::Uninterpreted(_)));
}
#[cfg(feature = "idna")]
#[test]
fn graceful_raw_utf8_host_preserved() {
let uri = parse_graceful("https://münchen.de/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "münchen.de");
let host = uninterpreted(&uri);
let d = Domain::try_from(&host).unwrap();
assert_eq!(d.as_str(), "xn--mnchen-3ya.de");
}
#[cfg(feature = "idna")]
#[test]
fn strict_rejects_raw_utf8_host() {
let r = parse_strict("https://münchen.de/");
assert!(matches!(r, Err(ParseError::StrictViolation)));
}
#[test]
fn fqdn_trailing_dot_in_host_preserved() {
let u = parse_graceful("https://example.com./p").unwrap();
assert!(u.to_string().contains("example.com."));
}
#[test]
fn consecutive_dots_in_host_rejected_strict_accepted_graceful() {
if let Ok(u) = parse_graceful("https://example..com/p") {
assert!(u.to_string().contains("example..com"));
}
}
#[test]
fn long_host_within_rfc1035_limit() {
let host: String = core::iter::repeat_n('a', 63)
.chain(core::iter::once('.'))
.chain(core::iter::repeat_n('b', 63))
.chain(core::iter::once('.'))
.chain(core::iter::repeat_n('c', 63))
.chain(core::iter::once('.'))
.chain(core::iter::repeat_n('d', 60))
.collect();
assert_eq!(host.len(), 252);
let uri_str = format!("https://{host}/p");
parse_graceful(&uri_str).unwrap();
}