1use crate::{
4 field::{self, scan::Match},
5 request::{self, span},
6 uri::{self, path},
7};
8
9#[derive(Clone, Copy, PartialEq, Eq, Debug)]
10pub enum Error {
11 InvalidHost,
12 MissingHost,
13 EmptyAuthority,
14 InvalidTarget,
15 Path(path::Error),
16 Authority(uri::Error),
17 Span(Operation, span::Error),
18 Layout(request::Error),
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum HostError {
23 Authority(uri::Error),
24 Duplicate,
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub struct Invalid;
29
30#[derive(Clone, Copy, PartialEq, Eq)]
31#[repr(u8)]
32#[derive(Debug)]
33pub enum Operation {
34 Path,
35 Query,
36 Authority,
37 PathShift,
38 QueryShift,
39 AuthorityShift,
40}
41
42impl Operation {
43 fn check(self, start: usize, len: usize) -> Result<span::Span, Error> {
44 match span::Span::checked(start, len) {
45 Ok(span) => Ok(span),
46 Err(cause) => Err(Error::Span(self, cause)),
47 }
48 }
49
50 fn shift(self, span: span::Span, base: usize) -> Result<span::Span, Error> {
51 match span.shifted(base) {
52 Ok(span) => Ok(span),
53 Err(cause) => Err(Error::Span(self, cause)),
54 }
55 }
56}
57
58#[derive(Clone, Copy, PartialEq, Eq)]
59#[repr(u8)]
60pub enum Host {
61 Empty,
62 Authority,
63}
64
65#[derive(Clone, Copy, Default, PartialEq, Eq)]
66#[repr(u8)]
67enum HostState {
68 #[default]
69 Missing,
70 Empty,
71 Authority,
72}
73
74#[derive(Clone, Copy, Default, PartialEq, Eq)]
75pub struct Request {
76 host: HostState,
77}
78
79#[derive(Clone, Copy, PartialEq, Eq)]
80#[repr(u8)]
81pub enum Form {
82 Origin,
83 Absolute,
84 Authority,
85 Asterisk,
86}
87
88#[derive(Clone, Copy, PartialEq, Eq)]
89#[repr(u8)]
90pub(crate) enum Route {
91 Borrowed,
92 Root,
93 None,
94}
95
96#[derive(Clone, Copy, PartialEq, Eq)]
97enum Part {
98 Missing,
99 Present(span::Span),
100}
101
102#[derive(Clone, Copy, PartialEq, Eq)]
104pub struct Target {
105 path: span::Span,
106 query: Part,
107 authority: Part,
108 form: Form,
109 route: Route,
110}
111
112impl Target {
113 pub fn parse(method: &[u8], target: &[u8]) -> Result<Self, Error> {
114 if method.is_empty()
115 || !method.iter().copied().all(field::Error::is_name_byte)
116 || target.is_empty()
117 {
118 return Err(Error::InvalidTarget);
119 }
120 if target == b"*" {
121 return if method == b"OPTIONS" {
122 Ok(Self {
123 path: span::Span::default(),
124 query: Part::Missing,
125 authority: Part::Missing,
126 form: Form::Asterisk,
127 route: Route::None,
128 })
129 } else {
130 Err(Error::InvalidTarget)
131 };
132 }
133 if method == b"CONNECT" {
134 return if is_connect(target) {
135 Ok(Self {
136 path: span::Span::default(),
137 query: Part::Missing,
138 authority: Part::Present(Operation::Authority.check(0, target.len())?),
139 form: Form::Authority,
140 route: Route::None,
141 })
142 } else {
143 Err(Error::InvalidTarget)
144 };
145 }
146 if target[0] == b'/' {
147 let scan = path::Scan::new(target).complete().map_err(Error::Path)?;
148 return origin(scan, 0);
149 }
150
151 let rest = scheme(target).ok_or(Error::InvalidTarget)?;
152 let rest_at = target.len() - rest.len();
153 let (auth, path_at, suffix) = if let Some(rest) = rest.strip_prefix(b"//") {
154 let span_at = rest_at + 2;
155 let (value, suffix) = match Match::find2(b'/', b'?', rest) {
156 Some(found) => (found.prefix(), found.tail()),
157 None => (rest, &[][..]),
158 };
159 let auth = if value.is_empty() {
160 Part::Missing
161 } else {
162 uri::Authority::parse(value).map_err(Error::Authority)?;
163 Part::Present(Operation::Authority.check(span_at, value.len())?)
164 };
165 (auth, span_at + value.len(), suffix)
166 } else {
167 (Part::Missing, rest_at, rest)
168 };
169
170 let scan = path::Scan::new(suffix).complete().map_err(Error::Path)?;
171 let path_len = scan.path_end();
172 let route = if path_len == 0 {
173 Route::Root
174 } else if suffix.starts_with(b"/") {
175 Route::Borrowed
176 } else {
177 Route::None
178 };
179 Ok(Self {
180 path: Operation::Path.check(path_at, path_len)?,
181 query: match scan.query() {
182 path::At::Query(index) => Part::Present(
183 Operation::Query.check(path_at + index + 1, scan.end() - index - 1)?,
184 ),
185 path::At::Path => Part::Missing,
186 },
187 authority: auth,
188 form: Form::Absolute,
189 route,
190 })
191 }
192
193 pub(crate) fn parse_prefix(
194 method: &[u8],
195 bytes: &[u8],
196 base: usize,
197 ) -> Result<Option<(usize, Self)>, Error> {
198 if bytes.first() == Some(&b'/') && method != b"CONNECT" {
199 return match path::Scan::new(bytes).prefix() {
200 path::Outcome::Complete(scan) => {
201 origin(scan, base).map(|target| Some((scan.end(), target)))
202 }
203 path::Outcome::Incomplete => Ok(None),
204 path::Outcome::Invalid(error) => Err(Error::Path(error)),
205 };
206 }
207
208 let Some(end) = Match::find2(b' ', b'\r', bytes) else {
209 return Ok(None);
210 };
211 if end.start() == 0 || end.byte() == b'\r' {
212 return Err(Error::InvalidTarget);
213 }
214 let target = Self::parse(method, end.prefix())?.shifted(base)?;
215 Ok(Some((end.start(), target)))
216 }
217
218 pub fn shifted(self, base: usize) -> Result<Self, Error> {
219 Ok(Self {
220 path: Operation::PathShift.shift(self.path, base)?,
221 query: match self.query {
222 Part::Missing => Part::Missing,
223 Part::Present(query) => Part::Present(Operation::QueryShift.shift(query, base)?),
224 },
225 authority: match self.authority {
226 Part::Missing => Part::Missing,
227 Part::Present(auth) => Part::Present(Operation::AuthorityShift.shift(auth, base)?),
228 },
229 ..self
230 })
231 }
232
233 pub const fn form(self) -> Form {
234 self.form
235 }
236
237 pub const fn path_span(self) -> span::Span {
240 self.path
241 }
242
243 pub const fn query_span(self) -> Option<span::Span> {
244 match self.query {
245 Part::Missing => None,
246 Part::Present(query) => Some(query),
247 }
248 }
249
250 pub const fn authority(self) -> Option<span::Span> {
251 match self.authority {
252 Part::Missing => None,
253 Part::Present(auth) => Some(auth),
254 }
255 }
256
257 pub(crate) fn layout(
258 self,
259 target: span::Span,
260 span: span::Span,
261 ) -> Result<request::Layout, Error> {
262 let form = match self.form {
263 Form::Authority => request::Form::Authority,
264 Form::Asterisk => request::Form::Asterisk,
265 Form::Absolute if matches!(self.route, Route::Root) => request::Form::Root,
266 Form::Origin | Form::Absolute => request::Form::Path,
267 };
268 let route = match self.route {
269 Route::Borrowed | Route::Root => Some(self.path),
270 Route::None => None,
271 };
272 let selected = (!matches!(form, request::Form::Authority)).then_some(span);
273 request::Layout::try_new(form, target, selected, route).map_err(Error::Layout)
274 }
275
276 pub fn route_path(self, request: &[u8]) -> Option<&[u8]> {
279 match self.route {
280 Route::Borrowed => self.path.get(request),
281 Route::Root => Some(b"/"),
282 Route::None => match self.form {
283 Form::Authority | Form::Asterisk => Some(b"*"),
284 Form::Origin | Form::Absolute => None,
285 },
286 }
287 }
288
289 const fn source(self) -> Option<Source> {
290 match self.form {
291 Form::Absolute | Form::Authority => Some(match self.authority {
292 Part::Present(auth) if !auth.is_empty() => Source::Target,
293 Part::Missing | Part::Present(_) => Source::Empty,
294 }),
295 Form::Origin | Form::Asterisk => None,
296 }
297 }
298}
299
300#[derive(Clone, Copy, PartialEq, Eq)]
301#[repr(u8)]
302pub enum Source {
303 Empty,
304 Host,
305 Target,
306}
307
308#[derive(Clone, Copy, PartialEq, Eq)]
309#[repr(u8)]
310pub enum Policy {
311 AllowEmpty,
312 Require,
313}
314
315#[derive(Clone, Copy, PartialEq, Eq)]
316pub struct Plan {
317 target: Form,
318 source: Source,
319}
320
321impl Request {
322 pub fn try_host(self, value: &[u8]) -> Result<Self, HostError> {
323 let host = Host::parse(value).map_err(HostError::Authority)?;
324 self.try_push(host)
325 }
326
327 pub(crate) fn try_push(mut self, value: Host) -> Result<Self, HostError> {
328 if !matches!(self.host, HostState::Missing) {
329 return Err(HostError::Duplicate);
330 }
331 self.host = match value {
332 Host::Empty => HostState::Empty,
333 Host::Authority => HostState::Authority,
334 };
335 Ok(self)
336 }
337
338 pub const fn has_host(self) -> bool {
339 !matches!(self.host, HostState::Missing)
340 }
341
342 pub fn finish(self, method: &[u8], target: &[u8], version: &[u8]) -> Result<Plan, Error> {
343 let target = Target::parse(method, target)?;
344 self.finish_plan(target, version)
345 }
346
347 pub fn finish_plan(self, target: Target, version: &[u8]) -> Result<Plan, Error> {
350 if version == b"HTTP/1.1" && matches!(self.host, HostState::Missing) {
351 return Err(Error::MissingHost);
352 }
353
354 let source = match target.source() {
355 Some(source) => source,
356 None => match self.host {
357 HostState::Authority => Source::Host,
358 HostState::Missing | HostState::Empty => Source::Empty,
359 },
360 };
361 Ok(Plan {
362 target: target.form(),
363 source,
364 })
365 }
366}
367
368impl Plan {
369 pub const fn target_form(self) -> Form {
370 self.target
371 }
372
373 pub const fn source(self) -> Source {
374 self.source
375 }
376
377 pub const fn require(self) -> Result<Self, Error> {
380 if matches!(self.source, Source::Empty) {
381 Err(Error::EmptyAuthority)
382 } else {
383 Ok(self)
384 }
385 }
386
387 pub const fn span(self, target: Target, host: Option<span::Span>) -> Result<span::Span, Error> {
390 match self.source {
391 Source::Host => match host {
392 Some(span) => Ok(span),
393 None => Err(Error::InvalidHost),
394 },
395 Source::Target => match target.authority() {
396 Some(span) => Ok(span),
397 None => Err(Error::InvalidTarget),
398 },
399 Source::Empty => Err(Error::EmptyAuthority),
400 }
401 }
402}
403
404impl Host {
405 pub fn parse(value: &[u8]) -> Result<Self, uri::Error> {
406 let value = field::Error::trim_ows(value);
407 if value.is_empty() {
408 return Ok(Self::Empty);
409 }
410 uri::Authority::parse(value)?;
411 Ok(Self::Authority)
412 }
413}
414
415fn origin(scan: path::Plan, base: usize) -> Result<Target, Error> {
416 let path_end = scan.path_end();
417 Ok(Target {
418 path: Operation::Path.check(base, path_end)?,
419 query: match scan.query() {
420 path::At::Query(index) => {
421 Part::Present(Operation::Query.check(base + index + 1, scan.end() - index - 1)?)
422 }
423 path::At::Path => Part::Missing,
424 },
425 authority: Part::Missing,
426 form: Form::Origin,
427 route: Route::Borrowed,
428 })
429}
430
431fn scheme(value: &[u8]) -> Option<&[u8]> {
432 let mut bytes = value.iter();
433 if !bytes.next()?.is_ascii_alphabetic() {
434 return None;
435 }
436 while let Some(&byte) = bytes.next() {
437 if byte == b':' {
438 return Some(bytes.as_slice());
439 }
440 if !(byte.is_ascii_alphanumeric() || matches!(byte, b'+' | b'-' | b'.')) {
441 return None;
442 }
443 }
444 None
445}
446
447fn is_connect(value: &[u8]) -> bool {
448 if value.first() == Some(&b'[') {
449 let Some(close) = value.iter().position(|byte| *byte == b']') else {
450 return false;
451 };
452 return close > 1
453 && uri::Authority::is_ip(&value[1..close])
454 && uri::Authority::is_port(&value[close + 1..]);
455 }
456
457 let Some(colon) = value.iter().rposition(|byte| *byte == b':') else {
458 return false;
459 };
460 !value[..colon].contains(&b':')
461 && uri::Authority::is_reg(&value[..colon])
462 && uri::Authority::is_port(&value[colon..])
463}
464
465const _: () = assert!(core::mem::size_of::<Target>() <= 4 * core::mem::size_of::<usize>());
466const _: () = assert!(core::mem::size_of::<Part>() == core::mem::size_of::<span::Span>());
467const _: () = assert!(core::mem::size_of::<Request>() == 1);
468const _: () = assert!(core::mem::size_of::<Plan>() <= 2);
469const _: () = assert!(core::mem::size_of::<Operation>() == 1);
470const _: () = assert!(core::mem::size_of::<Error>() == 5 * core::mem::size_of::<usize>());
471const _: () = assert!(core::mem::size_of::<Option<Error>>() == core::mem::size_of::<Error>());