1mod error;
2mod host;
3mod pair;
4mod parser;
5pub mod path;
6mod scheme;
7pub mod target;
8
9use core::net;
10
11pub use error::Error;
12pub use host::Host;
13pub use scheme::Scheme;
14
15use crate::field::metadata::map::record;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct PortError;
20
21#[derive(Clone, Copy, PartialEq, Eq)]
22pub struct Authority<'a> {
23 host: &'a [u8],
24 port: Option<&'a [u8]>,
25}
26
27impl<'a> Authority<'a> {
28 pub(crate) const fn new(host: &'a [u8], port: Option<&'a [u8]>) -> Self {
29 Self { host, port }
30 }
31
32 pub fn parse(value: &'a [u8]) -> Result<Self, Error> {
33 let parts = Parts::<&[u8]>::parse(value)?;
34 Ok(Self {
35 host: parts.host,
36 port: parts.port,
37 })
38 }
39
40 pub fn is_same(self, other: Self, scheme: Scheme) -> bool {
41 self.spanned().is_same(other.spanned(), scheme)
42 }
43
44 pub fn compare_span(self, other: record::Span<'_>, scheme: Scheme) -> Result<bool, Error> {
45 Ok(self.spanned().is_same(Parsed::parse(other)?, scheme))
46 }
47
48 pub(crate) fn spanned(self) -> Parsed<'a> {
49 Parsed(Parts {
50 host: record::Span::from_slice(self.host),
51 port: self.port.map(record::Span::from_slice),
52 })
53 }
54
55 pub const fn host(self) -> &'a [u8] {
56 self.host
57 }
58
59 pub fn hostname(self) -> &'a [u8] {
60 if let Some(host) = self
61 .host
62 .strip_prefix(b"[")
63 .and_then(|host| host.strip_suffix(b"]"))
64 {
65 host
66 } else {
67 self.host
68 }
69 }
70
71 pub const fn port(self) -> Option<&'a [u8]> {
72 self.port
73 }
74
75 pub fn try_port_u16(self) -> Result<Option<u16>, PortError> {
77 let Some(port) = self.port.filter(|port| !port.is_empty()) else {
78 return Ok(None);
79 };
80 let mut value = 0u16;
81 for byte in port {
82 value = value
83 .checked_mul(10)
84 .and_then(|value| value.checked_add(u16::from(*byte - b'0')))
85 .ok_or(PortError)?;
86 }
87 Ok(Some(value))
88 }
89
90 pub fn parse_ip(value: &[u8]) -> Result<net::IpAddr, Host> {
91 if let Some(value) = value.strip_prefix(b"[") {
92 let Some(value) = value.strip_suffix(b"]") else {
93 return Err(Host::Invalid);
94 };
95 let value = record::Span::from_slice(value);
96 if is_future_span(value) {
97 return Err(Host::Future);
98 }
99 return parser::Parser::new(value)
100 .v6()
101 .map(net::IpAddr::V6)
102 .ok_or(Host::Invalid);
103 }
104 let span = record::Span::from_slice(value);
105 if let Some(ip) = parser::Parser::new(span).v4().map(net::IpAddr::V4) {
106 return Ok(ip);
107 }
108 if let Some(ip) = parser::Parser::new(span).v6().map(net::IpAddr::V6) {
109 return Ok(ip);
110 }
111 if Self::is_reg(value) {
112 Err(Host::Name)
113 } else {
114 Err(Host::Invalid)
115 }
116 }
117
118 pub(crate) fn is_port(value: &[u8]) -> bool {
119 value
120 .strip_prefix(b":")
121 .is_some_and(|port| !port.is_empty() && port.iter().all(u8::is_ascii_digit))
122 }
123
124 pub(crate) fn is_reg(value: &[u8]) -> bool {
125 is_reg_span(record::Span::from_slice(value))
126 }
127
128 pub(crate) fn is_ip(value: &[u8]) -> bool {
129 let value = record::Span::from_slice(value);
130 is_future_span(value) || parser::Parser::new(value).v6().is_some()
131 }
132}
133
134struct Parts<S> {
135 host: S,
136 port: Option<S>,
137}
138
139pub(crate) struct Parsed<'a>(Parts<record::Span<'a>>);
141
142impl<'a> Parsed<'a> {
143 pub(crate) fn parse(value: record::Span<'a>) -> Result<Self, Error> {
144 Parts::parse(value).map(Self)
145 }
146
147 pub(crate) fn has_port(self) -> bool {
149 self.0.port.is_some_and(|port| !port.is_empty())
150 }
151
152 pub(crate) fn is_same(&self, other: Parsed<'_>, scheme: Scheme) -> bool {
153 pair::is_same(self.0.host, other.0.host)
154 && is_port_span_equivalent(self.0.port, other.0.port, scheme)
155 }
156}
157
158trait Source<'a>: Copy {
161 fn span(self) -> record::Span<'a>;
162
163 fn sub(self, start: usize, end: usize) -> Option<Self>;
165
166 fn len(self) -> usize {
167 self.span().len()
168 }
169
170 fn get(self, index: usize) -> Option<u8> {
171 self.span().get(index)
172 }
173}
174
175impl<'a> Source<'a> for &'a [u8] {
176 fn span(self) -> record::Span<'a> {
177 record::Span::from_slice(self)
178 }
179
180 fn sub(self, start: usize, end: usize) -> Option<Self> {
181 self.get(start..end)
182 }
183}
184
185impl<'a> Source<'a> for record::Span<'a> {
186 fn span(self) -> record::Span<'a> {
187 self
188 }
189
190 fn sub(self, start: usize, end: usize) -> Option<Self> {
191 record::Span::slice(self, start..end)
192 }
193}
194
195fn is_digits<'a, S: Source<'a>>(value: S) -> bool {
196 value.span().iter().all(u8::is_ascii_digit)
197}
198
199impl<'a, S: Source<'a>> Parts<S> {
200 fn parse(value: S) -> Result<Self, Error> {
201 let len = value.len();
202 if len == 0 {
203 return Err(Error::Empty);
204 }
205 if value.get(0) == Some(b'[') {
206 let Some(close) = value.span().iter().position(|byte| *byte == b']') else {
207 return Err(Error::InvalidHost);
208 };
209 let Some(literal) = value.sub(1, close) else {
210 return Err(Error::InvalidHost);
211 };
212 let literal = literal.span();
213 if close == 1
214 || (!is_future_span(literal) && parser::Parser::new(literal).v6().is_none())
215 {
216 return Err(Error::InvalidHost);
217 }
218 let Some(suffix) = value.sub(close + 1, len) else {
219 return Err(Error::InvalidHost);
220 };
221 let port = if suffix.len() == 0 {
222 None
223 } else {
224 if suffix.get(0) != Some(b':') {
225 return Err(Error::InvalidPort);
226 }
227 let Some(port) = suffix.sub(1, suffix.len()) else {
228 return Err(Error::InvalidPort);
229 };
230 if !is_digits(port) {
231 return Err(Error::InvalidPort);
232 }
233 Some(port)
234 };
235 let Some(host) = value.sub(0, close + 1) else {
236 return Err(Error::InvalidHost);
237 };
238 return Ok(Self { host, port });
239 }
240 let span = value.span();
241 let mut bytes = span.iter();
242 let colon = bytes.position(|byte| *byte == b':');
243 if bytes.any(|byte| *byte == b':') {
244 return Err(Error::InvalidHost);
245 }
246 let host_end = colon.unwrap_or(len);
247 let Some(host) = value.sub(0, host_end) else {
248 return Err(Error::InvalidHost);
249 };
250 if !is_reg_span(host.span()) {
251 return Err(Error::InvalidHost);
252 }
253 let port = match colon {
254 Some(index) => {
255 let Some(port) = value.sub(index + 1, len) else {
256 return Err(Error::InvalidPort);
257 };
258 if !is_digits(port) {
259 return Err(Error::InvalidPort);
260 }
261 Some(port)
262 }
263 None => None,
264 };
265 Ok(Self { host, port })
266 }
267}
268
269fn is_port_span_equivalent(
270 left: Option<record::Span<'_>>,
271 right: Option<record::Span<'_>>,
272 scheme: Scheme,
273) -> bool {
274 normalized_port_span(left, scheme) == normalized_port_span(right, scheme)
275}
276
277fn normalized_port_span(
278 port: Option<record::Span<'_>>,
279 scheme: Scheme,
280) -> Option<record::Span<'_>> {
281 let port = port.filter(|port| !port.is_empty())?;
282 let first = port.iter().position(|byte| *byte != b'0');
283 let start = match first {
284 Some(index) => index,
285 None => port.len() - 1,
286 };
287 let port = port.slice(start..port.len())?;
288 if (scheme == Scheme::Http && port.is_equal(b"80"))
289 || (scheme == Scheme::Https && port.is_equal(b"443"))
290 {
291 None
292 } else {
293 Some(port)
294 }
295}
296
297fn is_reg_span(value: record::Span<'_>) -> bool {
298 if value.is_empty() {
299 return false;
300 }
301 let mut bytes = value.iter().copied();
302 while let Some(byte) = bytes.next() {
303 if byte == b'%' {
304 if bytes.next().is_none_or(|byte| !byte.is_ascii_hexdigit())
305 || bytes.next().is_none_or(|byte| !byte.is_ascii_hexdigit())
306 {
307 return false;
308 }
309 } else if !is_reg_byte(byte) {
310 return false;
311 }
312 }
313 true
314}
315
316fn is_reg_byte(byte: u8) -> bool {
317 byte.is_ascii_alphanumeric()
318 || matches!(
319 byte,
320 b'-' | b'.'
321 | b'_'
322 | b'~'
323 | b'!'
324 | b'$'
325 | b'&'
326 | b'\''
327 | b'('
328 | b')'
329 | b'*'
330 | b'+'
331 | b','
332 | b';'
333 | b'='
334 )
335}
336
337fn is_future_byte(byte: u8) -> bool {
338 is_reg_byte(byte) || byte == b':'
339}
340
341fn is_future_span(value: record::Span<'_>) -> bool {
342 if !matches!(value.get(0), Some(b'v' | b'V')) {
343 return false;
344 }
345 let Some(dot) = value.iter().position(|byte| *byte == b'.') else {
346 return false;
347 };
348 dot > 1
349 && (1..dot).all(|index| {
350 value
351 .get(index)
352 .is_some_and(|byte| byte.is_ascii_hexdigit())
353 })
354 && dot + 1 < value.len()
355 && (dot + 1..value.len()).all(|index| value.get(index).is_some_and(is_future_byte))
356}