Skip to main content

rust_integration_services/http/
http_request.rs

1use std::collections::HashMap;
2use std::marker::PhantomData;
3
4use bytes::Bytes;
5use futures::{Stream, TryStreamExt};
6use http_body_util::{BodyDataStream, Empty, Full, StreamBody};
7use http_body_util::{BodyExt, combinators::BoxBody};
8use hyper::HeaderMap;
9use hyper::body::Frame;
10use hyper::header::HeaderValue;
11use hyper::{Error, Request, body::Incoming};
12
13pub struct Final;
14pub struct SetMethod;
15
16#[derive(Debug)]
17pub struct HttpRequest {
18    body: BoxBody<Bytes, Error>,
19    parts: hyper::http::request::Parts,
20    params: HashMap<String, String>,
21}
22
23impl HttpRequest {
24
25    /// Create a new request using builder.
26    pub fn builder() -> HttpRequestBuilder<SetMethod>  {
27        HttpRequestBuilder {
28            builder: Request::builder(),
29            _state: PhantomData
30        }
31    }
32
33    /// Create a new request from hyper parts.
34    pub fn from_parts(body: BoxBody<Bytes, Error>, parts: hyper::http::request::Parts) -> HttpRequest {
35        HttpRequest {
36            body,
37            parts,
38            params: HashMap::new()
39        }
40    }
41
42    /// Create a new request from hyper parts with params.
43    pub fn from_parts_with_params(body: BoxBody<Bytes, Error>, parts: hyper::http::request::Parts, params: HashMap<String, String>) -> HttpRequest {
44        HttpRequest {
45            body,
46            parts,
47            params
48        }
49    }
50
51    /// Returns the boxed body.
52    /// 
53    /// Used for moving body between requests/responses.
54    ///
55    /// **This consumes the HttpRequest**
56    pub fn body(self) -> BoxBody<Bytes, Error> {
57        self.body
58    }
59
60    /// Returns the body data stream.
61    ///
62    /// **This consumes the HttpRequest**
63    pub fn body_as_stream(self) -> BodyDataStream<BoxBody<Bytes, Error>> {
64        self.body.into_data_stream()
65    }
66
67    /// Returns the body as bytes.
68    /// 
69    /// **This consumes the HttpRequest**
70    pub async fn body_as_bytes(self) -> anyhow::Result<Bytes> {
71        Ok(self.body.collect().await?.to_bytes())
72    }
73
74    /// Returns the method.
75    pub fn method(&self) -> &str {
76        self.parts.method.as_str()
77    }
78
79    /// Returns the uri host.
80    pub fn host(&self) -> Option<&str> {
81        self.parts.uri.host()
82    }
83
84    /// Returns the uri path.
85    pub fn path(&self) -> &str {
86        self.parts.uri.path()
87    }
88
89    /// Returns the uri port.
90    pub fn port(&self) -> Option<u16> {
91        self.parts.uri.port_u16()
92    }
93
94    /// Returns the uri scheme.
95    pub fn scheme(&self) -> Option<&str> {
96        self.parts.uri.scheme_str()
97    }
98
99    /// Returns all headers.
100    pub fn headers(&self) -> &HeaderMap<HeaderValue> {
101        &self.parts.headers
102    }
103
104    /// Returns a hashmap with request params.
105    pub fn params(&self) -> &HashMap<String, String> {
106        &self.params
107    }
108}
109
110pub struct HttpRequestBuilder<State> {
111    builder: hyper::http::request::Builder,
112    _state: PhantomData<State>
113}
114
115impl HttpRequestBuilder<SetMethod> {
116    /// Sets the HTTP method to `GET` and assigns the request URI.
117    pub fn get(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
118        self.builder = self.builder.method("GET").uri(uri.into());
119        HttpRequestBuilder {
120            builder: self.builder,
121            _state: PhantomData
122        }
123    }
124
125    /// Sets the HTTP method to `POST` and assigns the request URI.
126    pub fn post(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
127        self.builder = self.builder.method("POST").uri(uri.into());
128        HttpRequestBuilder {
129            builder: self.builder,
130            _state: PhantomData
131        }
132    }
133
134    /// Sets the HTTP method to `PUT` and assigns the request URI.
135    pub fn put(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
136        self.builder = self.builder.method("PUT").uri(uri.into());
137        HttpRequestBuilder {
138            builder: self.builder,
139            _state: PhantomData
140        }
141    }
142
143    /// Sets the HTTP method to `PATCH` and assigns the request URI.
144    pub fn patch(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
145        self.builder = self.builder.method("PATCH").uri(uri.into());
146        HttpRequestBuilder {
147            builder: self.builder,
148            _state: PhantomData
149        }
150    }
151
152    /// Sets the HTTP method to `DELETE` and assigns the request URI.
153    pub fn delete(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
154        self.builder = self.builder.method("DELETE").uri(uri.into());
155        HttpRequestBuilder {
156            builder: self.builder,
157            _state: PhantomData
158        }
159    }
160
161    /// Sets the HTTP method to `OPTIONS` and assigns the request URI.
162    pub fn options(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
163        self.builder = self.builder.method("OPTIONS").uri(uri.into());
164        HttpRequestBuilder {
165            builder: self.builder,
166            _state: PhantomData
167        }
168    }
169
170    /// Sets the HTTP method to `HEAD` and assigns the request URI.
171    pub fn head(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
172        self.builder = self.builder.method("OPTIONS").uri(uri.into());
173        HttpRequestBuilder {
174            builder: self.builder,
175            _state: PhantomData
176        }
177    }
178
179    /// Sets the HTTP method to `CONNECT` and assigns the request URI.
180    pub fn connect(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
181        self.builder = self.builder.method("CONNECT").uri(uri.into());
182        HttpRequestBuilder {
183            builder: self.builder,
184            _state: PhantomData
185        }
186    }
187
188    /// Sets the HTTP method to `TRACE` and assigns the request URI.
189    pub fn trace(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
190        self.builder = self.builder.method("TRACE").uri(uri.into());
191        HttpRequestBuilder {
192            builder: self.builder,
193            _state: PhantomData
194        }
195    }
196}
197
198impl HttpRequestBuilder<Final> {
199
200    /// Finish the builder and the create the request with an empty body.
201    pub fn body_empty(self) -> anyhow::Result<HttpRequest> {
202        let body = Empty::new().map_err(|e| match e {}).boxed();
203        let request = self.builder.body(body)?;
204        Ok(HttpRequest::from(request))
205    }
206
207    /// Finish the builder and the create the request with a boxed body.
208    /// 
209    /// **Used for moving a body from another request or response.**
210    pub fn body_boxed(self, body: BoxBody<Bytes, Error>) -> anyhow::Result<HttpRequest> {
211        let request = self.builder.body(body)?;
212        Ok(HttpRequest::from(request))
213    }
214
215    /// Finish the builder and the create the request with a body of bytes in memory.
216    pub fn body_bytes(self, body: impl Into<Bytes>) -> anyhow::Result<HttpRequest> {
217        let body = Full::from(body.into()).map_err(|e| match e {}).boxed();
218        let request = self.builder.body(body)?;
219        Ok(HttpRequest::from(request))
220    }
221
222    /// Finish the builder and the create the request with a body of bytes as a stream.
223    pub fn body_stream<S>(self, stream: S) -> anyhow::Result<HttpRequest>
224    where
225        S: Stream<Item = Result<Bytes, hyper::Error>> + Send + Sync + 'static,
226    {
227        let frame_stream = stream.map_ok(Frame::data);
228        let body = StreamBody::new(frame_stream);
229        let body_ext = BodyExt::boxed(body);
230        let request = self.builder.body(body_ext)?;
231        Ok(HttpRequest::from(request))
232    }
233
234    /// Add a header to the request.
235    pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
236        self.builder = self.builder.header(key.into(), value.into());
237        self
238    }
239
240    /// Copy headers from another request or response.
241    pub fn headers(mut self, headers: &HeaderMap<HeaderValue>) -> Self {
242        for (key, value) in headers.iter() {
243            self.builder = self.builder.header(key, value);
244        }
245        self
246    }
247}
248
249impl From<HttpRequest> for Request<BoxBody<Bytes, Error>> {
250    fn from(req: HttpRequest) -> Self {
251        Request::from_parts(req.parts, req.body.boxed())
252    }
253}
254
255impl From<Request<BoxBody<Bytes, Error>>> for HttpRequest {
256    fn from(req: Request<BoxBody<Bytes, Error>>) -> Self {
257        let (parts, body) = req.into_parts();
258        HttpRequest::from_parts(body, parts)
259    }
260}
261
262impl From<Request<Incoming>> for HttpRequest {
263    fn from(req: Request<Incoming>) -> Self {
264        let (parts, body) = req.into_parts();
265        HttpRequest::from_parts(body.boxed(), parts)
266    }
267}
268