generic_async_http_client/
request.rs

1use crate::{imp, Body, Error, HeaderName, HeaderValue, Response};
2use serde::Serialize;
3use std::convert::TryInto;
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        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        let r = self.0.send_request().await.map(Response)?;
146        //https://crates.io/crates/hreq
147
148        if r.status_code() > 299 && r.status_code() < 399 {
149            if let Some(loc) = r.header("Location").and_then(|l| l.try_into().ok()) {
150                let _l: String = loc;
151                //TODO redirect
152            }
153        }
154        Ok(r)
155    }
156}
157impl std::fmt::Debug for Request {
158    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159        std::fmt::Debug::fmt(&self.0, f)
160    }
161}
162
163/*
164enum State{
165    Build(imp::Req),
166    Fetch(Pin<Box<dyn Future<Output=Result<imp::Resp, Error>>>>)
167}
168struct Request2{
169    state: std::cell::Cell<State>
170}
171impl Future for Request2{
172    type Output = Result<Response, Error>;
173
174    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
175        println!("poll");
176        let pin = self.get_mut();
177
178        match pin.state.get_mut() {
179            State::Build(req) => {
180                let fut = req.send_request();
181                pin.state.set(State::Fetch(fut.boxed()));
182                Poll::Pending
183            },
184            State::Fetch(mut fut) => {
185                match fut.poll_unpin(cx) {
186                    Poll::Ready(Ok(resp)) => Poll::Ready(Ok(Response(resp))),
187                    Poll::Pending => Poll::Pending,
188                    Poll::Ready(Err(e)) => Poll::Ready(Err(e))
189                }
190            },
191        }
192    }
193}*/