http/request/
mod.rs

1/* pub mod handler; */
2use 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/// HTTP Request
17///
18/// Represents an HTTP request
19#[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    /// Read and parse an HTTP request from the given [`HttpStream`]
42    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    /// Url of the request
58    #[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    /// Get the query parameters
69    #[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    /// Get the filename for the request
81    ///
82    /// It computes the path in the server corresponding to the
83    /// request's url.
84    ///
85    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    /// Sends the ``HttpRequest`` to a [stream](HttpStream)
116    ///
117    /// # Errors
118    /// If the transfer fails, returns the error
119    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    /// Get the value of the *Content-Length* HTTP header
149    ///
150    /// If the header is not present, or if it fails to parse
151    /// it's value, it returns 0
152    #[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    /// Get the value of the given header key, if present
162    #[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    /// Reads the request body into [writer](Write)
188    ///
189    /// # Errors
190    /// If, while reading of writing, some io Error is found
191    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    /// Respond to the request without a body
211    ///
212    /// # Errors
213    /// If some io error is produced while sending the request
214    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    /// Respond to the request with the data of buf as a body
233    ///
234    /// # Errors
235    /// If some io error is produced while sending the request
236    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    /// Respond to the request with the given string
241    ///
242    /// # Errors
243    /// If some io error is produced while sending the request
244    #[inline]
245    pub fn respond_str(&mut self, text: &str) -> Result<()> {
246        self.respond_buf(text.as_bytes())
247    }
248    /// Respond to the request with the data read from reader as a body
249    ///
250    /// # Errors
251    /// If some io error is produced while sending the request
252    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    /// Respond to the request as a chunked transfer
268    ///
269    /// This means that the Content-Length of the request doen't need to be known.
270    ///
271    /// # Errors
272    /// If some io error is produced while sending the request
273    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    /// Respond with a basic HTML error page
279    ///
280    /// # Errors
281    /// If some io error is produced while sending the request
282    #[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    /// Respond to the request with an 200 OK status
288    ///
289    /// # Errors
290    /// If some io error is produced while sending the request
291    #[inline]
292    pub fn ok(&mut self) -> Result<()> {
293        self.set_status(200).respond()
294    }
295    /// Respond to the request with an 403 FORBIDDEN status
296    ///
297    /// # Errors
298    /// If some io error is produced while sending the request
299    #[inline]
300    pub fn forbidden(&mut self) -> Result<()> {
301        self.set_status(403).respond_error_page()
302    }
303    /// Respond to the request with an 401 UNAUTHORIZED status
304    ///
305    /// # Errors
306    /// If some io error is produced while sending the request
307    #[inline]
308    pub fn unauthorized(&mut self) -> Result<()> {
309        self.set_status(401).respond_error_page()
310    }
311    /// Respond to the request with an 404 NOT FOUND status
312    ///
313    /// # Errors
314    /// If some io error is produced while sending the request
315    #[inline]
316    pub fn not_found(&mut self) -> Result<()> {
317        self.set_status(404).respond_error_page()
318    }
319    /// Respond to the request with an 500 INTERNAL SERVER ERROR status
320    ///
321    /// # Errors
322    /// If some io error is produced while sending the request
323    #[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    /// Returns a basic HTML error page of the given status
343    #[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;