1use std::fmt;
4use std::ops::Range;
5use std::rc::Rc;
6
7use crate::parse::{header_lines, Head};
8
9pub(crate) enum BodyBytes {
10 InBuffer(Range<usize>),
12 Decoded(Vec<u8>),
14}
15
16pub struct Request {
22 pub(crate) buf: Rc<Vec<u8>>,
23 pub(crate) start: usize,
25 pub(crate) head: Head,
26 pub(crate) body: BodyBytes,
27}
28
29impl Request {
30 fn head_bytes(&self) -> &[u8] {
31 &self.buf[self.start..self.start + self.head.len]
32 }
33
34 fn head_str(&self, range: Range<usize>) -> &str {
35 std::str::from_utf8(&self.head_bytes()[range]).unwrap_or("")
37 }
38
39 pub fn method(&self) -> &str {
41 self.head_str(self.head.method.clone())
42 }
43
44 pub fn target(&self) -> &str {
46 self.head_str(self.head.target.clone())
47 }
48
49 pub fn path(&self) -> &str {
52 let target = self.target();
53 let target = match target.find("://") {
54 Some(i) if !target.starts_with('/') => {
55 let rest = &target[i + 3..];
56 rest.find('/').map_or("/", |slash| &rest[slash..])
57 }
58 _ => target,
59 };
60 target.split('?').next().unwrap_or(target)
61 }
62
63 pub fn query(&self) -> Option<&str> {
65 self.target().split_once('?').map(|(_, query)| query)
66 }
67
68 pub fn version_minor(&self) -> u8 {
70 self.head.minor
71 }
72
73 pub fn headers(&self) -> impl Iterator<Item = (&str, &[u8])> {
75 header_lines(&self.head_bytes()[self.head.headers.clone()])
76 .map(|(name, value)| (std::str::from_utf8(name).unwrap_or(""), value))
77 }
78
79 pub fn header(&self, name: &str) -> Option<&[u8]> {
81 self.headers()
82 .find(|(n, _)| n.eq_ignore_ascii_case(name))
83 .map(|(_, value)| value)
84 }
85
86 pub fn header_str(&self, name: &str) -> Option<&str> {
88 self.header(name).and_then(|v| std::str::from_utf8(v).ok())
89 }
90
91 pub fn body(&self) -> &[u8] {
93 match &self.body {
94 BodyBytes::InBuffer(range) => &self.buf[range.clone()],
95 BodyBytes::Decoded(body) => body,
96 }
97 }
98}
99
100impl fmt::Debug for Request {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 f.debug_struct("Request")
103 .field("method", &self.method())
104 .field("target", &self.target())
105 .field("version_minor", &self.version_minor())
106 .field("body_len", &self.body().len())
107 .finish()
108 }
109}