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