hitt_request/
lib.rs

1use hitt_parser::HittRequest;
2
3pub struct HittResponse {
4    pub url: String,
5    pub method: String,
6    pub status_code: reqwest::StatusCode,
7    pub headers: reqwest::header::HeaderMap,
8    pub body: String,
9    pub http_version: http::version::Version,
10    pub duration: core::time::Duration,
11}
12
13#[inline]
14pub async fn send_request(
15    http_client: &reqwest::Client,
16    input: &HittRequest,
17    timeout: Option<&core::time::Duration>,
18) -> Result<HittResponse, reqwest::Error> {
19    let url = input.uri.to_string();
20
21    let mut partial_req = http_client.request(input.method.clone(), &url);
22
23    if let Some(http_version) = input.http_version {
24        partial_req = partial_req.version(http_version);
25    }
26
27    if !input.headers.is_empty() {
28        partial_req = partial_req.headers(input.headers.clone());
29    }
30
31    if input.body.is_some()
32        && let Some(body) = input.body.clone()
33    {
34        partial_req = partial_req.body(body);
35    }
36
37    if timeout.is_some()
38        && let Some(dur) = timeout
39    {
40        partial_req = partial_req.timeout(dur.to_owned());
41    }
42
43    let request = partial_req.build()?;
44
45    // TODO: implement more precise benchmark?
46    let start = std::time::Instant::now();
47    let response = http_client.execute(request).await?;
48    let duration = start.elapsed();
49
50    Ok(HittResponse {
51        url,
52        method: input.method.to_string(),
53        status_code: response.status(),
54        headers: response.headers().to_owned(),
55        http_version: response.version(),
56        duration,
57        body: response.text().await?,
58    })
59}
60
61#[cfg(test)]
62mod test_send_request {
63    use core::{str::FromStr, time::Duration};
64
65    use http::{HeaderMap, StatusCode};
66
67    use crate::send_request;
68
69    #[tokio::test]
70    async fn it_should_return_a_response() {
71        let http_client = reqwest::Client::new();
72
73        let timeout = None;
74
75        let uri = http::Uri::from_static("https://dummyjson.com/products/1");
76
77        let method = http::Method::GET;
78
79        let input = hitt_parser::HittRequest {
80            method: method.clone(),
81            uri: uri.clone(),
82            headers: HeaderMap::default(),
83            body: None,
84            http_version: None,
85        };
86
87        let result = send_request(&http_client, &input, timeout.as_ref())
88            .await
89            .expect("it to be successful");
90
91        assert_eq!(result.url, uri.to_string());
92
93        assert_eq!(result.status_code, StatusCode::OK);
94
95        assert_eq!(
96            http::Method::from_str(&result.method).expect("it to be a valid method"),
97            method
98        );
99
100        assert!(!result.body.is_empty());
101    }
102
103    #[tokio::test]
104    async fn it_should_set_http_version() {
105        let http_client = reqwest::Client::new();
106
107        let timeout = None;
108
109        let uri = http::Uri::from_static("https://dummyjson.com/products/1");
110
111        let method = http::Method::GET;
112
113        let input = hitt_parser::HittRequest {
114            method: method.clone(),
115            uri: uri.clone(),
116            headers: HeaderMap::default(),
117            body: Some("hello world".to_owned()),
118            http_version: Some(http::Version::HTTP_11),
119        };
120
121        let result = send_request(&http_client, &input, timeout.as_ref())
122            .await
123            .expect("it to be successful");
124
125        assert_eq!(result.url, uri.to_string());
126
127        assert_eq!(result.status_code, StatusCode::OK);
128
129        assert_eq!(
130            http::Method::from_str(&result.method).expect("it to be a valid method"),
131            method
132        );
133
134        assert!(!result.body.is_empty());
135    }
136
137    #[tokio::test]
138    async fn it_should_set_headers() {
139        let http_client = reqwest::Client::new();
140
141        let timeout = None;
142
143        let uri = http::Uri::from_static("https://dummyjson.com/products/1");
144
145        let mut headers = HeaderMap::new();
146
147        let header_key = http::HeaderName::from_static("mads");
148
149        let header_value = http::HeaderValue::from_static("hougesen");
150
151        headers.insert(header_key, header_value);
152
153        let method = http::Method::DELETE;
154
155        let input = hitt_parser::HittRequest {
156            method: method.clone(),
157            uri: uri.clone(),
158            headers,
159            body: Some("hello world".to_owned()),
160            http_version: None,
161        };
162
163        let result = send_request(&http_client, &input, timeout.as_ref())
164            .await
165            .expect("it to be successful");
166
167        assert_eq!(result.url, uri.to_string());
168
169        assert_eq!(result.status_code, StatusCode::OK);
170
171        assert_eq!(
172            http::Method::from_str(&result.method).expect("it to be a valid method"),
173            method
174        );
175
176        assert!(!result.body.is_empty());
177    }
178
179    #[tokio::test]
180    async fn it_should_set_body() {
181        let http_client = reqwest::Client::new();
182
183        let timeout = None;
184
185        let uri = http::Uri::from_static("https://dummyjson.com/products/1");
186
187        let input = hitt_parser::HittRequest {
188            method: http::Method::GET,
189            uri: uri.clone(),
190            headers: HeaderMap::default(),
191            body: Some("hello world".to_owned()),
192            http_version: None,
193        };
194
195        let result = send_request(&http_client, &input, timeout.as_ref())
196            .await
197            .expect("it to be successful");
198
199        assert_eq!(result.url, uri.to_string());
200
201        assert_eq!(result.status_code, StatusCode::OK);
202
203        assert!(!result.body.is_empty());
204    }
205
206    #[tokio::test]
207    async fn timeout_should_work() {
208        let http_client = reqwest::Client::new();
209
210        let timeout = Some(Duration::from_millis(5));
211
212        let uri = http::Uri::from_static("https://dummyjson.com/products/1");
213
214        let input = hitt_parser::HittRequest {
215            method: http::Method::GET,
216            uri: uri.clone(),
217            headers: HeaderMap::default(),
218            body: None,
219            http_version: None,
220        };
221
222        let response = send_request(&http_client, &input, timeout.as_ref())
223            .await
224            .err()
225            .expect("it to throw an error");
226
227        assert!(response.is_timeout());
228    }
229}