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::{HttpMethod, HttpResponse, HttpStream, Result, StatusCode, encoding::Chunked};
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(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    /// 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
81    /// Get the filename for the request
82    ///
83    /// It computes the path in the server corresponding to the
84    /// request's url.
85    ///
86    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    /// Writes the request into the given [Write] object.
94    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    /// Sends the ``HttpRequest`` to a [stream](HttpStream)
120    ///
121    /// # Errors
122    /// If the transfer fails, returns the error
123    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    /// Get the value of the *Content-Length* HTTP header
153    ///
154    /// If the header is not present, or if it fails to parse
155    /// it's value, it returns 0
156    #[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    /// Get the value of the given header key, if present
166    #[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    /// Reads the body from the stream into the buffer.
184    ///
185    /// This method is primarly used by [`body`](Self::body), and
186    /// for unit tests, where we need to force-load the body into
187    /// the stream's buffer.
188    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    /// Reads the body from the [`stream`] into the request's buffer.
197    ///
198    /// # NOTE
199    /// This loads the whole body of the request into memory,
200    /// and it'll stick with the request for it's lifetime.
201    /// It's not very efficient memory-wise for requests with big bodies.
202    ///
203    /// For a nicer way to process a request's body, see the
204    /// [read_body](Self::read_body) function.
205    ///
206    /// # Errors
207    /// If some IO error happens when reading the body from the [`stream`]
208    ///
209    /// # Returns
210    /// And option of &[u8]. A None variant means the request doesn't have a body.
211    /// For example, GET request don't usually have a body.
212    ///
213    /// [`stream`]: HttpStream
214    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    /// Returns true if the [`stream`](HttpStream) has a body,
222    /// and false if it's empty.
223    ///
224    /// This method is preferred to check the presence of a body,
225    /// over calling [body](Self::body) and checking the returned Option,
226    /// since this function doesn't allocate memory, nor mutates the request.
227    ///
228    /// # Errors
229    /// If some IO error happens in the process of checking the
230    /// [`stream`]'s availability
231    ///
232    /// [`stream`]: HttpStream
233    pub fn has_body(&self) -> Result<bool> {
234        Ok(self.stream.get_ref().is_ready()?)
235    }
236
237    /// Reads the request body into [writer](Write)
238    ///
239    /// # Errors
240    /// If, while reading or writing, some io Error is found
241    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    /// Respond to the request without a body
261    ///
262    /// # Errors
263    /// If some io error is produced while sending the request
264    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    /// Respond to the request with the data of buf as a body
283    ///
284    /// # Errors
285    /// If some io error is produced while sending the request
286    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    /// Respond to the request with the given string
291    ///
292    /// # Errors
293    /// If some io error is produced while sending the request
294    #[inline]
295    pub fn respond_str(&mut self, text: &str) -> Result<()> {
296        self.respond_buf(text.as_bytes())
297    }
298    /// Respond to the request with the data read from reader as a body
299    ///
300    /// # Errors
301    /// If some io error is produced while sending the request
302    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    /// Respond to the request as a chunked transfer
318    ///
319    /// This means that the Content-Length of the request doen't need to be known.
320    ///
321    /// # Errors
322    /// If some io error is produced while sending the request
323    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    /// Respond with a basic HTML error page
329    ///
330    /// # Errors
331    /// If some io error is produced while sending the request
332    #[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    /// Respond to the request with an 200 OK status
338    ///
339    /// # Errors
340    /// If some io error is produced while sending the request
341    #[inline]
342    pub fn ok(&mut self) -> Result<()> {
343        self.set_status(200).respond()
344    }
345    /// Respond to the request with an 403 FORBIDDEN status
346    ///
347    /// # Errors
348    /// If some io error is produced while sending the request
349    #[inline]
350    pub fn forbidden(&mut self) -> Result<()> {
351        self.set_status(403).respond_error_page()
352    }
353    /// Respond to the request with an 401 UNAUTHORIZED status
354    ///
355    /// # Errors
356    /// If some io error is produced while sending the request
357    #[inline]
358    pub fn unauthorized(&mut self) -> Result<()> {
359        self.set_status(401).respond_error_page()
360    }
361    /// Respond to the request with an 404 NOT FOUND status
362    ///
363    /// # Errors
364    /// If some io error is produced while sending the request
365    #[inline]
366    pub fn not_found(&mut self) -> Result<()> {
367        self.set_status(404).respond_error_page()
368    }
369    /// Respond to the request with an 500 INTERNAL SERVER ERROR status
370    ///
371    /// # Errors
372    /// If some io error is produced while sending the request
373    #[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    /// Returns a basic HTML error page of the given status
393    #[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;