use core::net::{IpAddr, Ipv4Addr};
use super::{assert_origin_form, lazy, parse_graceful, parse_strict, userinfo_str};
use crate::address::Host;
use crate::uri::parser::MAX_URI_LEN;
use crate::uri::{Component, ParseError};
#[test]
fn crlf_anywhere_rejected() {
for s in [
"/foo\r/bar",
"/foo\n/bar",
"/foo\r\n/bar",
"/foo?bar\rbaz",
"/foo?bar\nbaz",
"/foo#frag\r",
"/foo#frag\n",
"http://example.com\r/",
"http://example.com\n/",
"http://example.com/\r",
] {
assert!(
matches!(parse_graceful(s), Err(ParseError::ControlCharInUri { .. })),
"graceful must reject {s:?}"
);
assert!(
matches!(parse_strict(s), Err(ParseError::ControlCharInUri { .. })),
"strict must reject {s:?}"
);
}
}
#[test]
fn nul_byte_rejected() {
for s in ["/\0foo", "/foo\0", "/foo?\0", "/foo#\0", "http://\0/"] {
assert!(matches!(
parse_graceful(s),
Err(ParseError::ControlCharInUri { byte: 0, .. })
));
}
}
#[test]
fn tab_rejected() {
for s in [
"/foo\tbar",
"http://example\t.com/",
"http://example.com/foo\tbar",
] {
assert!(matches!(
parse_graceful(s),
Err(ParseError::ControlCharInUri { byte: b'\t', .. })
));
}
}
#[test]
fn del_byte_rejected() {
for s in ["/foo\x7Fbar", "http://example.com/\x7F"] {
assert!(matches!(
parse_graceful(s),
Err(ParseError::ControlCharInUri { byte: 0x7F, .. })
));
}
}
#[test]
fn backslash_not_folded_to_slash() {
let u = parse_graceful("/path\\foo").unwrap();
assert_origin_form(&u, "/path\\foo", None, None);
assert!(matches!(
parse_strict("/path\\foo"),
Err(ParseError::StrictViolation)
));
}
#[test]
fn backslash_authority_spoof_rejected_in_both_modes() {
assert!(matches!(
parse_graceful("https://example.com\\evil.com/"),
Err(ParseError::InvalidComponent(Component::Host))
));
parse_strict("https://example.com\\evil.com/").unwrap_err();
}
#[test]
fn alt_ipv4_octal_not_treated_as_ipv4() {
let u = parse_graceful("http://0177.0.0.1/").unwrap();
let auth = lazy(&u).authority.as_ref().unwrap();
assert!(matches!(auth.host, Host::Name(_)));
assert_ne!(
auth.host,
Host::Address(IpAddr::V4(Ipv4Addr::LOCALHOST)),
"alt-form must not silently map to 127.0.0.1"
);
}
#[test]
fn alt_ipv4_hex_not_treated_as_ipv4() {
let u = parse_graceful("http://0x7f.0.0.1/").unwrap();
let auth = lazy(&u).authority.as_ref().unwrap();
assert!(matches!(auth.host, Host::Name(_)));
assert_ne!(auth.host, Host::Address(IpAddr::V4(Ipv4Addr::LOCALHOST)));
}
#[test]
fn alt_ipv4_single_int_not_treated_as_ipv4() {
let u = parse_graceful("http://2130706433/").unwrap();
let auth = lazy(&u).authority.as_ref().unwrap();
assert!(matches!(auth.host, Host::Name(_)));
assert_ne!(auth.host, Host::Address(IpAddr::V4(Ipv4Addr::LOCALHOST)));
}
#[test]
fn percent_2f_not_decoded_in_path() {
let u = parse_graceful("/admin/%2F../secret").unwrap();
assert_origin_form(&u, "/admin/%2F../secret", None, None);
}
#[test]
fn overlong_input_rejected() {
let big = "/".to_owned() + &"a".repeat(MAX_URI_LEN);
assert!(matches!(
parse_graceful(&big),
Err(ParseError::TooLong { .. })
));
}
#[test]
fn max_uri_len_minus_one_accepted() {
let just_under = "/".to_owned() + &"a".repeat(MAX_URI_LEN - 1);
parse_graceful(&just_under).unwrap();
}
#[test]
fn exactly_max_uri_len_accepted() {
let exactly = "/".to_owned() + &"a".repeat(MAX_URI_LEN - 1);
assert_eq!(exactly.len(), MAX_URI_LEN);
parse_graceful(&exactly).unwrap();
}
#[test]
fn public_uri_max_len_matches_internal_cap() {
use crate::uri::Uri;
assert_eq!(Uri::MAX_LEN, MAX_URI_LEN);
}
#[test]
fn unbracketed_ipv6_in_authority_rejected() {
let r = parse_graceful("http://2001:db8::1/");
assert!(matches!(
r,
Err(ParseError::InvalidComponent(
Component::Port | Component::Host
))
));
}
#[test]
fn ipv6_zone_id_rejected() {
let r = parse_graceful("https://[fe80::1%25en0]/");
assert!(matches!(r, Err(ParseError::IPv6ZoneNotSupported)));
}
#[test]
fn eager_and_lazy_authority_paths_agree_on_ipv6_zone() {
use crate::address::Authority;
let auth_str = "[fe80::1%25en0]:8080";
let eager_err = Authority::try_from(auth_str).is_err();
let lazy_err = parse_graceful(&format!("http://{auth_str}/")).is_err();
assert!(eager_err && lazy_err, "both paths must reject IPv6 zone");
}
#[test]
fn unbalanced_brackets_rejected() {
for s in [
"http://[2001:db8::1/", "http://2001:db8::1]/", "http://[2001:db8::1]:abc/", ] {
assert!(
parse_graceful(s).is_err(),
"must reject malformed brackets in {s:?}"
);
}
}
#[test]
fn userinfo_confusion_does_not_bleed_into_host() {
let u = parse_graceful("http://trusted.com@evil.com/").unwrap();
let l = lazy(&u);
assert_eq!(userinfo_str(l), Some("trusted.com"));
assert_eq!(
l.authority.as_ref().unwrap().host,
Host::Name(crate::address::Domain::from_static("evil.com"))
);
}
#[test]
fn at_in_path_is_not_userinfo() {
let u = parse_graceful("/foo@bar").unwrap();
assert_origin_form(&u, "/foo@bar", None, None);
}
#[test]
fn double_at_in_authority_uses_last_at_split() {
let u = parse_graceful("http://user@info@host/").unwrap();
let l = lazy(&u);
assert_eq!(userinfo_str(l), Some("user@info"));
assert_eq!(
l.authority.as_ref().unwrap().host,
Host::Name(crate::address::Domain::from_static("host"))
);
}
#[test]
fn port_0_accepted() {
let u = parse_graceful("http://example.com:0/").unwrap();
let auth = lazy(&u).authority.as_ref().unwrap();
assert_eq!(auth.port, crate::address::OptPort::Set(0));
}
#[test]
fn port_65535_accepted() {
let u = parse_graceful("http://example.com:65535/").unwrap();
let auth = lazy(&u).authority.as_ref().unwrap();
assert_eq!(auth.port, crate::address::OptPort::Set(65535));
}
#[test]
fn port_65536_overflow_rejected() {
let r = parse_graceful("http://example.com:65536/");
assert!(matches!(
r,
Err(ParseError::InvalidComponent(Component::Port))
));
}
#[test]
fn port_negative_rejected() {
let r = parse_graceful("http://example.com:-80/");
assert!(matches!(
r,
Err(ParseError::InvalidComponent(Component::Port))
));
}
#[test]
fn empty_authority_accepted_as_empty_uninterpreted_host() {
use crate::address::Host;
let u = parse_graceful("file:///tmp/x").unwrap();
let auth = lazy(&u).authority.as_ref().unwrap();
match &auth.host {
Host::Uninterpreted(h) => assert!(h.as_str().is_empty()),
other => panic!("expected empty Uninterpreted host, got {other:?}"),
}
assert_eq!(auth.port, crate::address::OptPort::Unset);
let u = parse_graceful("http://").unwrap();
let auth = lazy(&u).authority.as_ref().unwrap();
assert!(matches!(&auth.host, Host::Uninterpreted(h) if h.as_str().is_empty()));
}
#[test]
fn scheme_with_only_colon_accepted() {
let u = parse_graceful("http:").unwrap();
assert!(lazy(&u).scheme.is_some());
}