1use crate::{Error, Framing, Header, Limits, Request, Response, Status, TargetForm, Version};
4use simoom_sys::scan::{field_line, find_byte, scan, FIELD_VALUE, TCHAR, URI};
5
6#[derive(Default)]
8struct Semantics<'b> {
9 host: Option<&'b [u8]>,
10 hosts: u8,
11 content_length: Option<u64>,
12 te_codings: u32,
13 te_chunked: u32,
14 te_last_chunked: bool,
15 close: bool,
16 keep_alive: bool,
17 conn_upgrade: bool,
18 expect_continue: bool,
19 expect_other: bool,
20 upgrade: Option<&'b [u8]>,
21}
22
23#[derive(Clone, Copy, PartialEq, Eq)]
24enum Section {
25 Head,
26 Trailers,
27}
28
29#[inline]
30fn is_ws(b: u8) -> bool {
31 b == b' ' || b == b'\t'
32}
33
34fn trim(mut v: &[u8]) -> &[u8] {
35 while let [first, rest @ ..] = v {
36 if !is_ws(*first) {
37 break;
38 }
39 v = rest;
40 }
41 while let [rest @ .., last] = v {
42 if !is_ws(*last) {
43 break;
44 }
45 v = rest;
46 }
47 v
48}
49
50pub fn has_token(value: &[u8], token: &[u8]) -> bool {
53 value.split(|&b| b == b',').any(|t| trim(t).eq_ignore_ascii_case(token))
54}
55
56fn hex(b: u8) -> bool {
57 b.is_ascii_hexdigit()
58}
59
60fn valid_host(v: &[u8], need_port: bool) -> bool {
63 if v.is_empty() {
64 return !need_port;
65 }
66 let rest = if v[0] == b'[' {
67 let Some(end) = v.iter().position(|&b| b == b']') else { return false };
68 let inner = &v[1..end];
69 if inner.is_empty() || !inner.iter().all(|&b| hex(b) || b == b':' || b == b'.') {
70 return false;
71 }
72 &v[end + 1..]
73 } else {
74 let colon = v.iter().position(|&b| b == b':').unwrap_or(v.len());
75 let host = &v[..colon];
76 let mut i = 0;
77 while i < host.len() {
78 let b = host[i];
79 if b == b'%' {
80 if i + 2 >= host.len() || !hex(host[i + 1]) || !hex(host[i + 2]) {
81 return false;
82 }
83 i += 3;
84 continue;
85 }
86 if !(b.is_ascii_alphanumeric() || b"-._~!$&'()*+,;=".contains(&b)) {
87 return false;
88 }
89 i += 1;
90 }
91 if host.is_empty() {
92 return false;
93 }
94 &v[colon..]
95 };
96 match rest {
97 [] => !need_port,
98 [b':', port @ ..] => port.iter().all(u8::is_ascii_digit) && (!need_port || !port.is_empty()),
99 _ => false,
100 }
101}
102
103fn valid_absolute(t: &[u8], brackets: bool) -> bool {
106 let Some(colon) = t.iter().position(|&b| b == b':') else { return false };
107 let scheme = &t[..colon];
108 if scheme.is_empty()
109 || !scheme[0].is_ascii_alphabetic()
110 || !scheme.iter().all(|&b| b.is_ascii_alphanumeric() || b == b'+' || b == b'-' || b == b'.')
111 {
112 return false;
113 }
114 if let Some(rest) = t[colon + 1..].strip_prefix(b"//") {
117 let end = rest.iter().position(|&b| b == b'/' || b == b'?').unwrap_or(rest.len());
118 if rest[..end].contains(&b'@') {
119 return false;
120 }
121 }
122 if !brackets {
123 return true;
124 }
125 let Some(rest) = t[colon + 1..].strip_prefix(b"//") else { return false };
126 let auth_end = rest.iter().position(|&b| b == b'/' || b == b'?').unwrap_or(rest.len());
127 let auth = &rest[..auth_end];
128 let host = match auth.iter().rposition(|&b| b == b'@') {
129 Some(at) => &auth[at + 1..],
130 None => auth,
131 };
132 !rest[auth_end..].iter().any(|&b| b == b'[' || b == b']') && valid_host(host, false)
133}
134
135fn parse_content_length(v: &[u8]) -> Result<u64, Error> {
136 let digits = |s: &[u8]| -> Option<u64> {
137 if s.is_empty() {
138 return None;
139 }
140 s.iter().try_fold(0u64, |n, &b| {
141 if b.is_ascii_digit() {
142 n.checked_mul(10)?.checked_add(u64::from(b - b'0'))
143 } else {
144 None
145 }
146 })
147 };
148 if let Some(n) = digits(v) {
149 return Ok(n);
150 }
151 if v.contains(&b',') && v.split(|&b| b == b',').all(|p| digits(trim(p)).is_some()) {
152 return Err(Error::DuplicateContentLength);
153 }
154 Err(Error::InvalidContentLength)
155}
156
157impl<'b> Semantics<'b> {
158 fn field(&mut self, name: &[u8], value: &'b [u8]) -> Result<(), Error> {
159 match name.len() {
160 4 if name.eq_ignore_ascii_case(b"host") => {
161 self.hosts = self.hosts.saturating_add(1);
162 if self.hosts > 1 {
163 return Err(Error::DuplicateHost);
164 }
165 if !valid_host(value, false) {
166 return Err(Error::InvalidHost);
167 }
168 self.host = Some(value);
169 }
170 6 if name.eq_ignore_ascii_case(b"expect") => {
171 if value.eq_ignore_ascii_case(b"100-continue") {
172 self.expect_continue = true;
173 } else {
174 self.expect_other = true;
175 }
176 }
177 7 if name.eq_ignore_ascii_case(b"upgrade") => {
178 if self.upgrade.is_some() {
179 return Err(Error::DuplicateUpgrade);
180 }
181 self.upgrade = Some(value);
182 }
183 10 if name.eq_ignore_ascii_case(b"connection") => {
184 for t in value.split(|&b| b == b',') {
185 let t = trim(t);
186 if t.eq_ignore_ascii_case(b"close") {
187 self.close = true;
188 } else if t.eq_ignore_ascii_case(b"keep-alive") {
189 self.keep_alive = true;
190 } else if t.eq_ignore_ascii_case(b"upgrade") {
191 self.conn_upgrade = true;
192 }
193 }
194 }
195 14 if name.eq_ignore_ascii_case(b"content-length") => {
196 if self.content_length.is_some() {
197 return Err(Error::DuplicateContentLength);
198 }
199 self.content_length = Some(parse_content_length(value)?);
200 }
201 17 if name.eq_ignore_ascii_case(b"transfer-encoding") => {
202 for c in value.split(|&b| b == b',') {
203 let c = trim(c);
204 if c.is_empty() || scan(c, &TCHAR) != c.len() {
205 return Err(Error::InvalidTransferEncoding);
206 }
207 self.te_codings += 1;
208 self.te_last_chunked = c.eq_ignore_ascii_case(b"chunked");
209 self.te_chunked += u32::from(self.te_last_chunked);
210 }
211 }
212 _ => {}
213 }
214 Ok(())
215 }
216
217 fn request_framing(&self, version: Version, lim: &Limits) -> Result<Framing, Error> {
219 if self.te_codings > 0 {
220 if self.content_length.is_some() {
221 return Err(Error::TeWithContentLength);
222 }
223 if version == Version::Http10 {
224 return Err(Error::TransferEncodingHttp10);
225 }
226 if self.te_chunked != 1 || !self.te_last_chunked {
227 return Err(Error::ChunkedNotFinal);
228 }
229 if self.te_codings > 1 {
230 return Err(Error::UnsupportedTransferCoding);
231 }
232 return Ok(Framing::Chunked);
233 }
234 match self.content_length {
235 Some(n) if n > lim.max_body => Err(Error::BodyTooLarge),
236 Some(0) | None => Ok(Framing::None),
237 Some(n) => Ok(Framing::Length(n)),
238 }
239 }
240
241 fn keep_alive(&self, version: Version) -> bool {
242 !self.close && (version == Version::Http11 || self.keep_alive)
243 }
244}
245
246#[allow(clippy::too_many_arguments)]
250fn parse_fields<'b>(
251 buf: &'b [u8],
252 mut p: usize,
253 start: usize,
254 hs: &mut [Header<'b>],
255 store: bool,
256 lim: &Limits,
257 section: Section,
258 sem: &mut Semantics<'b>,
259) -> Result<Option<(usize, usize)>, Error> {
260 let (max_total, too_large) = match section {
261 Section::Head => (lim.max_head, Error::HeadTooLarge),
262 Section::Trailers => (lim.max_trailers, Error::TrailersTooLarge),
263 };
264 let partial = |ls: usize| -> Result<Option<(usize, usize)>, Error> {
265 if buf.len() - ls > lim.max_header_line {
266 Err(Error::HeaderLineTooLong)
267 } else if buf.len() - start > max_total {
268 Err(too_large)
269 } else {
270 Ok(None)
271 }
272 };
273 let mut count = 0;
274 loop {
275 let ls = p;
276 let Some(&first) = buf.get(p) else { return partial(ls) };
277 match first {
278 b'\r' => {
279 let Some(&lf) = buf.get(p + 1) else { return partial(ls) };
280 if lf != b'\n' {
281 return Err(Error::BareCr);
282 }
283 p += 2;
284 if p - start > max_total {
285 return Err(too_large);
286 }
287 return Ok(Some((p, count)));
288 }
289 b'\n' => return Err(Error::BareLf),
290 b' ' | b'\t' => return Err(Error::ObsFold),
291 _ => {}
292 }
293 let (n, v0, v1) = field_line(&buf[p..]);
295 let Some(&stop) = buf.get(p + n) else { return partial(ls) };
296 match stop {
297 b':' if n > 0 => {}
298 b' ' | b'\t' if n > 0 => return Err(Error::WhitespaceBeforeColon),
299 _ => return Err(Error::InvalidHeaderName),
300 }
301 let name = &buf[p..p + n];
302 let vs = p + v0;
303 p += v1;
304 let Some(&cr) = buf.get(p) else { return partial(ls) };
305 if cr != b'\r' {
306 return Err(if cr == b'\n' { Error::BareLf } else { Error::InvalidHeaderValue });
307 }
308 let Some(&lf) = buf.get(p + 1) else { return partial(ls) };
309 if lf != b'\n' {
310 return Err(Error::BareCr);
311 }
312 let mut ve = p;
313 while ve > vs && is_ws(buf[ve - 1]) {
314 ve -= 1;
315 }
316 let value = &buf[vs..ve];
317 p += 2;
318 if p - ls > lim.max_header_line {
319 return Err(Error::HeaderLineTooLong);
320 }
321 if p - start > max_total {
322 return Err(too_large);
323 }
324 if count >= lim.max_headers || (store && count >= hs.len()) {
325 return Err(Error::TooManyHeaders);
326 }
327 if store {
328 hs[count] = Header { name, value };
329 }
330 count += 1;
331 match section {
332 Section::Head => sem.field(name, value)?,
333 Section::Trailers => {
334 if name.eq_ignore_ascii_case(b"content-length")
335 || name.eq_ignore_ascii_case(b"transfer-encoding")
336 || name.eq_ignore_ascii_case(b"host")
337 {
338 return Err(Error::ForbiddenTrailer);
339 }
340 }
341 }
342 }
343}
344
345fn skip_empty_lines(buf: &[u8], lim: &Limits) -> Result<Option<usize>, Error> {
347 let mut p = 0;
348 loop {
349 match buf.get(p) {
350 None => return Ok(None),
351 Some(b'\r') => match buf.get(p + 1) {
352 None => return Ok(None),
353 Some(b'\n') => {
354 p += 2;
355 if p > lim.max_head {
356 return Err(Error::HeadTooLarge);
357 }
358 }
359 Some(_) => return Err(Error::BareCr),
360 },
361 Some(b'\n') => return Err(Error::BareLf),
362 Some(_) => return Ok(Some(p)),
363 }
364 }
365}
366
367fn version_shape(v: &[u8]) -> Result<(), Error> {
370 let ok = v.iter().enumerate().take(8).skip(5).all(|(i, &b)| if i == 6 { b == b'.' } else { b.is_ascii_digit() });
371 if ok {
372 Ok(())
373 } else {
374 Err(Error::InvalidVersion)
375 }
376}
377
378fn parse_version_crlf(buf: &[u8], p: usize) -> Result<Option<(Version, usize)>, Error> {
381 let rest = &buf[p..];
382 let want = b"HTTP/";
383 let k = rest.len().min(5);
384 if rest[..k] != want[..k] {
385 return Err(Error::InvalidVersion);
386 }
387 if rest.len() < 10 {
388 version_shape(rest)?;
391 if rest.len() == 9 && rest[8] != b'\r' {
392 return Err(if rest[8] == b'\n' { Error::BareLf } else { Error::InvalidVersion });
393 }
394 return Ok(None);
395 }
396 let version = match &rest[..8] {
397 b"HTTP/1.1" => Version::Http11,
398 b"HTTP/1.0" => Version::Http10,
399 [.., a, b'.', b] if a.is_ascii_digit() && b.is_ascii_digit() => return Err(Error::VersionNotSupported),
400 _ => return Err(Error::InvalidVersion),
401 };
402 match &rest[8..10] {
403 b"\r\n" => Ok(Some((version, p + 10))),
404 [b'\r', _] => Err(Error::BareCr),
405 [b'\n', _] => Err(Error::BareLf),
406 _ => Err(Error::InvalidVersion),
407 }
408}
409
410pub fn parse_request<'b, 'h>(
416 buf: &'b [u8],
417 hs: &'h mut [Header<'b>],
418 lim: &Limits,
419) -> Result<Status<Request<'b, 'h>>, Error> {
420 let Some(ls) = skip_empty_lines(buf, lim)? else { return Ok(Status::Partial) };
421 let line_partial = || -> Result<Status<Request<'b, 'h>>, Error> {
422 if buf.len() - ls > lim.max_request_line {
423 Err(Error::RequestLineTooLong)
424 } else if buf.len() > lim.max_head {
425 Err(Error::HeadTooLarge)
426 } else {
427 Ok(Status::Partial)
428 }
429 };
430 let mut p = ls;
432 let n = scan(&buf[p..], &TCHAR);
433 let Some(&sp) = buf.get(p + n) else { return line_partial() };
434 if n == 0 || sp != b' ' {
435 return Err(Error::InvalidMethod);
436 }
437 let method = &buf[p..p + n];
438 p += n + 1;
439 let t0 = p;
441 let mut brackets = false;
442 loop {
443 p += scan(&buf[p..], &URI);
444 let Some(&b) = buf.get(p) else { return line_partial() };
445 match b {
446 b' ' => break,
447 b'%' => {
448 if p + 2 >= buf.len() {
449 if buf[p + 1..].iter().all(|&c| hex(c)) {
450 return line_partial();
451 }
452 return Err(Error::InvalidTarget);
453 }
454 if !hex(buf[p + 1]) || !hex(buf[p + 2]) {
455 return Err(Error::InvalidTarget);
456 }
457 p += 3;
458 }
459 b'[' | b']' => {
460 brackets = true;
461 p += 1;
462 }
463 _ => return Err(Error::InvalidTarget),
464 }
465 }
466 let target = &buf[t0..p];
467 if target.is_empty() {
468 return Err(Error::InvalidTarget);
469 }
470 p += 1;
471 let Some((version, after)) = parse_version_crlf(buf, p)? else { return line_partial() };
472 if after - ls > lim.max_request_line {
473 return Err(Error::RequestLineTooLong);
474 }
475 let connect = method == b"CONNECT";
476 let form = if target[0] == b'/' && !connect {
477 if brackets {
478 return Err(Error::InvalidTarget);
479 }
480 TargetForm::Origin
481 } else if target == b"*" && method == b"OPTIONS" {
482 TargetForm::Asterisk
483 } else if connect {
484 if !valid_host(target, true) {
485 return Err(Error::InvalidTarget);
486 }
487 TargetForm::Authority
488 } else if valid_absolute(target, brackets) {
489 TargetForm::Absolute
490 } else {
491 return Err(Error::InvalidTarget);
492 };
493 let mut sem = Semantics::default();
494 let Some((end, count)) = parse_fields(buf, after, 0, hs, true, lim, Section::Head, &mut sem)? else {
495 return Ok(Status::Partial);
496 };
497 if version == Version::Http11 && sem.hosts == 0 {
498 return Err(Error::MissingHost);
499 }
500 let framing = sem.request_framing(version, lim)?;
501 let http11 = version == Version::Http11;
502 if sem.expect_other && http11 {
503 return Err(Error::ExpectationFailed);
504 }
505 Ok(Status::Complete(Request {
506 method,
507 target,
508 form,
509 version,
510 headers: &hs[..count],
511 framing,
512 keep_alive: sem.keep_alive(version),
513 expect_continue: sem.expect_continue && http11,
514 upgrade: if sem.conn_upgrade && http11 { sem.upgrade } else { None },
515 host: sem.host,
516 head_len: end,
517 }))
518}
519
520#[derive(Clone, Copy, Debug, Default)]
524pub struct RequestParser {
525 scanned: u32,
526 line_start: u32,
527}
528
529impl RequestParser {
530 pub fn new() -> Self {
532 Self::default()
533 }
534
535 pub fn parse<'b, 'h>(
538 &mut self,
539 buf: &'b [u8],
540 hs: &'h mut [Header<'b>],
541 lim: &Limits,
542 ) -> Result<Status<Request<'b, 'h>>, Error> {
543 let scanned = self.scanned as usize;
544 if scanned > 0 && buf.len() >= scanned {
545 let mut i = scanned.saturating_sub(3);
546 let mut line_start = self.line_start as usize;
547 let mut complete = false;
548 while let Some(k) = find_byte(&buf[i..], b'\n') {
549 let at = i + k;
550 if at >= 3 && &buf[at - 3..=at] == b"\r\n\r\n" {
551 complete = true;
552 break;
553 }
554 line_start = at + 1;
555 i = at + 1;
556 }
557 let line_limit = if line_start == 0 { lim.max_request_line } else { lim.max_header_line };
558 if !complete && buf.len() - line_start <= line_limit && buf.len() <= lim.max_head {
559 self.scanned = buf.len() as u32;
560 self.line_start = line_start as u32;
561 return Ok(Status::Partial);
562 }
563 }
564 let r = parse_request(buf, hs, lim);
565 match r {
566 Ok(Status::Partial) => {
567 self.scanned = buf.len().max(1) as u32;
568 self.line_start = buf.iter().rposition(|&b| b == b'\n').map_or(0, |i| i + 1) as u32;
569 }
570 _ => *self = Self::default(),
571 }
572 r
573 }
574
575 pub fn reset(&mut self) {
577 *self = Self::default();
578 }
579}
580
581pub fn parse_response<'b, 'h>(
584 buf: &'b [u8],
585 hs: &'h mut [Header<'b>],
586 lim: &Limits,
587 request_method: &[u8],
588) -> Result<Status<Response<'b, 'h>>, Error> {
589 let line_partial = || -> Result<Status<Response<'b, 'h>>, Error> {
590 if buf.len() > lim.max_request_line {
591 Err(Error::RequestLineTooLong)
592 } else {
593 Ok(Status::Partial)
594 }
595 };
596 let k = buf.len().min(5);
597 if buf[..k] != b"HTTP/"[..k] {
598 return Err(Error::InvalidVersion);
599 }
600 if buf.len() < 13 {
601 version_shape(buf)?;
602 if buf.len() > 8 && buf[8] != b' ' {
603 return Err(Error::InvalidVersion);
604 }
605 if buf.iter().skip(9).any(|b| !b.is_ascii_digit()) {
606 return Err(Error::InvalidStatus);
607 }
608 return line_partial();
609 }
610 let version = match &buf[..8] {
611 b"HTTP/1.1" => Version::Http11,
612 b"HTTP/1.0" => Version::Http10,
613 [.., a, b'.', b] if a.is_ascii_digit() && b.is_ascii_digit() => return Err(Error::VersionNotSupported),
614 _ => return Err(Error::InvalidVersion),
615 };
616 if buf[8] != b' ' {
617 return Err(Error::InvalidVersion);
618 }
619 let code = &buf[9..12];
620 if !code.iter().all(u8::is_ascii_digit) || buf[12] != b' ' {
621 return Err(Error::InvalidStatus);
622 }
623 let status = code.iter().fold(0u16, |n, &d| n * 10 + u16::from(d - b'0'));
624 if !(100..=599).contains(&status) {
625 return Err(Error::InvalidStatus);
626 }
627 let rs = 13;
628 let mut p = rs + scan(&buf[rs..], &FIELD_VALUE);
629 let Some(&cr) = buf.get(p) else { return line_partial() };
630 if cr != b'\r' {
631 return Err(if cr == b'\n' { Error::BareLf } else { Error::InvalidReason });
632 }
633 let Some(&lf) = buf.get(p + 1) else { return line_partial() };
634 if lf != b'\n' {
635 return Err(Error::BareCr);
636 }
637 let reason = &buf[rs..p];
638 p += 2;
639 if p > lim.max_request_line {
640 return Err(Error::RequestLineTooLong);
641 }
642 let mut sem = Semantics::default();
643 let Some((end, count)) = parse_fields(buf, p, 0, hs, true, lim, Section::Head, &mut sem)? else {
644 return Ok(Status::Partial);
645 };
646 let ok_connect = request_method == b"CONNECT" && (200..300).contains(&status);
647 let upgraded = status == 101 || ok_connect;
648 let framing = if request_method == b"HEAD" || status < 200 || status == 204 || status == 304 || ok_connect {
649 Framing::None
650 } else if sem.te_codings > 0 {
651 if sem.content_length.is_some() {
652 return Err(Error::TeWithContentLength);
653 }
654 if sem.te_chunked > 1 {
655 return Err(Error::ChunkedNotFinal);
656 }
657 match (sem.te_last_chunked, sem.te_codings) {
658 (true, 1) => Framing::Chunked,
659 (true, _) => return Err(Error::UnsupportedTransferCoding),
660 (false, _) => Framing::Close,
661 }
662 } else {
663 match sem.content_length {
664 Some(0) => Framing::None,
665 Some(n) => Framing::Length(n),
666 None => Framing::Close,
667 }
668 };
669 Ok(Status::Complete(Response {
670 version,
671 status,
672 reason,
673 headers: &hs[..count],
674 framing,
675 keep_alive: framing != Framing::Close && sem.keep_alive(version),
676 upgraded,
677 head_len: end,
678 }))
679}
680
681pub fn parse_trailers<'b, 'h>(
684 section: &'b [u8],
685 hs: &'h mut [Header<'b>],
686 lim: &Limits,
687) -> Result<&'h [Header<'b>], Error> {
688 if section.is_empty() {
689 return Ok(&hs[..0]);
690 }
691 let mut sem = Semantics::default();
692 match parse_fields(section, 0, 0, hs, true, lim, Section::Trailers, &mut sem)? {
693 Some((end, count)) if end == section.len() => Ok(&hs[..count]),
694 _ => Err(Error::InvalidHeaderValue),
695 }
696}
697
698pub(crate) fn check_trailers(section: &[u8], lim: &Limits) -> Result<(), Error> {
700 let mut sem = Semantics::default();
701 match parse_fields(section, 0, 0, &mut [], false, lim, Section::Trailers, &mut sem)? {
702 Some((end, _)) if end == section.len() => Ok(()),
703 _ => Err(Error::InvalidHeaderValue),
704 }
705}