Skip to main content

http/request/
mod.rs

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