1use crate::bytes::{Data, Parse, Source};
4use crate::err;
5use crate::error::Error;
6use std::io::Read;
7use std::path::Path;
8
9#[derive(Debug)]
11pub struct Request<'a, const HEADER_SIZE_MAX: usize = 4096> {
12 pub header: Data,
14 pub method: Data,
16 pub target: Data,
18 pub version: Data,
20 pub fields: Vec<(Data, Data)>,
22 pub stream: &'a mut Source,
24}
25impl<'a, const HEADER_SIZE_MAX: usize> Request<'a, HEADER_SIZE_MAX> {
27 pub fn from_stream(stream: &'a mut Source) -> Result<Option<Self>, Error> {
29 let header = Self::read_header(stream)?;
31 if header.is_empty() {
32 return Ok(None);
33 }
34
35 let mut header_parsing = header.clone();
37 let (method, target, version) = {
38 let (method, target, version) = Self::parse_start_line(&mut header_parsing)?;
39 (method.trim(), target.trim(), version.trim())
40 };
41
42 let mut fields = Vec::new();
44 while !header_parsing.eq(b"\r\n") {
45 let (key, value) = Self::parse_field(&mut header_parsing)?;
46 fields.push((key, value));
47 }
48
49 Ok(Some(Self { header, method, target, version, fields, stream }))
51 }
52
53 fn read_header(stream: &mut Source) -> Result<Data, Error> {
55 let mut header = Vec::with_capacity(HEADER_SIZE_MAX);
57 'read_loop: for byte in stream.bytes() {
58 let byte = byte?;
60 header.push(byte);
61
62 if header.ends_with(b"\r\n\r\n") {
64 break 'read_loop;
65 }
66 if header.len() == HEADER_SIZE_MAX {
67 return Err(err!("HTTP header is too large"));
68 }
69 }
70
71 header.shrink_to_fit();
73 let header = Data::from(header);
74 Ok(header)
75 }
76 #[allow(clippy::type_complexity)]
78 fn parse_start_line(header: &mut Data) -> Result<(Data, Data, Data), Error> {
79 let mut line = header.split_off(b"\r\n").ok_or_else(|| err!("Truncated HTTP start line: {header}"))?;
81 let method = line.split_off(b" ").ok_or_else(|| err!("Invalid HTTP start line: {line}"))?;
82 let target = line.split_off(b" ").ok_or_else(|| err!("Invalid HTTP start line: {line}"))?;
83 Ok((method, target, line))
84 }
85 fn parse_field(header: &mut Data) -> Result<(Data, Data), Error> {
87 let mut line = header.split_off(b"\r\n").ok_or_else(|| err!("Truncated HTTP header field: {header}"))?;
89 let key = line.split_off(b":").ok_or_else(|| err!("Invalid HTTP header field: {line}"))?;
90
91 let key = key.trim();
93 let value = line.trim();
94 Ok((key, value))
95 }
96}
97impl<'a, const HEADER_SIZE_MAX: usize> Request<'a, HEADER_SIZE_MAX> {
99 #[cfg(target_family = "unix")]
105 pub fn target_path(&self) -> Option<&Path> {
106 use std::ffi::OsStr;
107 use std::os::unix::ffi::OsStrExt;
108
109 let target = OsStr::from_bytes(&self.target);
111 Some(Path::new(target))
112 }
113 #[cfg(not(any(target_family = "unix")))]
119 pub fn target_path(&self) -> Option<&Path> {
120 let target = str::from_utf8(&self.target).ok()?;
122 Some(Path::new(target))
123 }
124
125 pub fn field<N>(&self, name: N) -> Option<&Data>
127 where
128 N: AsRef<[u8]>,
129 {
130 for (key, value) in &self.fields {
131 if key.eq_ignore_ascii_case(name.as_ref()) {
133 return Some(value);
134 }
135 }
136 None
137 }
138 pub fn content_length(&self) -> Result<Option<u64>, Error> {
140 let Some(content_length_raw) = self.field("Content-Length") else {
142 return Ok(None);
144 };
145
146 let content_length_utf8 = str::from_utf8(content_length_raw)?;
148 let content_length: u64 = content_length_utf8.parse()?;
149 Ok(Some(content_length))
150 }
151
152 pub fn read_body_data(&mut self, content_length_max: u64) -> Result<Option<Data>, Error> {
155 let is_chunked = self.field("Transfer-Encoding").map(|encoding| encoding.eq_ignore_ascii_case(b"chunked"));
157 let (None | Some(false)) = is_chunked else {
158 return Ok(None);
160 };
161
162 let Some(content_length) = self.content_length()? else {
164 return Ok(None);
166 };
167
168 let true = content_length <= content_length_max else {
170 return Err(err!("HTTP body is too large"));
172 };
173
174 let mut body = Vec::new();
176 let body_len = self.stream.take(content_length).read_to_end(&mut body)? as u64;
177 let true = body_len == content_length else {
178 return Err(err!("Truncated HTTP body"))?;
180 };
181
182 let body = Data::from(body);
184 Ok(Some(body))
185 }
186
187 pub fn has_connection_close(&self) -> bool {
189 for (key, value) in &self.fields {
191 if key.eq_ignore_ascii_case(b"Connection") {
192 return value.eq_ignore_ascii_case(b"close");
193 }
194 }
195 false
196 }
197}