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::{HttpMethod, HttpResponse, HttpStream, Result, StatusCode, encoding::Chunked};
15
16#[derive(Builder, Debug)]
20pub struct HttpRequest {
21 method: HttpMethod,
22 url: Box<str>,
23 #[builder(map = "header")]
24 headers: HashMap<Box<str>, Box<str>>,
25 #[builder(map = "param")]
26 params: HashMap<Box<str>, Box<str>>,
27 #[builder(map = "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
81 pub fn filename(&self) -> Result<Box<str>> {
87 let mut cwd = env::current_dir()?;
88 cwd.push(Path::new(OsStr::new(&self.url[1..])));
89 let cwd = cwd.to_str().ok_or("Error getting cwd")?;
90 Ok(Box::from(cwd))
91 }
92
93 pub fn write_to(&self, f: &mut dyn Write) -> Result<()> {
95 write!(f, "{} {}", self.method(), self.url())?;
96 if !self.params().is_empty() {
97 write!(f, "?")?;
98 for (k, v) in self.params() {
99 let ke = url::encode(k).unwrap_or("".into());
100 let ve = url::encode(v).unwrap_or("".into());
101 write!(f, "{ke}={ve}&")?;
102 }
103 }
104 write!(f, " HTTP/{}\r\n", self.version())?;
105
106 for (k, v) in self.headers() {
107 write!(f, "{k}: {v}\r\n")?;
108 }
109
110 if let Some(ref b) = self.body {
111 f.write_all(b)?;
112 }
113
114 write!(f, "\r\n")?;
115
116 Ok(())
117 }
118
119 pub fn send_to(&self, mut stream: HttpStream) -> crate::Result<HttpResponse> {
124 self.write_to(&mut stream)?;
125 stream.flush()?;
126 HttpResponse::parse(stream)
127 }
128 #[inline]
129 #[must_use]
130 pub fn method(&self) -> &HttpMethod {
131 &self.method
132 }
133
134 #[inline]
135 #[must_use]
136 pub fn status(&self) -> u16 {
137 self.status
138 }
139
140 #[inline]
141 pub fn set_status(&mut self, status: u16) -> &mut Self {
142 self.status = status;
143 self
144 }
145
146 #[inline]
147 #[must_use]
148 pub fn version(&self) -> f32 {
149 self.version
150 }
151
152 #[inline]
157 #[must_use]
158 pub fn content_length(&self) -> usize {
159 match self.headers.get("Content-Length") {
160 Some(l) => l.parse().unwrap_or(0),
161 None => 0,
162 }
163 }
164
165 #[inline]
167 #[must_use]
168 pub fn header(&self, key: &str) -> Option<&str> {
169 self.headers.get(key).map(AsRef::as_ref)
170 }
171
172 #[inline]
173 #[must_use]
174 pub fn headers(&self) -> &HashMap<Box<str>, Box<str>> {
175 &self.headers
176 }
177
178 #[inline]
179 pub fn set_header(&mut self, key: impl Into<Box<str>>, value: impl Into<Box<str>>) {
180 self.response_headers.insert(key.into(), value.into());
181 }
182
183 pub(crate) fn read_body_into_buffer(&mut self) -> Result<()> {
189 let len = self.content_length();
190 let mut buf = Vec::with_capacity(len);
191 self.stream.read_to_end(&mut buf)?;
192 self.body = Some(buf.into_boxed_slice());
193 Ok(())
194 }
195
196 pub fn body(&mut self) -> Result<Option<&[u8]>> {
215 if self.body.is_none() {
216 self.read_body_into_buffer()?;
217 }
218 Ok(self.body.as_deref())
219 }
220
221 pub fn has_body(&self) -> Result<bool> {
234 Ok(self.stream.get_ref().is_ready()?)
235 }
236
237 pub fn read_body(&mut self, writer: &mut dyn Write) -> Result<()> {
242 const CHUNK_SIZE: usize = 1024;
243 let mut buf: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
244 let len = self.content_length();
245 let n = len / CHUNK_SIZE;
246 let remainder = len % CHUNK_SIZE;
247
248 for _ in 0..n {
249 self.stream.read_exact(&mut buf)?;
250 writer.write_all(&buf)?;
251 }
252
253 if remainder > 0 {
254 self.stream.read_exact(&mut buf[0..remainder])?;
255 writer.write_all(&buf[0..remainder])?;
256 }
257
258 Ok(())
259 }
260 pub fn respond(&mut self) -> Result<()> {
265 let response_line = format!(
266 "HTTP/{} {} {}\r\n",
267 self.version,
268 self.status,
269 self.status_msg()
270 );
271 self.stream.get_mut().write_all(response_line.as_bytes())?;
272 let stream = self.stream.get_mut();
273 for (k, v) in &self.response_headers {
274 stream.write_all(k.as_bytes())?;
275 stream.write_all(b": ")?;
276 stream.write_all(v.as_bytes())?;
277 stream.write_all(b"\r\n")?;
278 }
279 stream.write_all(b"\r\n")?;
280 Ok(())
281 }
282 pub fn respond_buf(&mut self, mut buf: &[u8]) -> Result<()> {
287 self.set_header("Content-Length", buf.len().to_string());
288 self.respond_reader(&mut buf)
289 }
290 #[inline]
295 pub fn respond_str(&mut self, text: &str) -> Result<()> {
296 self.respond_buf(text.as_bytes())
297 }
298 pub fn respond_reader(&mut self, reader: &mut dyn Read) -> Result<()> {
303 const CHUNK_SIZE: usize = 1024;
304 let mut buf: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
305
306 self.respond()?;
307
308 let stream = self.stream.get_mut();
309 while let Ok(n) = reader.read(&mut buf) {
310 if n == 0 {
311 break;
312 }
313 stream.write_all(&buf[0..n])?;
314 }
315 Ok(())
316 }
317 pub fn respond_chunked(&mut self, reader: &mut dyn Read) -> Result<()> {
324 self.set_header("Transfer-Encoding", "chunked");
325 let mut reader = Chunked::new(reader);
326 self.respond_reader(&mut reader)
327 }
328 #[inline]
333 pub fn respond_error_page(&mut self) -> Result<()> {
334 self.set_header("Content-Type", "text/html");
335 self.respond_str(&self.error_page())
336 }
337 #[inline]
342 pub fn ok(&mut self) -> Result<()> {
343 self.set_status(200).respond()
344 }
345 #[inline]
350 pub fn forbidden(&mut self) -> Result<()> {
351 self.set_status(403).respond_error_page()
352 }
353 #[inline]
358 pub fn unauthorized(&mut self) -> Result<()> {
359 self.set_status(401).respond_error_page()
360 }
361 #[inline]
366 pub fn not_found(&mut self) -> Result<()> {
367 self.set_status(404).respond_error_page()
368 }
369 #[inline]
374 pub fn server_error(&mut self) -> Result<()> {
375 self.set_status(500).respond_error_page()
376 }
377 #[inline]
378 #[must_use]
379 pub fn is_http_ok(&self) -> bool {
380 self.status.is_http_ok()
381 }
382 #[inline]
383 #[must_use]
384 pub fn is_http_err(&self) -> bool {
385 self.status.is_http_err()
386 }
387 #[inline]
388 #[must_use]
389 pub fn status_msg(&self) -> &'static str {
390 self.status.status_msg()
391 }
392 #[must_use]
394 pub fn error_page(&self) -> String {
395 let code = self.status;
396 let msg = self.status_msg();
397 format!(
398 "<!DOCTYPE html>
399<html lang=\"en\">
400 <head>
401 <meta charset=\"utf-8\">
402 <title>{code} {msg}</title>
403 </head>
404<body>
405 <h1>{code} {msg}</h1>
406</body>
407</html>"
408 )
409 }
410}
411
412impl PartialEq for HttpRequest {
413 fn eq(&self, other: &Self) -> bool {
414 self.method == other.method
415 && self.url == other.url
416 && self.headers == other.headers
417 && self.params == other.params
418 && self.response_headers == other.response_headers
419 && self.version == other.version
420 && self.status == other.status
421 && self.body == other.body
422 }
423}
424
425#[cfg(test)]
426mod test;