1use crate::{
4 component::Scheme,
5 imp::{HostMeta, Meta, RiMaybeRef, RmrRef},
6 parse,
7 pct_enc::{
8 decode_octet, encode_byte,
9 table::{is_iprivate, is_ucschar, UNRESERVED},
10 },
11 resolve,
12 utf8::{self, Utf8Chunks},
13};
14use alloc::{string::String, vec::Vec};
15use borrow_or_share::Bos;
16use core::{
17 fmt::{self, Write},
18 num::NonZeroUsize,
19};
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum NormalizeError {
24 PathUnderflow,
28}
29
30impl fmt::Display for NormalizeError {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 let msg = match self {
33 Self::PathUnderflow => "underflow occurred in path resolution",
34 };
35 f.write_str(msg)
36 }
37}
38
39#[cfg(feature = "impl-error")]
40impl crate::Error for NormalizeError {}
41
42#[derive(Clone, Copy)]
44#[allow(missing_debug_implementations)]
45#[must_use]
46pub struct Normalizer {
47 allow_path_underflow: bool,
48 default_port_f: fn(&Scheme) -> Option<u16>,
49}
50
51impl Normalizer {
52 pub fn new() -> Self {
54 Self {
55 allow_path_underflow: true,
56 default_port_f: Scheme::default_port,
57 }
58 }
59
60 pub fn allow_path_underflow(mut self, value: bool) -> Self {
78 self.allow_path_underflow = value;
79 self
80 }
81
82 pub fn default_port_with(mut self, f: fn(&Scheme) -> Option<u16>) -> Self {
106 self.default_port_f = f;
107 self
108 }
109
110 pub fn normalize<R: RiMaybeRef>(&self, r: &R) -> Result<R::WithVal<String>, NormalizeError>
121 where
122 R::Val: Bos<str>,
123 {
124 normalize(
125 r.make_ref(),
126 R::CONSTRAINTS.ascii_only,
127 self.allow_path_underflow,
128 self.default_port_f,
129 )
130 .map(RiMaybeRef::from_pair)
131 }
132}
133
134impl Default for Normalizer {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
140pub(crate) fn normalize(
141 r: RmrRef<'_, '_>,
142 ascii_only: bool,
143 allow_path_underflow: bool,
144 default_port_f: fn(&Scheme) -> Option<u16>,
145) -> Result<(String, Meta), NormalizeError> {
146 let mut buf = String::with_capacity(r.as_str().len());
149
150 let path = r.path().as_str();
151 let mut path_buf = String::with_capacity(path.len());
152
153 if r.has_scheme() && path.starts_with('/') {
154 normalize_estr(&mut buf, path, false, ascii_only, false);
155
156 let underflow_occurred = resolve::remove_dot_segments(&mut path_buf, 0, &[&buf]);
157 if underflow_occurred && !allow_path_underflow {
158 return Err(NormalizeError::PathUnderflow);
159 }
160
161 buf.clear();
162 } else {
163 normalize_estr(&mut path_buf, path, false, ascii_only, false);
165 }
166
167 let mut meta = Meta::default();
168
169 if let Some(scheme) = r.scheme_opt() {
170 buf.push_str(scheme.as_str());
171 buf.make_ascii_lowercase();
172 meta.scheme_end = NonZeroUsize::new(buf.len());
173 buf.push(':');
174 }
175
176 if let Some(auth) = r.authority() {
177 buf.push_str("//");
178
179 if let Some(userinfo) = auth.userinfo() {
180 normalize_estr(&mut buf, userinfo.as_str(), false, ascii_only, false);
181 buf.push('@');
182 }
183
184 let mut auth_meta = auth.meta();
185 auth_meta.host_bounds.0 = buf.len();
186 match auth_meta.host_meta {
187 HostMeta::Ipv4(..) => buf.push_str(auth.host()),
189 #[cfg(feature = "net")]
190 HostMeta::Ipv6(addr) => write!(buf, "[{addr}]").unwrap(),
191 #[cfg(not(feature = "net"))]
192 HostMeta::Ipv6() => {
193 buf.push('[');
194 write_v6(&mut buf, parse::parse_v6(&auth.host().as_bytes()[1..]));
195 buf.push(']');
196 }
197 HostMeta::IpvFuture => {
198 let start = buf.len();
199 buf.push_str(auth.host());
200
201 buf[start..].make_ascii_lowercase();
202 }
203 HostMeta::RegName => {
204 let start = buf.len();
205 let host = auth.host();
206 normalize_estr(&mut buf, host, true, ascii_only, false);
207
208 if buf.len() < start + host.len() {
209 auth_meta.host_meta = parse::parse_v4_or_reg_name(&buf.as_bytes()[start..]);
211 }
212 }
213 }
214 auth_meta.host_bounds.1 = buf.len();
215 meta.auth_meta = Some(auth_meta);
216
217 if let Some(port) = auth.port() {
218 if !port.is_empty() {
219 let mut eq_default = false;
220 if let Some(scheme) = r.scheme_opt() {
221 if let Some(default) = default_port_f(scheme) {
222 eq_default = port.as_str().parse().ok() == Some(default);
223 }
224 }
225 if !eq_default {
226 buf.push(':');
227 buf.push_str(port.as_str());
228 }
229 }
230 }
231 }
232
233 meta.path_bounds.0 = buf.len();
234 if r.has_scheme() && !r.has_authority() && path_buf.starts_with("//") {
236 buf.push_str("/.");
237 }
238 buf.push_str(&path_buf);
239 meta.path_bounds.1 = buf.len();
240
241 if let Some(query) = r.query() {
242 buf.push('?');
243 normalize_estr(&mut buf, query.as_str(), false, ascii_only, true);
244 meta.query_end = NonZeroUsize::new(buf.len());
245 }
246
247 if let Some(fragment) = r.fragment() {
248 buf.push('#');
249 normalize_estr(&mut buf, fragment.as_str(), false, ascii_only, false);
250 }
251
252 Ok((buf, meta))
253}
254
255fn normalize_estr(
256 buf: &mut String,
257 s: &str,
258 to_ascii_lowercase: bool,
259 ascii_only: bool,
260 is_query: bool,
261) {
262 let s = s.as_bytes();
263 let mut i = 0;
264
265 if ascii_only {
266 while i < s.len() {
267 let mut x = s[i];
268 if x == b'%' {
269 let (hi, lo) = (s[i + 1], s[i + 2]);
270 let mut octet = decode_octet(hi, lo);
271 if UNRESERVED.allows_ascii(octet) {
272 if to_ascii_lowercase {
273 octet = octet.to_ascii_lowercase();
274 }
275 buf.push(octet as char);
276 } else {
277 buf.push('%');
278 buf.push(hi.to_ascii_uppercase() as char);
279 buf.push(lo.to_ascii_uppercase() as char);
280 }
281 i += 3;
282 } else {
283 if to_ascii_lowercase {
284 x = x.to_ascii_lowercase();
285 }
286 buf.push(x as char);
287 i += 1;
288 }
289 }
290 } else {
291 let mut dec_buf = Vec::new();
292
293 while i < s.len() {
294 if s[i] == b'%' {
295 let (hi, lo) = (s[i + 1], s[i + 2]);
296 let mut octet = decode_octet(hi, lo);
297 if UNRESERVED.allows_ascii(octet) {
298 consume_dec_buf(buf, &mut dec_buf, is_query);
299
300 if to_ascii_lowercase {
301 octet = octet.to_ascii_lowercase();
302 }
303 buf.push(octet as char);
304 } else {
305 dec_buf.push(octet);
306 }
307 i += 3;
308 } else {
309 consume_dec_buf(buf, &mut dec_buf, is_query);
310
311 let (x, len) = utf8::next_code_point(s, i);
312 let mut x = char::from_u32(x).unwrap();
313 if to_ascii_lowercase {
314 x = x.to_ascii_lowercase();
315 }
316 buf.push(x);
317 i += len;
318 }
319 }
320 consume_dec_buf(buf, &mut dec_buf, is_query);
321 }
322}
323
324fn consume_dec_buf(buf: &mut String, dec_buf: &mut Vec<u8>, is_query: bool) {
325 for chunk in Utf8Chunks::new(dec_buf) {
326 for ch in chunk.valid().chars() {
327 if is_ucschar(ch as u32) || (is_query && is_iprivate(ch as u32)) {
328 buf.push(ch);
329 } else {
330 for x in ch.encode_utf8(&mut [0; 4]).bytes() {
331 encode_byte(x, buf);
332 }
333 }
334 }
335 for &x in chunk.invalid() {
336 encode_byte(x, buf);
337 }
338 }
339 dec_buf.clear();
340}
341
342#[cfg(not(feature = "net"))]
344fn write_v6(buf: &mut String, segments: [u16; 8]) {
345 if let [0, 0, 0, 0, 0, 0xffff, ab, cd] = segments {
346 let [a, b] = ab.to_be_bytes();
347 let [c, d] = cd.to_be_bytes();
348 write!(buf, "::ffff:{a}.{b}.{c}.{d}").unwrap();
349 } else {
350 #[derive(Copy, Clone, Default)]
351 struct Span {
352 start: usize,
353 len: usize,
354 }
355
356 let zeroes = {
358 let mut longest = Span::default();
359 let mut current = Span::default();
360
361 for (i, &segment) in segments.iter().enumerate() {
362 if segment == 0 {
363 if current.len == 0 {
364 current.start = i;
365 }
366
367 current.len += 1;
368
369 if current.len > longest.len {
370 longest = current;
371 }
372 } else {
373 current = Span::default();
374 }
375 }
376
377 longest
378 };
379
380 #[inline]
382 fn write_subslice(buf: &mut String, chunk: &[u16]) {
383 if let Some((first, tail)) = chunk.split_first() {
384 write!(buf, "{first:x}").unwrap();
385 for segment in tail {
386 write!(buf, ":{segment:x}").unwrap();
387 }
388 }
389 }
390
391 if zeroes.len > 1 {
392 write_subslice(buf, &segments[..zeroes.start]);
393 buf.push_str("::");
394 write_subslice(buf, &segments[zeroes.start + zeroes.len..]);
395 } else {
396 write_subslice(buf, &segments);
397 }
398 }
399}