1mod authority;
4mod path;
5
6use crate::parser::char;
7use crate::parser::str::{
8 find_split, find_split2_hole, find_split_hole, satisfy_chars_with_pct_encoded,
9};
10use crate::spec::Spec;
11use crate::validate::Error;
12
13use self::authority::validate_authority;
14pub(crate) use self::authority::{validate_host, validate_userinfo};
15pub(crate) use self::path::validate_path;
16use self::path::{
17 validate_path_abempty, validate_path_absolute_authority_absent,
18 validate_path_relative_authority_absent,
19};
20
21pub(crate) fn validate_scheme(i: &str) -> Result<(), Error> {
23 debug_assert!(!i.is_empty());
24 let bytes = i.as_bytes();
25 if bytes[0].is_ascii_alphabetic()
26 && bytes[1..]
27 .iter()
28 .all(|&b| b.is_ascii() && char::is_ascii_scheme_continue(b))
29 {
30 Ok(())
31 } else {
32 Err(Error::new())
33 }
34}
35
36pub(crate) fn validate_query<S: Spec>(i: &str) -> Result<(), Error> {
38 let is_valid =
39 satisfy_chars_with_pct_encoded(i, char::is_ascii_frag_query, char::is_nonascii_query::<S>);
40 if is_valid {
41 Ok(())
42 } else {
43 Err(Error::new())
44 }
45}
46
47fn validate_authority_path_abempty<S: Spec>(i: &str) -> Result<(), Error> {
49 let (maybe_authority, maybe_path) = match find_split(i, b'/') {
50 Some(v) => v,
51 None => (i, ""),
52 };
53 validate_authority::<S>(maybe_authority)?;
54 validate_path_abempty::<S>(maybe_path)
55}
56
57#[inline]
59pub(crate) fn validate_uri<S: Spec>(i: &str) -> Result<(), Error> {
60 validate_uri_reference_common::<S>(i, UriReferenceRule::Absolute)
61}
62
63#[inline]
65pub(crate) fn validate_uri_reference<S: Spec>(i: &str) -> Result<(), Error> {
66 validate_uri_reference_common::<S>(i, UriReferenceRule::Any)
67}
68
69#[inline]
71pub(crate) fn validate_absolute_uri<S: Spec>(i: &str) -> Result<(), Error> {
72 validate_uri_reference_common::<S>(i, UriReferenceRule::AbsoluteWithoutFragment)
73}
74
75#[derive(Clone, Copy, PartialEq, Eq, Hash)]
77enum UriReferenceRule {
78 Absolute,
82 AbsoluteWithoutFragment,
86 Any,
90}
91
92impl UriReferenceRule {
93 #[inline]
95 #[must_use]
96 fn is_relative_allowed(self) -> bool {
97 self == Self::Any
98 }
99
100 #[inline]
102 #[must_use]
103 fn is_fragment_allowed(self) -> bool {
104 matches!(self, Self::Absolute | Self::Any)
105 }
106}
107
108fn validate_uri_reference_common<S: Spec>(
110 i: &str,
111 ref_rule: UriReferenceRule,
112) -> Result<(), Error> {
113 let (i, _scheme) = match find_split_hole(i, b':') {
115 None => {
116 if ref_rule.is_relative_allowed() {
117 return validate_relative_ref::<S>(i);
118 } else {
119 return Err(Error::new());
120 }
121 }
122 Some(("", _)) => return Err(Error::new()),
123 Some((maybe_scheme, rest)) => {
124 if validate_scheme(maybe_scheme).is_err() {
125 if ref_rule.is_relative_allowed() {
128 return validate_relative_ref::<S>(i);
129 } else {
130 return Err(Error::new());
131 }
132 }
133 (rest, maybe_scheme)
134 }
135 };
136
137 let after_path = match i.strip_prefix("//") {
139 Some(i) => {
140 let (maybe_authority_path, after_path) = match find_split2_hole(i, b'?', b'#') {
141 Some((maybe_authority_path, c, rest)) => (maybe_authority_path, Some((c, rest))),
142 None => (i, None),
143 };
144 validate_authority_path_abempty::<S>(maybe_authority_path)?;
145 after_path
146 }
147 None => {
148 let (maybe_path, after_path) = match find_split2_hole(i, b'?', b'#') {
149 Some((maybe_path, c, rest)) => (maybe_path, Some((c, rest))),
150 None => (i, None),
151 };
152 validate_path_absolute_authority_absent::<S>(maybe_path)?;
154 after_path
155 }
156 };
157
158 if let Some((first, rest)) = after_path {
160 validate_after_path::<S>(first, rest, ref_rule.is_fragment_allowed())?;
161 }
162 Ok(())
163}
164
165pub(crate) fn validate_relative_ref<S: Spec>(i: &str) -> Result<(), Error> {
167 let after_path = match i.strip_prefix("//") {
169 Some(i) => {
170 let (maybe_authority_path, after_path) = match find_split2_hole(i, b'?', b'#') {
171 Some((maybe_authority_path, c, rest)) => (maybe_authority_path, Some((c, rest))),
172 None => (i, None),
173 };
174 validate_authority_path_abempty::<S>(maybe_authority_path)?;
175 after_path
176 }
177 None => {
178 let (maybe_path, after_path) = match find_split2_hole(i, b'?', b'#') {
179 Some((maybe_path, c, rest)) => (maybe_path, Some((c, rest))),
180 None => (i, None),
181 };
182 validate_path_relative_authority_absent::<S>(maybe_path)?;
184 after_path
185 }
186 };
187
188 if let Some((first, rest)) = after_path {
190 validate_after_path::<S>(first, rest, true)?;
191 }
192 Ok(())
193}
194
195fn validate_after_path<S: Spec>(first: u8, rest: &str, accept_fragment: bool) -> Result<(), Error> {
197 let (maybe_query, maybe_fragment) = if first == b'?' {
198 match find_split_hole(rest, b'#') {
199 Some(v) => v,
200 None => (rest, ""),
201 }
202 } else {
203 debug_assert_eq!(first, b'#');
204 ("", rest)
205 };
206 validate_query::<S>(maybe_query)?;
207 if !accept_fragment && !maybe_fragment.is_empty() {
208 return Err(Error::new());
209 }
210 validate_fragment::<S>(maybe_fragment)
211}
212
213pub(crate) fn validate_fragment<S: Spec>(i: &str) -> Result<(), Error> {
215 let is_valid = satisfy_chars_with_pct_encoded(
216 i,
217 char::is_ascii_frag_query,
218 char::is_nonascii_fragment::<S>,
219 );
220 if is_valid {
221 Ok(())
222 } else {
223 Err(Error::new())
224 }
225}