stream_httparse/
request.rs

1use crate::{general::StringContainer, header::HeaderValue, Headers, Method};
2
3#[derive(Debug)]
4enum BodyData<'a> {
5    Ref(&'a [u8]),
6    Owned(Vec<u8>),
7}
8
9impl<'a> AsRef<[u8]> for BodyData<'a> {
10    fn as_ref(&self) -> &[u8] {
11        match self {
12            Self::Ref(tmp) => tmp,
13            Self::Owned(tmp) => tmp.as_ref(),
14        }
15    }
16}
17
18impl<'a> PartialEq for BodyData<'a> {
19    fn eq(&self, other: &Self) -> bool {
20        self.as_ref().eq(other.as_ref())
21    }
22}
23
24/// Represents a single HTTP-Request
25#[derive(Debug, PartialEq)]
26pub struct Request<'a> {
27    method: Method,
28    path: StringContainer<'a>,
29    protocol: &'a str,
30    headers: Headers<'a>,
31    body: BodyData<'a>,
32}
33
34impl<'a> Request<'a> {
35    /// Creates a new Request with the given Data as its
36    /// initial Data
37    pub fn new(
38        protocol: &'a str,
39        method: Method,
40        path: &'a str,
41        headers: Headers<'a>,
42        body: &'a [u8],
43    ) -> Self {
44        Self {
45            method,
46            path: StringContainer::Ref(path),
47            protocol,
48            headers,
49            body: BodyData::Ref(body),
50        }
51    }
52
53    /// Serializes the Request and returns the final Data
54    /// as a tuple of (HTTP-Head, HTTP-Body)
55    pub fn serialize(&self) -> (Vec<u8>, &[u8]) {
56        let method = self.method.serialize();
57        let path = self.path.as_ref();
58        let capacity = method.len() + 1 + path.len() + 1 + self.protocol.len() + 4;
59        let mut result = Vec::with_capacity(capacity);
60
61        // The first line with method, path, protocol
62        result.extend_from_slice(method.as_bytes());
63        result.push(b' ');
64        result.extend_from_slice(path.as_bytes());
65        result.push(b' ');
66        result.extend_from_slice(self.protocol.as_bytes());
67        result.extend_from_slice("\r\n".as_bytes());
68
69        // The headers
70        self.headers.serialize(&mut result);
71
72        // The ending of the head
73        result.extend_from_slice("\r\n".as_bytes());
74
75        (result, self.body.as_ref())
76    }
77
78    /// Returns the Protocol of the Request
79    pub fn protocol(&self) -> &'a str {
80        &self.protocol
81    }
82    /// Returns the Method of the Request
83    pub fn method(&self) -> &Method {
84        &self.method
85    }
86    /// Returns the Path of the Request
87    pub fn path(&'a self) -> &'a str {
88        self.path.as_ref()
89    }
90    /// Returns the Headers of the Request
91    pub fn headers(&self) -> &Headers<'a> {
92        &self.headers
93    }
94    /// Returns a mutable Reference to the Headers of the Request
95    pub fn header_mut(&mut self) -> &mut Headers<'a> {
96        &mut self.headers
97    }
98    /// Returns the Body of the Request
99    pub fn body(&self) -> &[u8] {
100        self.body.as_ref()
101    }
102
103    /// Checks if the Requests expects a
104    /// Keep-alive connection
105    pub fn is_keep_alive(&self) -> bool {
106        match self.headers.get("Connection") {
107            None => false,
108            Some(value) => value.eq_ignore_case(&HeaderValue::StrRef("Keep-Alive")),
109        }
110    }
111
112    /// Overwrites the Path with the new Path
113    pub fn set_path_ref<'b>(&mut self, n_path: &'b str)
114    where
115        'b: 'a,
116    {
117        self.path = StringContainer::Ref(n_path);
118    }
119    /// Overwrites the Path with the new Path, but using
120    /// an owned String instead of a reference
121    pub fn set_path_owned(&mut self, n_path: String) {
122        self.path = StringContainer::Owned(n_path);
123    }
124
125    /// Replaces the current Body with the given Data
126    pub fn set_body(&mut self, data: Vec<u8>) {
127        self.body = BodyData::Owned(data);
128    }
129}
130
131impl std::fmt::Display for Request<'_> {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        write!(f, "[{}] Path: '{}'", self.method, self.path.as_ref())
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn update_path_to_own() {
143        let mut req = Request::new("HTTP/1.1", Method::GET, "/test/path", Headers::new(), &[]);
144
145        let path = req.path().to_owned();
146        req.set_path_ref(&path[1..]);
147
148        assert_eq!("test/path", req.path());
149    }
150
151    #[test]
152    fn serialize_valid() {
153        let mut headers = Headers::new();
154        headers.set("test-1", "value-1");
155
156        let req = Request::new("HTTP/1.1", Method::GET, "/test", headers, "body".as_bytes());
157        let raw_header = "GET /test HTTP/1.1\r\ntest-1: value-1\r\n\r\n";
158        let header_resp = raw_header.as_bytes().to_vec();
159        let body_resp = "body".as_bytes();
160
161        assert_eq!(req.serialize(), (header_resp, body_resp));
162    }
163    #[test]
164    fn serialize_valid_no_body() {
165        let mut headers = Headers::new();
166        headers.set("test-1", "value-1");
167
168        let req = Request::new("HTTP/1.1", Method::GET, "/test", headers, "".as_bytes());
169        let raw_header = "GET /test HTTP/1.1\r\ntest-1: value-1\r\n\r\n";
170        let resp_header = raw_header.as_bytes().to_vec();
171        let resp_body = "".as_bytes();
172
173        assert_eq!(req.serialize(), (resp_header, resp_body));
174    }
175
176    #[test]
177    fn is_keep_alive_not_set() {
178        let mut headers = Headers::new();
179        headers.set("test-1", "value-1");
180
181        let req = Request::new("HTTP/1.1", Method::GET, "/test", headers, "".as_bytes());
182
183        assert_eq!(false, req.is_keep_alive());
184    }
185    #[test]
186    fn is_keep_alive_is_set() {
187        let mut headers = Headers::new();
188        headers.set("Connection", "Keep-Alive");
189
190        let req = Request::new("HTTP/1.1", Method::GET, "/test", headers, "".as_bytes());
191
192        assert_eq!(true, req.is_keep_alive());
193    }
194    #[test]
195    fn is_keep_alive_is_set_to_off() {
196        let mut headers = Headers::new();
197        headers.set("Connection", "Close");
198
199        let req = Request::new("HTTP/1.1", Method::GET, "/test", headers, "".as_bytes());
200
201        assert_eq!(false, req.is_keep_alive());
202    }
203}