stream_httparse/
response.rs

1use crate::{
2    header::{HeaderKey, HeaderValue},
3    Headers, StatusCode,
4};
5
6#[derive(Debug)]
7enum ProtocolData<'a> {
8    Ref(&'a str),
9    Owned(String),
10}
11
12impl<'a> AsRef<str> for ProtocolData<'a> {
13    fn as_ref(&self) -> &str {
14        match self {
15            Self::Ref(tmp) => tmp,
16            Self::Owned(tmp) => &tmp,
17        }
18    }
19}
20
21impl<'a> PartialEq for ProtocolData<'a> {
22    fn eq(&self, other: &ProtocolData<'_>) -> bool {
23        self.as_ref().eq(other.as_ref())
24    }
25}
26
27/// Represents a single HTTP-Request
28#[derive(Debug, PartialEq)]
29pub struct Response<'a> {
30    status_code: StatusCode,
31    protocol: ProtocolData<'a>,
32    headers: Headers<'a>,
33    body: Vec<u8>,
34}
35
36impl<'a> Response<'a> {
37    /// Creates a new Response with the given
38    /// Data as its inital State
39    pub fn new(
40        protocol: &'a str,
41        status_code: StatusCode,
42        headers: Headers<'a>,
43        body: Vec<u8>,
44    ) -> Self {
45        Self {
46            status_code,
47            protocol: ProtocolData::Ref(protocol),
48            headers,
49            body,
50        }
51    }
52
53    /// Creates a new Response that owns all of
54    /// its Data
55    pub(crate) fn new_owned(
56        protocol: String,
57        status_code: StatusCode,
58        headers: Headers<'a>,
59        body: Vec<u8>,
60    ) -> Self {
61        Self {
62            status_code,
63            protocol: ProtocolData::Owned(protocol),
64            headers,
65            body,
66        }
67    }
68
69    /// Serialzes the Response and returns the Data as
70    /// a tuple of form (HTTP-Head, HTTP-Body)
71    pub fn serialize(&self) -> (Vec<u8>, &[u8]) {
72        let protocol = self.protocol.as_ref();
73        let status_code = self.status_code.serialize();
74
75        let capacity = protocol.len() + 1 + status_code.len() + 4;
76        let mut result = Vec::with_capacity(capacity);
77
78        // The first line with method, path, protocol
79        result.extend_from_slice(protocol.as_bytes());
80        result.push(b' ');
81        result.extend_from_slice(status_code.as_bytes());
82        result.extend_from_slice("\r\n".as_bytes());
83
84        // The headers
85        self.headers.serialize(&mut result);
86
87        // The ending of the head
88        result.extend_from_slice("\r\n".as_bytes());
89
90        (result, &self.body)
91    }
92
93    /// Returns the Protocol of the Response
94    pub fn protocol(&self) -> &str {
95        self.protocol.as_ref()
96    }
97    /// Returns the StatusCode of the Response
98    pub fn status_code(&self) -> &StatusCode {
99        &self.status_code
100    }
101    /// Returns the Headers of the Response
102    pub fn headers(&self) -> &Headers<'a> {
103        &self.headers
104    }
105    /// Returns the Body of the Response
106    pub fn body(&self) -> &[u8] {
107        &self.body
108    }
109
110    /// Adds the Key-Value Pair as a new Header to
111    /// the Response or replaces the old Value of the
112    /// Header if it already existed on the Response
113    pub fn add_header<'b, K, V>(&mut self, key: K, value: V)
114    where
115        'b: 'a,
116        K: Into<HeaderKey<'a>>,
117        V: Into<HeaderValue<'a>>,
118    {
119        self.headers.set(key, value);
120    }
121
122    /// Replaces the old Body of the Response with the
123    /// new given Body and updates the Content-Length
124    /// Header as well with the new Length
125    pub fn set_body(&mut self, n_body: Vec<u8>) {
126        self.body = n_body;
127        self.add_header("Content-Length", self.body.len());
128    }
129
130    /// Checks if the Response is send using
131    /// `Transfer-Encoding: Chunked`
132    pub fn is_chunked(&self) -> bool {
133        match self.headers.get("Transfer-Encoding") {
134            None => false,
135            Some(value) => value.eq_ignore_case(&HeaderValue::StrRef("Chunked")),
136        }
137    }
138
139    /// Clones the entire Response to produce a new indepandent
140    /// Response
141    pub fn to_owned<'refed, 'owned>(&'refed self) -> Response<'owned> {
142        Response::new_owned(
143            self.protocol.as_ref().to_owned(),
144            self.status_code.clone(),
145            self.headers.to_owned(),
146            self.body.clone(),
147        )
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn serialize_valid() {
157        let mut headers = Headers::new();
158        headers.set("test-1", "value-1");
159
160        let req = Response::new(
161            "HTTP/1.1",
162            StatusCode::OK,
163            headers,
164            "body".as_bytes().to_vec(),
165        );
166        let raw_resp_header = "HTTP/1.1 200 OK\r\ntest-1: value-1\r\n\r\n";
167        let resp_header = raw_resp_header.as_bytes().to_vec();
168        let resp_body = "body".as_bytes();
169
170        assert_eq!(req.serialize(), (resp_header, resp_body));
171    }
172
173    #[test]
174    fn serialize_valid_no_body() {
175        let mut headers = Headers::new();
176        headers.set("test-1", "value-1");
177
178        let req = Response::new("HTTP/1.1", StatusCode::OK, headers, "".as_bytes().to_vec());
179        let raw_resp_header = "HTTP/1.1 200 OK\r\ntest-1: value-1\r\n\r\n";
180        let resp_header = raw_resp_header.as_bytes().to_vec();
181        let resp_body = "".as_bytes();
182
183        assert_eq!(req.serialize(), (resp_header, resp_body));
184    }
185
186    #[test]
187    fn is_chunked_not_set() {
188        let mut headers = Headers::new();
189        headers.set("test-1", "value-1");
190
191        let resp = Response::new("HTTP/1.1", StatusCode::OK, headers, "".as_bytes().to_vec());
192
193        assert_eq!(false, resp.is_chunked());
194    }
195    #[test]
196    fn is_chunked_set() {
197        let mut headers = Headers::new();
198        headers.set("Transfer-Encoding", "Chunked");
199
200        let resp = Response::new("HTTP/1.1", StatusCode::OK, headers, "".as_bytes().to_vec());
201
202        assert_eq!(true, resp.is_chunked());
203    }
204    #[test]
205    fn is_chunked_set_differently() {
206        let mut headers = Headers::new();
207        headers.set("Transfer-Encoding", "compress");
208
209        let resp = Response::new("HTTP/1.1", StatusCode::OK, headers, "".as_bytes().to_vec());
210
211        assert_eq!(false, resp.is_chunked());
212    }
213
214    #[test]
215    fn to_owned() {
216        let resp = Response::new("HTTP/1.1", StatusCode::OK, Headers::new(), Vec::new());
217
218        let cloned = resp.to_owned();
219
220        drop(resp);
221
222        assert_eq!(&StatusCode::OK, cloned.status_code())
223    }
224}