Skip to main content

actix_http/test/
test_request.rs

1use std::str::FromStr;
2
3use bytes::Bytes;
4use http::{header, Method, Uri, Version};
5
6use crate::{
7    header::{HeaderMap, TryIntoHeaderPair},
8    payload::Payload,
9    Request,
10};
11
12/// Test `Request` builder.
13pub struct TestRequest(Option<Inner>);
14
15struct Inner {
16    version: Version,
17    method: Method,
18    uri: Uri,
19    headers: HeaderMap,
20    payload: Option<Payload>,
21}
22
23impl Default for TestRequest {
24    fn default() -> TestRequest {
25        TestRequest(Some(Inner {
26            method: Method::GET,
27            uri: Uri::from_str("/").unwrap(),
28            version: Version::HTTP_11,
29            headers: HeaderMap::new(),
30            payload: None,
31        }))
32    }
33}
34
35impl TestRequest {
36    /// Create a default TestRequest and then set its URI.
37    pub fn with_uri(path: &str) -> TestRequest {
38        TestRequest::default().uri(path).take()
39    }
40
41    /// Set HTTP version of this request.
42    pub fn version(&mut self, ver: Version) -> &mut Self {
43        parts(&mut self.0).version = ver;
44        self
45    }
46
47    /// Set HTTP method of this request.
48    pub fn method(&mut self, meth: Method) -> &mut Self {
49        parts(&mut self.0).method = meth;
50        self
51    }
52
53    /// Set URI of this request.
54    ///
55    /// # Panics
56    /// If provided URI is invalid.
57    pub fn uri(&mut self, path: &str) -> &mut Self {
58        parts(&mut self.0).uri = Uri::from_str(path).unwrap();
59        self
60    }
61
62    /// Insert a header, replacing any that were set with an equivalent field name.
63    pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {
64        match header.try_into_pair() {
65            Ok((key, value)) => {
66                parts(&mut self.0).headers.insert(key, value);
67            }
68            Err(err) => {
69                panic!("Error inserting test header: {}.", err.into());
70            }
71        }
72
73        self
74    }
75
76    /// Append a header, keeping any that were set with an equivalent field name.
77    pub fn append_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {
78        match header.try_into_pair() {
79            Ok((key, value)) => {
80                parts(&mut self.0).headers.append(key, value);
81            }
82            Err(err) => {
83                panic!("Error inserting test header: {}.", err.into());
84            }
85        }
86
87        self
88    }
89
90    /// Set request payload.
91    ///
92    /// This sets the `Content-Length` header with the size of `data`.
93    pub fn set_payload(&mut self, data: impl Into<Bytes>) -> &mut Self {
94        let mut payload = crate::h1::Payload::empty();
95        let bytes = data.into();
96        self.insert_header((header::CONTENT_LENGTH, bytes.len()));
97        payload.unread_data(bytes);
98        parts(&mut self.0).payload = Some(payload.into());
99        self
100    }
101
102    pub fn take(&mut self) -> TestRequest {
103        TestRequest(self.0.take())
104    }
105
106    /// Complete request creation and generate `Request` instance.
107    pub fn finish(&mut self) -> Request {
108        let inner = self.0.take().expect("cannot reuse test request builder");
109
110        let mut req = if let Some(pl) = inner.payload {
111            Request::with_payload(pl)
112        } else {
113            Request::with_payload(crate::h1::Payload::empty().into())
114        };
115
116        let head = req.head_mut();
117        head.uri = inner.uri;
118        head.method = inner.method;
119        head.version = inner.version;
120        head.headers = inner.headers;
121
122        req
123    }
124}
125
126#[inline]
127fn parts(parts: &mut Option<Inner>) -> &mut Inner {
128    parts.as_mut().expect("cannot reuse test request builder")
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn request_builder() {
137        let mut request = TestRequest::with_uri("/initial");
138        request
139            .version(Version::HTTP_10)
140            .method(Method::POST)
141            .uri("/final")
142            .insert_header(("x-test", "one"))
143            .append_header(("x-test", "two"));
144
145        let request = request.finish();
146        assert_eq!(request.head().uri, Uri::from_static("/final"));
147        assert_eq!(request.head().method, Method::POST);
148        assert_eq!(request.head().version, Version::HTTP_10);
149        assert_eq!(request.head().headers.get_all("x-test").count(), 2);
150
151        let mut request = TestRequest::default();
152        request.set_payload("body");
153        let request = request.finish();
154        assert_eq!(
155            request.head().headers.get(header::CONTENT_LENGTH).unwrap(),
156            "4"
157        );
158
159        let mut request = TestRequest::default();
160        let request = request.take();
161        let mut request = request;
162        assert_eq!(request.finish().head().uri, Uri::from_static("/"));
163    }
164
165    #[test]
166    #[should_panic(expected = "Error inserting test header")]
167    fn insert_header_rejects_invalid_header() {
168        TestRequest::default().insert_header(("invalid name", "value"));
169    }
170
171    #[test]
172    #[should_panic(expected = "Error inserting test header")]
173    fn append_header_rejects_invalid_header() {
174        TestRequest::default().append_header(("invalid name", "value"));
175    }
176}