generic_async_http_client/
request.rs

1use crate::{imp, Body, Error, HeaderName, HeaderValue, Response};
2use serde::Serialize;
3use std::{convert::TryInto, fmt::Debug};
4
5/// Builds a HTTP request, poll it to query
6/// ```
7/// # use generic_async_http_client::{Request, Response, Error};
8/// # async fn get() -> Result<(), Error> {
9///     let req = Request::get("http://example.com/");
10///     let resp = req.exec().await?;
11/// # Ok(())
12/// # }
13/// ```
14///
15/// Depending on the chosen implementation, `Request` implements `TryFrom<(TryInto<Method>, TryInto<Url>)>`.
16pub struct Request(pub(crate) imp::Req);
17impl Request {
18    //auth
19    //proxy - should be set by bin
20    //cookies
21    //timeout
22    //tls validation
23    //tls client certa
24    //session (ref + cookies)
25    pub fn get(uri: &str) -> Request {
26        Request(imp::Req::get(uri))
27    }
28    pub fn post(uri: &str) -> Request {
29        Request(imp::Req::post(uri))
30    }
31    pub fn put(uri: &str) -> Request {
32        Request(imp::Req::put(uri))
33    }
34    pub fn delete(uri: &str) -> Request {
35        Request(imp::Req::delete(uri))
36    }
37    pub fn head(uri: &str) -> Request {
38        Request(imp::Req::head(uri))
39    }
40    pub fn options(uri: &str) -> Request {
41        Request(imp::Req::options(uri))
42    }
43    pub fn new(meth: &str, uri: &str) -> Result<Request, Error> {
44        Ok(imp::Req::new(meth, uri).map(Request)?)
45    }
46    /// Add a JSON body to the request
47    /// ```
48    /// # use generic_async_http_client::{Request, Response, Error};
49    /// # use serde::Serialize;
50    /// #[derive(Serialize)]
51    /// struct JoseBody {
52    ///     protected: String,
53    ///     payload: String,
54    ///     signature: String,
55    /// }
56    /// async fn jose(jose: &JoseBody) -> Result<Response, Error> {
57    ///    let req = Request::put("http://example.com/").json(jose)?;
58    ///    req.exec().await
59    /// }
60    /// ```
61    pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> Result<Self, Error> {
62        self.0.json(json)?;
63        Ok(self)
64    }
65    /// Add a form data body to the request
66    /// ```
67    /// # use generic_async_http_client::{Request, Response, Error};
68    /// # use serde::Serialize;
69    /// #[derive(Serialize)]
70    /// struct ContactForm {
71    ///     email: String,
72    ///     text: String,
73    /// }
74    /// async fn post_form(form: &ContactForm) -> Result<Response, Error> {
75    ///    let req = Request::post("http://example.com/").form(form)?;
76    ///    req.exec().await
77    /// }
78    /// ```
79    pub fn form<T: Serialize + ?Sized>(mut self, form: &T) -> Result<Self, Error> {
80        self.0.form(form)?;
81        Ok(self)
82    }
83    /// Add query parameter to the request
84    pub fn query<T: Serialize + ?Sized>(mut self, query: &T) -> Result<Self, Error> {
85        self.0.query(query)?;
86        Ok(self)
87    }
88    /// Add a body to the request
89    /// ```
90    /// # use generic_async_http_client::{Request, Response, Error};
91    /// # async fn body() -> Result<Response, Error> {
92    ///     let req = Request::post("http://example.com/").body("some body")?;
93    /// #   req.exec().await
94    /// # }
95    /// ```
96    pub fn body(mut self, body: impl Into<Body>) -> Result<Self, Error> {
97        self.0.body(body.into())?;
98        Ok(self)
99    }
100    /// Add a single header to the request
101    /// If the map did have this key present, the new value is associated with the key
102    /// ```
103    /// # use generic_async_http_client::{Request, Response, Error};
104    /// # async fn ua() -> Result<Response, Error> {
105    ///     let req = Request::get("http://example.com/").set_header("User-Agent", "generic_async_http_client v0.2")?;
106    /// #   req.exec().await
107    /// # }
108    /// ```
109    pub fn set_header<N, V, E1, E2>(mut self, name: N, value: V) -> Result<Self, Error>
110    where
111        N: TryInto<HeaderName, Error = E1>,
112        V: TryInto<HeaderValue, Error = E2>,
113        Error: From<E1>,
114        Error: From<E2>,
115    {
116        let val: HeaderValue = value.try_into()?;
117        let name: HeaderName = name.try_into()?;
118        self.0.set_header(name.into(), val.into())?;
119
120        Ok(self)
121    }
122    /// Add a single header to the request
123    /// If the map did have this key present, the new value is pushed to the end of the list of values
124    pub fn add_header<N, V, E1, E2>(mut self, name: N, value: V) -> Result<Self, Error>
125    where
126        N: TryInto<HeaderName, Error = E1>,
127        V: TryInto<HeaderValue, Error = E2>,
128        Error: From<E1>,
129        Error: From<E2>,
130    {
131        let val: HeaderValue = value.try_into()?;
132        let name: HeaderName = name.try_into()?;
133        self.0.add_header(name.into(), val.into())?;
134
135        Ok(self)
136    }
137    /*
138    TODO stream body
139    body(Body::from_reader)
140    */
141    //TODO multipart
142
143    /// Send the request to the webserver
144    pub async fn exec(self) -> Result<Response, Error> {
145        #[cfg(all(feature = "mock_tests", test))]
146        let r = if crate::Mock::uses_mock() {
147            Response(crate::Mock::check(self.0)?)
148        } else {
149            self.0.send_request().await?
150        };
151        #[cfg(not(all(feature = "mock_tests", test)))]
152        let r = self.0.send_request().await?;
153        //https://crates.io/crates/hreq
154
155        match r.status_code() {
156            100..300 => Ok(r),
157            300..400 => {
158                if let Some(loc) = r.header("Location").and_then(|l| l.try_into().ok()) {
159                    let _l: String = loc;
160                    //TODO redirect
161                }
162                Ok(r)
163            }
164            s @ 400..500 => Err(Error::HTTPClientErr(s, r)),
165            //s @ 500..600 => Err(Error::HTTPServerErr(s, r)),
166            s => Err(Error::HTTPServerErr(s, r)),
167        }
168    }
169}
170impl Debug for Request {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        std::fmt::Debug::fmt(&self.0, f)
173    }
174}
175
176pub trait Requests: Debug {
177    fn get(uri: &str) -> Self;
178    fn post(uri: &str) -> Self;
179    fn put(uri: &str) -> Self;
180    fn delete(uri: &str) -> Self;
181    fn head(uri: &str) -> Self;
182    fn options(uri: &str) -> Self;
183    fn new(meth: &str, uri: &str) -> Result<Self, imp::Error>
184    where
185        Self: std::marker::Sized;
186    async fn send_request(self) -> Result<Response, imp::Error>
187    where
188        Self: std::marker::Sized;
189    fn json<T: Serialize + ?Sized>(&mut self, json: &T) -> Result<(), imp::Error>;
190    fn form<T: Serialize + ?Sized>(&mut self, data: &T) -> Result<(), imp::Error>;
191    fn query<T: Serialize + ?Sized>(&mut self, query: &T) -> Result<(), imp::Error>;
192    fn body<B: Into<imp::Body>>(&mut self, body: B) -> Result<(), imp::Error>;
193    fn set_header(
194        &mut self,
195        name: imp::HeaderName,
196        values: imp::HeaderValue,
197    ) -> Result<(), imp::Error>;
198    fn add_header(
199        &mut self,
200        name: imp::HeaderName,
201        values: imp::HeaderValue,
202    ) -> Result<(), imp::Error>;
203}
204
205/*
206enum State{
207    Build(imp::Req),
208    Fetch(Pin<Box<dyn Future<Output=Result<imp::Resp, Error>>>>)
209}
210struct Request2{
211    state: std::cell::Cell<State>
212}
213impl Future for Request2{
214    type Output = Result<Response, Error>;
215
216    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
217        println!("poll");
218        let pin = self.get_mut();
219
220        match pin.state.get_mut() {
221            State::Build(req) => {
222                let fut = req.send_request();
223                pin.state.set(State::Fetch(fut.boxed()));
224                Poll::Pending
225            },
226            State::Fetch(mut fut) => {
227                match fut.poll_unpin(cx) {
228                    Poll::Ready(Ok(resp)) => Poll::Ready(Ok(Response(resp))),
229                    Poll::Pending => Poll::Pending,
230                    Poll::Ready(Err(e)) => Poll::Ready(Err(e))
231                }
232            },
233        }
234    }
235}*/