Skip to main content

rust_integration_services/http/
http_response.rs

1use std::marker::PhantomData;
2
3use bytes::Bytes;
4use futures::{Stream, TryStreamExt};
5use http_body_util::{BodyDataStream, BodyExt, Empty, Full, StreamBody, combinators::BoxBody};
6use hyper::{Error, HeaderMap, Response, body::{Frame, Incoming}, header::HeaderValue};
7
8pub struct Final;
9pub struct SetStatus;
10
11#[derive(Debug)]
12pub struct HttpResponse {
13    body: BoxBody<Bytes, Error>,
14    parts: hyper::http::response::Parts,
15}
16
17impl HttpResponse {
18
19    /// Create a new response using builder.
20    pub fn builder() -> HttpResponseBuilder<SetStatus>  {
21        HttpResponseBuilder {
22            builder: Response::builder(),
23            _state: PhantomData
24        }
25    }
26
27    /// Create a new response from hyper parts.
28    pub fn from_parts(body: BoxBody<Bytes, Error>, parts: hyper::http::response::Parts) -> HttpResponse {
29        HttpResponse {
30            body,
31            parts
32        }
33    }
34
35    /// Returns the boxed body.
36    /// 
37    /// Used for moving body between requests/responses.
38    ///
39    /// **This consumes the HttpResponse**
40    pub fn body(self) -> BoxBody<Bytes, Error> {
41        self.body
42    }
43
44    /// Returns the body data stream.
45    ///
46    /// **This consumes the HttpRequest**
47    pub fn body_as_stream(self) -> BodyDataStream<BoxBody<Bytes, Error>> {
48        self.body.into_data_stream()
49    }
50
51    /// Returns the body as bytes.
52    /// 
53    /// **This consumes the HttpResponse**
54    pub async fn body_as_bytes(self) -> anyhow::Result<Bytes> {
55        Ok(self.body.collect().await?.to_bytes())
56    }
57
58    /// Returns the status.
59    pub fn status(&self) -> u16 {
60        self.parts.status.as_u16()
61    }
62
63    /// Returns all headers.
64    pub fn headers(&self) -> &HeaderMap<HeaderValue> {
65        &self.parts.headers
66    }
67}
68
69pub struct HttpResponseBuilder<State> {
70    builder: hyper::http::response::Builder,
71    _state: PhantomData<State>
72}
73
74impl HttpResponseBuilder<SetStatus>  {
75    pub fn status(mut self, status: u16) -> HttpResponseBuilder<Final> {
76        self.builder = self.builder.status(status);
77        HttpResponseBuilder {
78            builder: self.builder,
79            _state: PhantomData
80        }
81    }
82}
83
84impl HttpResponseBuilder<Final> {
85
86    /// Finish the builder and the create the response with an empty body.
87    pub fn body_empty(self) -> anyhow::Result<HttpResponse> {
88        let body = Empty::new().map_err(|e| match e {}).boxed();
89        let response = self.builder.body(body)?;
90        Ok(HttpResponse::from(response))
91    }
92
93    /// Finish the builder and the create the response with a boxed body.
94    /// 
95    /// **Used for moving a body from another request or response.**
96    pub fn body_boxed(self, body: BoxBody<Bytes, Error>) -> anyhow::Result<HttpResponse> {
97        let response = self.builder.body(body)?;
98        Ok(HttpResponse::from(response))
99    }
100
101    /// Finish the builder and the create the response with a body of bytes in memory.
102    pub fn body_bytes(self, body: impl Into<Bytes>) -> anyhow::Result<HttpResponse> {
103        let body = Full::from(body.into()).map_err(|e| match e {}).boxed();
104        let response = self.builder.body(body)?;
105        Ok(HttpResponse::from(response))
106    }
107
108    /// Finish the builder and the create the response with a body of bytes as a stream.
109    pub fn body_stream<S>(self, stream: S) -> anyhow::Result<HttpResponse>
110    where
111        S: Stream<Item = Result<Bytes, hyper::Error>> + Send + Sync + 'static,
112    {
113        let frame_stream = stream.map_ok(Frame::data);
114        let body = StreamBody::new(frame_stream);
115        let body_ext = BodyExt::boxed(body);
116        let response = self.builder.body(body_ext)?;
117        Ok(HttpResponse::from(response))
118    }
119
120    /// Add a header to the response.
121    pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
122        self.builder = self.builder.header(key.into(), value.into());
123        self
124    }
125
126    /// Copy headers from another request or response.
127    pub fn headers(mut self, headers: &HeaderMap<HeaderValue>) -> Self {
128        for (key, value) in headers.iter() {
129            self.builder = self.builder.header(key, value);
130        }
131        self
132    }
133}
134
135impl From<HttpResponse> for Response<BoxBody<Bytes, Error>> {
136    fn from(res: HttpResponse) -> Self {
137        Response::from_parts(res.parts, res.body)
138    }
139}
140
141impl From<Response<BoxBody<Bytes, Error>>> for HttpResponse {
142    fn from(res: Response<BoxBody<Bytes, Error>>) -> Self {
143        let (parts, body) = res.into_parts();
144        HttpResponse::from_parts(body, parts)
145    }
146}
147
148impl From<Response<Incoming>> for HttpResponse {
149    fn from(req: Response<Incoming>) -> Self {
150        let (parts, body) = req.into_parts();
151        HttpResponse::from_parts(body.boxed(), parts)
152    }
153}
154