http/request/
mod.rs

1/* pub mod handler; */
2use builders::Builder;
3mod parse;
4use core::fmt;
5use std::{
6    collections::HashMap,
7    env,
8    ffi::OsStr,
9    io::{BufRead, BufReader, Read, Write},
10    path::Path,
11};
12
13use parse::parse_request;
14
15use crate::{
16    HttpMethod, HttpResponse, HttpStream, Result, StatusCode,
17    encoding::Chunked,
18    stream::{self, IntoHttpStream},
19};
20
21/// HTTP Request
22///
23/// Represents an HTTP request
24#[derive(Builder)]
25pub struct HttpRequest {
26    #[builder(def = { HttpMethod::GET })]
27    method: HttpMethod,
28    url: Box<str>,
29    #[builder(map = "header")]
30    headers: HashMap<Box<str>, Box<str>>,
31    #[builder(map = "param")]
32    params: HashMap<Box<str>, Box<str>>,
33    #[builder(map = "response_header")]
34    response_headers: HashMap<Box<str>, Box<str>>,
35    #[builder(def = 1.0)]
36    version: f32,
37    #[builder(def = { BufReader::new(stream::dummy())} )]
38    stream: BufReader<Box<dyn HttpStream>>,
39    #[builder(def = 200u16)]
40    status: u16,
41    #[builder(optional = true)]
42    body: Option<Box<[u8]>>,
43}
44
45impl fmt::Debug for HttpRequest {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        f.debug_struct("HttpRequest")
48            .field("method", &self.method)
49            .field("url", &self.url)
50            .field("headers", &self.headers)
51            .field("params", &self.params)
52            .field("response_headers", &self.response_headers)
53            .field("version", &self.version)
54            .field("status", &self.status)
55            .field("body", &self.body)
56            .finish()
57    }
58}
59
60impl HttpRequest {
61    /// Read and parse an HTTP request from the given [`HttpStream`]
62    pub fn parse<S: IntoHttpStream>(stream: S) -> Result<Self> {
63        let stream: Box<dyn HttpStream> = Box::new(stream.into_http_stream());
64        parse_request(BufReader::new(stream))
65    }
66
67    #[inline]
68    pub fn keep_alive(self) -> Result<Self> {
69        let mut req = parse_request(self.stream)?;
70        req.set_header("Connection", "keep-alive");
71        Ok(req)
72    }
73
74    #[inline]
75    pub fn stream(&self) -> &BufReader<Box<dyn HttpStream>> {
76        &self.stream
77    }
78
79    #[inline]
80    pub fn stream_mut(&mut self) -> &mut BufReader<Box<dyn HttpStream>> {
81        &mut self.stream
82    }
83
84    /// Url of the request
85    #[inline]
86    pub fn url(&self) -> &str {
87        &self.url
88    }
89
90    #[inline]
91    pub fn set_url(&mut self, url: impl Into<Box<str>>) {
92        self.url = url.into();
93    }
94    /// Get the query parameters
95    #[inline]
96    #[must_use]
97    pub fn params(&self) -> &HashMap<Box<str>, Box<str>> {
98        &self.params
99    }
100
101    #[inline]
102    #[must_use]
103    pub fn param(&self, key: &str) -> Option<&str> {
104        self.params.get(key).map(AsRef::as_ref)
105    }
106
107    /// Get the filename for the request
108    ///
109    /// It computes the path in the server corresponding to the
110    /// request's url.
111    ///
112    pub fn filename(&self) -> Result<Box<str>> {
113        let mut cwd = env::current_dir()?;
114        cwd.push(Path::new(OsStr::new(&self.url[1..])));
115        let cwd = cwd.to_str().ok_or("Error getting cwd")?;
116        Ok(Box::from(cwd))
117    }
118
119    /// Writes the request into the given [Write] object.
120    pub fn write_to(&self, f: &mut dyn Write) -> Result<()> {
121        write!(f, "{} {}", self.method(), self.url())?;
122        if !self.params().is_empty() {
123            write!(f, "?")?;
124            for (k, v) in self.params() {
125                let ke = url::encode(k).unwrap_or("".into());
126                let ve = url::encode(v).unwrap_or("".into());
127                write!(f, "{ke}={ve}&")?;
128            }
129        }
130        write!(f, " HTTP/{}\r\n", self.version())?;
131
132        for (k, v) in self.headers() {
133            write!(f, "{k}: {v}\r\n")?;
134        }
135
136        if let Some(ref b) = self.body {
137            f.write_all(b)?;
138        }
139
140        write!(f, "\r\n")?;
141
142        Ok(())
143    }
144
145    /// Sends the ``HttpRequest`` to a [stream](HttpStream)
146    ///
147    /// # Errors
148    /// If the transfer fails, returns the error
149    pub fn send_to<Out: IntoHttpStream>(&self, stream: Out) -> crate::Result<HttpResponse> {
150        let mut stream = stream.into_http_stream();
151        self.write_to(&mut stream)?;
152        stream.flush()?;
153        HttpResponse::parse(stream)
154    }
155    #[inline]
156    #[must_use]
157    pub fn method(&self) -> &HttpMethod {
158        &self.method
159    }
160
161    #[inline]
162    #[must_use]
163    pub fn status(&self) -> u16 {
164        self.status
165    }
166
167    #[inline]
168    pub fn set_status(&mut self, status: u16) -> &mut Self {
169        self.status = status;
170        self
171    }
172
173    #[inline]
174    #[must_use]
175    pub fn version(&self) -> f32 {
176        self.version
177    }
178
179    /// Get the value of the *Content-Length* HTTP header
180    ///
181    /// If the header is not present, or if it fails to parse
182    /// it's value, it returns 0
183    #[inline]
184    #[must_use]
185    pub fn content_length(&self) -> usize {
186        match self.headers.get("Content-Length") {
187            Some(l) => l.parse().unwrap_or(0),
188            None => 0,
189        }
190    }
191
192    /// Get the value of the given header key, if present
193    #[inline]
194    #[must_use]
195    pub fn header(&self, key: &str) -> Option<&str> {
196        self.headers.get(key).map(AsRef::as_ref)
197    }
198
199    #[inline]
200    #[must_use]
201    pub fn headers(&self) -> &HashMap<Box<str>, Box<str>> {
202        &self.headers
203    }
204
205    #[inline]
206    pub fn set_header(&mut self, key: impl Into<Box<str>>, value: impl Into<Box<str>>) {
207        self.response_headers.insert(key.into(), value.into());
208    }
209
210    /// Reads the body from the stream into the buffer.
211    ///
212    /// This method is primarly used by [`body`](Self::body), and
213    /// for unit tests, where we need to force-load the body into
214    /// the stream's buffer.
215    pub(crate) fn read_body_into_buffer(&mut self) -> Result<()> {
216        let len = self.content_length();
217        let mut buf = Vec::with_capacity(len);
218        self.stream.read_to_end(&mut buf)?;
219        self.body = Some(buf.into_boxed_slice());
220        Ok(())
221    }
222
223    /// Reads the body from the [`stream`] into the request's buffer.
224    ///
225    /// # NOTE
226    /// This loads the whole body of the request into memory,
227    /// and it'll stick with the request for it's lifetime.
228    /// It's not very efficient memory-wise for requests with big bodies.
229    ///
230    /// For a nicer way to process a request's body, see the
231    /// [read_body](Self::read_body) function.
232    ///
233    /// # Errors
234    /// If some IO error happens when reading the body from the [`stream`]
235    ///
236    /// # Returns
237    /// And option of &[u8]. A None variant means the request doesn't have a body.
238    /// For example, GET request don't usually have a body.
239    ///
240    /// [`stream`]: HttpStream
241    pub fn body(&mut self) -> Result<Option<&[u8]>> {
242        if self.body.is_none() {
243            self.read_body_into_buffer()?;
244        }
245        Ok(self.body.as_deref())
246    }
247
248    /// Returns true if the [`stream`](HttpStream) has a body,
249    /// and false if it's empty.
250    ///
251    /// This method is preferred to check the presence of a body,
252    /// over calling [body](Self::body) and checking the returned Option,
253    /// since this function doesn't allocate memory, nor mutates the request.
254    ///
255    /// # Errors
256    /// If some IO error happens in the process of checking the
257    /// [`stream`]'s availability
258    ///
259    /// [`stream`]: HttpStream
260    pub fn has_body(&mut self) -> Result<bool> {
261        Ok(self.body.is_some() || !self.stream.fill_buf()?.is_empty())
262    }
263
264    /// Reads the request body into [writer](Write)
265    ///
266    /// # Errors
267    /// If, while reading or writing, some io Error is found
268    pub fn read_body(&mut self, out: &mut dyn Write) -> Result<usize> {
269        let mut total = 0;
270        loop {
271            let slice = self.stream.fill_buf()?;
272            if slice.is_empty() {
273                break;
274            }
275            out.write_all(slice)?;
276
277            let len = slice.len();
278            self.stream.consume(len);
279            total += len;
280        }
281        out.flush()?;
282        Ok(total)
283    }
284    /// Respond to the request without a body
285    ///
286    /// # Errors
287    /// If some io error is produced while sending the request
288    pub fn respond(&mut self) -> Result<()> {
289        let response_line = format!(
290            "HTTP/{} {} {}\r\n",
291            self.version,
292            self.status,
293            self.status_msg()
294        );
295        self.stream.get_mut().write_all(response_line.as_bytes())?;
296        let stream = self.stream.get_mut();
297        for (k, v) in &self.response_headers {
298            stream.write_all(k.as_bytes())?;
299            stream.write_all(b": ")?;
300            stream.write_all(v.as_bytes())?;
301            stream.write_all(b"\r\n")?;
302        }
303        stream.write_all(b"\r\n")?;
304        Ok(())
305    }
306    /// Respond to the request with the data of buf as a body
307    ///
308    /// # Errors
309    /// If some io error is produced while sending the request
310    pub fn respond_buf(&mut self, mut buf: &[u8]) -> Result<()> {
311        self.set_header("Content-Length", buf.len().to_string());
312        self.respond_reader(&mut buf)
313    }
314    /// Respond to the request with the given string
315    ///
316    /// # Errors
317    /// If some io error is produced while sending the request
318    #[inline]
319    pub fn respond_str(&mut self, text: &str) -> Result<()> {
320        self.respond_buf(text.as_bytes())
321    }
322    /// Respond to the request with the data read from reader as a body
323    ///
324    /// # Errors
325    /// If some io error is produced while sending the request
326    pub fn respond_reader(&mut self, reader: &mut dyn Read) -> Result<()> {
327        const CHUNK_SIZE: usize = 1024;
328        let mut buf: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
329
330        self.respond()?;
331
332        let stream = self.stream.get_mut();
333        while let Ok(n) = reader.read(&mut buf) {
334            if n == 0 {
335                break;
336            }
337            stream.write_all(&buf[0..n])?;
338        }
339        Ok(())
340    }
341    /// Respond to the request as a chunked transfer
342    ///
343    /// This means that the Content-Length of the request doen't need to be known.
344    ///
345    /// # Errors
346    /// If some io error is produced while sending the request
347    pub fn respond_chunked(&mut self, reader: &mut dyn Read) -> Result<()> {
348        self.set_header("Transfer-Encoding", "chunked");
349        let mut reader = Chunked::with_default_size(reader);
350        self.respond_reader(&mut reader)
351    }
352    /// Respond with a basic HTML error page
353    ///
354    /// # Errors
355    /// If some io error is produced while sending the request
356    #[inline]
357    pub fn respond_error_page(&mut self) -> Result<()> {
358        self.set_header("Content-Type", "text/html");
359        self.respond_str(&self.error_page())
360    }
361    /// Respond to the request with an 200 OK status
362    ///
363    /// # Errors
364    /// If some io error is produced while sending the request
365    #[inline]
366    pub fn ok(&mut self) -> Result<()> {
367        self.set_status(200).respond()
368    }
369    /// Respond to the request with an 403 FORBIDDEN status
370    ///
371    /// # Errors
372    /// If some io error is produced while sending the request
373    #[inline]
374    pub fn forbidden(&mut self) -> Result<()> {
375        self.set_status(403).respond_error_page()
376    }
377    /// Respond to the request with an 401 UNAUTHORIZED status
378    ///
379    /// # Errors
380    /// If some io error is produced while sending the request
381    #[inline]
382    pub fn unauthorized(&mut self) -> Result<()> {
383        self.set_status(401).respond_error_page()
384    }
385    /// Respond to the request with an 404 NOT FOUND status
386    ///
387    /// # Errors
388    /// If some io error is produced while sending the request
389    #[inline]
390    pub fn not_found(&mut self) -> Result<()> {
391        self.set_status(404).respond_error_page()
392    }
393    /// Respond to the request with an 500 INTERNAL SERVER ERROR status
394    ///
395    /// # Errors
396    /// If some io error is produced while sending the request
397    #[inline]
398    pub fn server_error(&mut self) -> Result<()> {
399        self.set_status(500).respond_error_page()
400    }
401    #[inline]
402    #[must_use]
403    pub fn is_http_ok(&self) -> bool {
404        self.status.is_http_ok()
405    }
406    #[inline]
407    #[must_use]
408    pub fn is_http_err(&self) -> bool {
409        self.status.is_http_err()
410    }
411    #[inline]
412    #[must_use]
413    pub fn status_msg(&self) -> &'static str {
414        self.status.status_msg()
415    }
416    /// Returns a basic HTML error page of the given status
417    #[must_use]
418    pub fn error_page(&self) -> String {
419        let code = self.status;
420        let msg = self.status_msg();
421        format!(
422            "<!DOCTYPE html>
423<html lang=\"en\">
424    <head>
425        <meta charset=\"utf-8\">
426        <title>{code} {msg}</title>
427    </head>
428<body>
429    <h1>{code} {msg}</h1>
430</body>
431</html>"
432        )
433    }
434}
435
436impl PartialEq for HttpRequest {
437    fn eq(&self, other: &Self) -> bool {
438        self.method == other.method
439            && self.url == other.url
440            && self.headers == other.headers
441            && self.params == other.params
442            && self.response_headers == other.response_headers
443            && self.version == other.version
444            && self.status == other.status
445            && self.body == other.body
446    }
447}
448
449#[cfg(test)]
450mod test;