1use builders::Builder;
3mod parse;
4use std::{
5 collections::HashMap,
6 env,
7 ffi::OsStr,
8 io::{BufReader, Read, Write},
9 path::Path,
10};
11
12use parse::parse_request;
13
14use crate::{encoding::Chunked, HttpMethod, HttpResponse, HttpStream, Result, StatusCode};
15
16#[derive(Builder, Debug)]
20pub struct HttpRequest {
21 method: HttpMethod,
22 url: Box<str>,
23 #[builder(each = "header")]
24 headers: HashMap<Box<str>, Box<str>>,
25 #[builder(each = "param")]
26 params: HashMap<Box<str>, Box<str>>,
27 #[builder(each = "response_header")]
28 response_headers: HashMap<Box<str>, Box<str>>,
29 #[builder(def = 1.0)]
30 version: f32,
31 #[builder(disabled = true)]
32 #[builder(def = { BufReader::new(HttpStream::dummy()) })]
33 stream: BufReader<HttpStream>,
34 #[builder(def = 200u16)]
35 status: u16,
36 #[builder(optional = true)]
37 body: Option<Box<[u8]>>,
38}
39
40impl HttpRequest {
41 pub fn parse(stream: impl Into<HttpStream>) -> Result<Self> {
43 let stream = BufReader::new(stream.into());
44 parse_request(stream)
45 }
46 #[inline]
47 pub fn keep_alive(self) -> Result<Self> {
48 let mut req = parse_request(self.stream)?;
49 req.set_header("Connection", "keep-alive");
50 Ok(req)
51 }
52 #[inline]
53 #[must_use]
54 pub fn stream(&self) -> &HttpStream {
55 self.stream.get_ref()
56 }
57 #[inline]
59 #[must_use]
60 pub fn url(&self) -> &str {
61 &self.url
62 }
63
64 #[inline]
65 pub fn set_url(&mut self, url: impl Into<Box<str>>) {
66 self.url = url.into();
67 }
68 #[inline]
70 #[must_use]
71 pub fn params(&self) -> &HashMap<Box<str>, Box<str>> {
72 &self.params
73 }
74
75 #[inline]
76 #[must_use]
77 pub fn param(&self, key: &str) -> Option<&str> {
78 self.params.get(key).map(AsRef::as_ref)
79 }
80 pub fn filename(&self) -> Result<Box<str>> {
86 let mut cwd = env::current_dir()?;
87 cwd.push(Path::new(OsStr::new(&self.url[1..])));
88 let cwd = cwd.to_str().ok_or("Error getting cwd")?;
89 Ok(Box::from(cwd))
90 }
91 pub fn write_to(&self, f: &mut dyn Write) -> Result<()> {
92 write!(f, "{} {}", self.method(), self.url())?;
93 if !self.params().is_empty() {
94 write!(f, "?")?;
95 for (k, v) in self.params() {
96 let ke = url::encode(k).unwrap_or("".into());
97 let ve = url::encode(v).unwrap_or("".into());
98 write!(f, "{ke}={ve}&")?;
99 }
100 }
101 write!(f, " HTTP/{}\r\n", self.version())?;
102
103 for (k, v) in self.headers() {
104 write!(f, "{k}: {v}\r\n")?;
105 }
106
107 if let Some(ref b) = self.body {
108 f.write_all(b)?;
109 }
110
111 write!(f, "\r\n")?;
112
113 Ok(())
114 }
115 pub fn send_to(&self, mut stream: HttpStream) -> crate::Result<HttpResponse> {
120 self.write_to(&mut stream)?;
121 stream.flush()?;
122 HttpResponse::parse(stream)
123 }
124 #[inline]
125 #[must_use]
126 pub fn method(&self) -> &HttpMethod {
127 &self.method
128 }
129
130 #[inline]
131 #[must_use]
132 pub fn status(&self) -> u16 {
133 self.status
134 }
135
136 #[inline]
137 pub fn set_status(&mut self, status: u16) -> &mut Self {
138 self.status = status;
139 self
140 }
141
142 #[inline]
143 #[must_use]
144 pub fn version(&self) -> f32 {
145 self.version
146 }
147
148 #[inline]
153 #[must_use]
154 pub fn content_length(&self) -> usize {
155 match self.headers.get("Content-Length") {
156 Some(l) => l.parse().unwrap_or(0),
157 None => 0,
158 }
159 }
160
161 #[inline]
163 #[must_use]
164 pub fn header(&self, key: &str) -> Option<&str> {
165 self.headers.get(key).map(AsRef::as_ref)
166 }
167
168 #[inline]
169 #[must_use]
170 pub fn headers(&self) -> &HashMap<Box<str>, Box<str>> {
171 &self.headers
172 }
173
174 #[inline]
175 pub fn set_header(&mut self, key: impl Into<Box<str>>, value: impl Into<Box<str>>) {
176 self.response_headers.insert(key.into(), value.into());
177 }
178
179 pub fn body(&mut self) -> Option<&[u8]> {
180 let len = self.content_length().max(32);
181 let mut buf = Vec::with_capacity(len);
182 if self.stream.read_to_end(&mut buf).is_ok() {
183 self.body = Some(buf.into_boxed_slice());
184 }
185 self.body.as_deref()
186 }
187 pub fn read_body(&mut self, writer: &mut dyn Write) -> Result<()> {
192 const CHUNK_SIZE: usize = 1024;
193 let mut buf: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
194 let len = self.content_length();
195 let n = len / CHUNK_SIZE;
196 let remainder = len % CHUNK_SIZE;
197
198 for _ in 0..n {
199 self.stream.read_exact(&mut buf)?;
200 writer.write_all(&buf)?;
201 }
202
203 if remainder > 0 {
204 self.stream.read_exact(&mut buf[0..remainder])?;
205 writer.write_all(&buf[0..remainder])?;
206 }
207
208 Ok(())
209 }
210 pub fn respond(&mut self) -> Result<()> {
215 let response_line = format!(
216 "HTTP/{} {} {}\r\n",
217 self.version,
218 self.status,
219 self.status_msg()
220 );
221 self.stream.get_mut().write_all(response_line.as_bytes())?;
222 let stream = self.stream.get_mut();
223 for (k, v) in &self.response_headers {
224 stream.write_all(k.as_bytes())?;
225 stream.write_all(b": ")?;
226 stream.write_all(v.as_bytes())?;
227 stream.write_all(b"\r\n")?;
228 }
229 stream.write_all(b"\r\n")?;
230 Ok(())
231 }
232 pub fn respond_buf(&mut self, mut buf: &[u8]) -> Result<()> {
237 self.set_header("Content-Length", buf.len().to_string());
238 self.respond_reader(&mut buf)
239 }
240 #[inline]
245 pub fn respond_str(&mut self, text: &str) -> Result<()> {
246 self.respond_buf(text.as_bytes())
247 }
248 pub fn respond_reader(&mut self, reader: &mut dyn Read) -> Result<()> {
253 const CHUNK_SIZE: usize = 1024;
254 let mut buf: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
255
256 self.respond()?;
257
258 let stream = self.stream.get_mut();
259 while let Ok(n) = reader.read(&mut buf) {
260 if n == 0 {
261 break;
262 }
263 stream.write_all(&buf[0..n])?;
264 }
265 Ok(())
266 }
267 pub fn respond_chunked(&mut self, reader: &mut dyn Read) -> Result<()> {
274 self.set_header("Transfer-Encoding", "chunked");
275 let mut reader = Chunked::new(reader);
276 self.respond_reader(&mut reader)
277 }
278 #[inline]
283 pub fn respond_error_page(&mut self) -> Result<()> {
284 self.set_header("Content-Type", "text/html");
285 self.respond_str(&self.error_page())
286 }
287 #[inline]
292 pub fn ok(&mut self) -> Result<()> {
293 self.set_status(200).respond()
294 }
295 #[inline]
300 pub fn forbidden(&mut self) -> Result<()> {
301 self.set_status(403).respond_error_page()
302 }
303 #[inline]
308 pub fn unauthorized(&mut self) -> Result<()> {
309 self.set_status(401).respond_error_page()
310 }
311 #[inline]
316 pub fn not_found(&mut self) -> Result<()> {
317 self.set_status(404).respond_error_page()
318 }
319 #[inline]
324 pub fn server_error(&mut self) -> Result<()> {
325 self.set_status(500).respond_error_page()
326 }
327 #[inline]
328 #[must_use]
329 pub fn is_http_ok(&self) -> bool {
330 self.status.is_http_ok()
331 }
332 #[inline]
333 #[must_use]
334 pub fn is_http_err(&self) -> bool {
335 self.status.is_http_err()
336 }
337 #[inline]
338 #[must_use]
339 pub fn status_msg(&self) -> &'static str {
340 self.status.status_msg()
341 }
342 #[must_use]
344 pub fn error_page(&self) -> String {
345 let code = self.status;
346 let msg = self.status_msg();
347 format!(
348 "<!DOCTYPE html>
349<html lang=\"en\">
350 <head>
351 <meta charset=\"utf-8\">
352 <title>{code} {msg}</title>
353 </head>
354<body>
355 <h1>{code} {msg}</h1>
356</body>
357</html>"
358 )
359 }
360}
361
362impl PartialEq for HttpRequest {
363 fn eq(&self, other: &Self) -> bool {
364 self.method == other.method
365 && self.url == other.url
366 && self.headers == other.headers
367 && self.params == other.params
368 && self.response_headers == other.response_headers
369 && self.version == other.version
370 && self.status == other.status
371 }
372}
373
374#[cfg(test)]
375mod test;