use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use super::ParserMode;
use super::check_pct_encoded;
use crate::address::parse_utils;
use crate::address::{Domain, Host, UninterpretedHost};
use crate::byte_sets::{
is_control_byte, is_ipvfuture_tail_byte, is_reg_name_byte, is_userinfo_byte,
};
use crate::uri::lazy::LazyAuthority;
use crate::uri::{Component, ParseError};
use rama_core::bytes::Bytes;
pub(super) struct AuthorityScan {
pub(super) authority: Option<LazyAuthority>,
pub(super) path_start: usize,
}
pub(super) fn parse_optional_authority(
bytes: &Bytes,
start: usize,
mode: ParserMode,
) -> Result<AuthorityScan, ParseError> {
if bytes.len() >= start + 2 && bytes[start] == b'/' && bytes[start + 1] == b'/' {
let auth_start = start + 2;
let auth_end = bytes[auth_start..]
.iter()
.position(|&b| matches!(b, b'/' | b'?' | b'#'))
.map_or(bytes.len(), |p| p + auth_start);
let auth = parse_authority(bytes, auth_start, auth_end, mode)?;
Ok(AuthorityScan {
authority: Some(auth),
path_start: auth_end,
})
} else {
Ok(AuthorityScan {
authority: None,
path_start: start,
})
}
}
pub(super) fn parse_authority(
bytes: &Bytes,
start: usize,
end: usize,
mode: ParserMode,
) -> Result<LazyAuthority, ParseError> {
let mut k = start;
while k < end {
let b = bytes[k];
if is_control_byte(b) {
return Err(ParseError::ControlCharInUri { at: k, byte: b });
}
if mode == ParserMode::Graceful && b >= 0x80 {
let seq_len = super::check_utf8_sequence(bytes, k)?;
k += seq_len;
continue;
}
k += 1;
}
let userinfo_range = parse_utils::find_userinfo_split(&bytes[start..end])
.map(|rel| (start as u16, (start + rel) as u16));
let host_start = userinfo_range.map_or(start, |(_, e)| (e as usize) + 1);
if let (ParserMode::Strict, Some((s, e))) = (mode, userinfo_range) {
validate_userinfo_strict(&bytes[s as usize..e as usize])?;
}
let host_view = &bytes[host_start..end];
let (host, port) = parse_host_and_port(bytes, host_start, host_view, mode)?;
Ok(LazyAuthority {
userinfo_range,
host,
port,
})
}
fn validate_userinfo_strict(bytes: &[u8]) -> Result<(), ParseError> {
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if b == b'%' {
check_pct_encoded(bytes, i)?;
i += 3;
continue;
}
if !is_userinfo_byte(b) {
return Err(ParseError::StrictViolation);
}
i += 1;
}
Ok(())
}
fn parse_host_and_port(
parent: &Bytes,
host_start: usize,
view: &[u8],
mode: ParserMode,
) -> Result<(Host, crate::address::OptPort), ParseError> {
if view.is_empty() {
return Ok((
Host::Uninterpreted(UninterpretedHost::from_validated_bytes(
parent.slice(host_start..host_start),
false,
)),
crate::address::OptPort::Unset,
));
}
if view[0] == b'[' {
let close_rel = view
.iter()
.position(|&b| b == b']')
.ok_or(ParseError::InvalidComponent(Component::Host))?;
let inside = &view[1..close_rel];
let inside_start = host_start + 1;
let host = if matches!(inside.first(), Some(b'v' | b'V')) {
validate_ipvfuture(inside)?;
let body = parent.slice(inside_start..inside_start + inside.len());
Host::Uninterpreted(UninterpretedHost::from_validated_bytes(body, true))
} else {
if parse_utils::ipv6_bracket_has_zone(inside) {
return Err(ParseError::IPv6ZoneNotSupported);
}
let Ok(s) = core::str::from_utf8(inside) else {
return Err(ParseError::InvalidComponent(Component::Host));
};
let Ok(addr) = s.parse::<Ipv6Addr>() else {
return Err(ParseError::InvalidComponent(Component::Host));
};
Host::Address(IpAddr::V6(addr))
};
let after = &view[close_rel + 1..];
let port = match after {
[] => crate::address::OptPort::Unset,
[b':', rest @ ..] => parse_port(rest)?,
_ => return Err(ParseError::InvalidComponent(Component::Authority)),
};
return Ok((host, port));
}
let (host_bytes_rel, port) = match view.iter().rposition(|&b| b == b':') {
Some(colon) => {
let port = parse_port(&view[colon + 1..])?;
(&view[..colon], port)
}
None => (view, crate::address::OptPort::Unset),
};
if host_bytes_rel.is_empty() {
return Err(ParseError::InvalidComponent(Component::Host));
}
let host_bytes_len = host_bytes_rel.len();
validate_reg_name(host_bytes_rel, mode)?;
let Ok(host_str) = core::str::from_utf8(host_bytes_rel) else {
return Err(ParseError::InvalidComponent(Component::Host));
};
let host = if let Ok(v4) = host_str.parse::<Ipv4Addr>() {
Host::Address(IpAddr::V4(v4))
} else if host_bytes_rel.is_ascii() && Domain::try_from(host_str).is_ok() {
let domain_bytes = parent.slice(host_start..host_start + host_bytes_len);
let domain = unsafe { Domain::from_maybe_borrowed_unchecked(domain_bytes) };
Host::Name(domain)
} else {
let body = parent.slice(host_start..host_start + host_bytes_len);
Host::Uninterpreted(UninterpretedHost::from_validated_bytes(body, false))
};
Ok((host, port))
}
pub(crate) fn validate_reg_name_graceful(bytes: &[u8]) -> Result<(), ParseError> {
validate_reg_name(bytes, ParserMode::Graceful)
}
pub(crate) fn validate_reg_name_strict(bytes: &[u8]) -> Result<(), ParseError> {
validate_reg_name(bytes, ParserMode::Strict)
}
fn validate_reg_name(bytes: &[u8], mode: ParserMode) -> Result<(), ParseError> {
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if b == b'%' {
check_pct_encoded(bytes, i)?;
if let Some(decoded) =
crate::byte_sets::pct_decoded_control_byte(bytes[i + 1], bytes[i + 2])
{
return Err(ParseError::ControlCharInUri {
at: i,
byte: decoded,
});
}
i += 3;
continue;
}
if is_reg_name_byte(b) {
i += 1;
continue;
}
if b >= 0x80 {
if mode == ParserMode::Strict {
return Err(ParseError::StrictViolation);
}
i += super::check_utf8_sequence(bytes, i)?;
continue;
}
return Err(if mode == ParserMode::Strict {
ParseError::StrictViolation
} else {
ParseError::InvalidComponent(Component::Host)
});
}
Ok(())
}
pub(crate) fn validate_ipvfuture(inside: &[u8]) -> Result<(), ParseError> {
let Some((&v, rest)) = inside.split_first() else {
return Err(ParseError::InvalidComponent(Component::Host));
};
if v != b'v' && v != b'V' {
return Err(ParseError::InvalidComponent(Component::Host));
}
let dot_at = rest
.iter()
.position(|&b| b == b'.')
.ok_or(ParseError::InvalidComponent(Component::Host))?;
if dot_at == 0 {
return Err(ParseError::InvalidComponent(Component::Host));
}
let hex = &rest[..dot_at];
let tail = &rest[dot_at + 1..];
if !hex.iter().all(|&b| b.is_ascii_hexdigit()) {
return Err(ParseError::InvalidComponent(Component::Host));
}
if tail.is_empty() || !tail.iter().all(|&b| is_ipvfuture_tail_byte(b)) {
return Err(ParseError::InvalidComponent(Component::Host));
}
Ok(())
}
fn parse_port(bytes: &[u8]) -> Result<crate::address::OptPort, ParseError> {
if bytes.is_empty() {
return Ok(crate::address::OptPort::Empty);
}
parse_utils::parse_port_bytes(bytes)
.map(crate::address::OptPort::Set)
.ok_or(ParseError::InvalidComponent(Component::Port))
}