generic-async-http-client 0.4.0

async HTTP(S) client
Documentation
use std::{str::FromStr, convert::{TryFrom, Infallible}};

use serde::Serialize;

pub use hyper::{
    header::{HeaderName, HeaderValue},
    Body,
};
use hyper::{
    header::{InvalidHeaderName, InvalidHeaderValue, CONTENT_TYPE},
    http::{
        method::{InvalidMethod, Method},
        request::Builder,
        uri::{Builder as UriBuilder, InvalidUri, PathAndQuery, Uri},
        Error as HTTPError,
    },
    Client, Error as HyperError, Response,
};
use std::mem::take;

mod connector;
use connector::Connector;

#[derive(Debug)]
pub struct Req {
    req: Builder,
    body: Body,
}
pub struct Resp {
    resp: Response<Body>,
}

impl Into<Response<Body>> for crate::Response {
    fn into(self) -> Response<Body> {
        self.0.resp
    }
}

impl<M,U> TryFrom<(M,U)> for crate::Request
where
    Method: TryFrom<M>,
    <Method as TryFrom<M>>::Error: Into<HTTPError>,
    Uri: TryFrom<U>,
    <Uri as TryFrom<U>>::Error: Into<HTTPError>,
    {

    type Error = Infallible;

    fn try_from(value: (M,U)) -> Result<Self, Self::Error> {
        let req = Builder::new().method(value.0).uri(value.1);

        Ok(crate::Request(Req {
            req,
            body: Body::empty(),
        }))
    }
}

impl Req {
    pub fn get(uri: &str) -> Req {
        Self::init(Method::GET, uri)
    }
    pub fn post(uri: &str) -> Req {
        Self::init(Method::POST, uri)
    }
    pub fn put(uri: &str) -> Req {
        Self::init(Method::PUT, uri)
    }
    pub fn delete(uri: &str) -> Req {
        Self::init(Method::DELETE, uri)
    }
    pub fn head(uri: &str) -> Req {
        Self::init(Method::HEAD, uri)
    }
    pub fn options(uri: &str) -> Req {
        Self::init(Method::OPTIONS, uri)
    }
    pub fn new(meth: &str, uri: &str) -> Result<Req, Error> {
        Ok(Self::init(Method::from_str(meth)?, uri))
    }
    fn init(method: Method, uri: &str) -> Req {
        let req = Builder::new().method(method).uri(uri);

        Req {
            req,
            body: Body::empty(),
        }
    }
    pub async fn send_request(self) -> Result<Resp, Error> {
        let req = self.req.body(self.body)?;

        let connector = Connector::new();
        let client = Client::builder().build::<_, Body>(connector);

        let resp = client.request(req).await?;
        Ok(Resp { resp })
    }
    pub fn json<T: Serialize + ?Sized>(&mut self, json: &T) -> Result<(), Error> {
        let bytes = serde_json::to_vec(&json)?;
        self.set_header(CONTENT_TYPE, HeaderValue::from_static("application/json"))?;
        self.body = Body::from(bytes);
        Ok(())
    }
    pub fn form<T: Serialize + ?Sized>(&mut self, data: &T) -> Result<(), Error> {
        let query = serde_urlencoded::to_string(data)?;
        let bytes = query.into_bytes();
        self.set_header(
            CONTENT_TYPE,
            HeaderValue::from_static("application/x-www-form-urlencoded"),
        )?;
        self.body = Body::from(bytes);
        Ok(())
    }
    pub fn query<T: Serialize + ?Sized>(&mut self, query: &T) -> Result<(), Error> {
        let query = serde_qs::to_string(&query)?;
        let old = self.req.uri_ref().expect("no uri");

        let mut p_and_p = String::with_capacity(old.path().len() + query.len() + 1);
        p_and_p.push_str(old.path());
        p_and_p.push('?');
        p_and_p.push_str(&query);

        let path_and_query = PathAndQuery::from_str(&p_and_p)?;

        let new = UriBuilder::new()
            .scheme(old.scheme_str().unwrap())
            .authority(old.authority().unwrap().as_str())
            .path_and_query(path_and_query)
            .build()?;

        self.req = take(&mut self.req).uri(new);
        Ok(())
    }
    pub fn body<B: Into<Body>>(&mut self, body: B) -> Result<(), Error> {
        self.body = body.into();
        Ok(())
    }
    pub fn set_header(&mut self, name: HeaderName, value: HeaderValue) -> Result<(), Error> {
        self.req.headers_mut().map(|hm| hm.insert(name, value));
        Ok(())
    }
    pub fn add_header(&mut self, name: HeaderName, value: HeaderValue) -> Result<(), Error> {
        self.req = take(&mut self.req).header(name, value);
        Ok(())
    }
}
use hyper::body::Buf;
use hyper::body::{aggregate, to_bytes};
use serde::de::DeserializeOwned;
impl Resp {
    pub fn status(&self) -> u16 {
        self.resp.status().as_u16()
    }
    pub fn status_str(&self) -> &'static str {
        self.resp.status().canonical_reason().unwrap_or("")
    }
    pub async fn json<D: DeserializeOwned>(&mut self) -> Result<D, Error> {
        let reader = aggregate(self.resp.body_mut()).await?.reader();
        Ok(serde_json::from_reader(reader)?)
    }
    pub async fn bytes(&mut self) -> Result<Vec<u8>, Error> {
        let b = to_bytes(self.resp.body_mut()).await?;
        Ok(b.to_vec())
    }
    pub async fn string(&mut self) -> Result<String, Error> {
        let b = self.bytes().await?;
        Ok(String::from_utf8_lossy(&b).to_string())
    }
    pub fn get_header(&self, name: HeaderName) -> Option<&HeaderValue> {
        self.resp.headers().get(name)
    }
    pub fn header_iter(&self) -> impl Iterator<Item = (&HeaderName, &HeaderValue)> {
        self.resp.headers().into_iter()
    }
}

#[derive(Debug)]
pub enum Error {
    Scheme,
    Http(HTTPError),
    InvalidQueryString(serde_qs::Error),
    InvalidMethod(InvalidMethod),
    Hyper(HyperError),
    Json(serde_json::Error),
    InvalidHeaderValue(InvalidHeaderValue),
    InvalidHeaderName(InvalidHeaderName),
    InvalidUri(InvalidUri),
    Urlencoded(serde_urlencoded::ser::Error),
}
impl std::error::Error for Error {}
use std::fmt;
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Scheme => write!(f, "Scheme"),
            Error::Http(i) => write!(f, "{}", i.to_string()),
            Error::InvalidQueryString(i) => write!(f, "{}", i.to_string()),
            Error::InvalidMethod(i) => write!(f, "{}", i.to_string()),
            Error::Hyper(i) => write!(f, "{}", i.to_string()),
            Error::Json(i) => write!(f, "{}", i.to_string()),
            Error::InvalidHeaderValue(i) => write!(f, "{}", i.to_string()),
            Error::InvalidHeaderName(i) => write!(f, "{}", i.to_string()),
            Error::InvalidUri(i) => write!(f, "{}", i.to_string()),
            Error::Urlencoded(i) => write!(f, "{}", i.to_string()),
        }
    }
}

impl From<serde_urlencoded::ser::Error> for Error {
    fn from(e: serde_urlencoded::ser::Error) -> Self {
        Self::Urlencoded(e)
    }
}
impl From<InvalidUri> for Error {
    fn from(e: InvalidUri) -> Self {
        Self::InvalidUri(e)
    }
}
impl From<InvalidHeaderName> for Error {
    fn from(e: InvalidHeaderName) -> Self {
        Self::InvalidHeaderName(e)
    }
}

impl From<InvalidHeaderValue> for Error {
    fn from(e: InvalidHeaderValue) -> Self {
        Self::InvalidHeaderValue(e)
    }
}
impl From<serde_json::Error> for Error {
    fn from(e: serde_json::Error) -> Self {
        Self::Json(e)
    }
}
impl From<HyperError> for Error {
    fn from(e: HyperError) -> Self {
        Self::Hyper(e)
    }
}
impl From<InvalidMethod> for Error {
    fn from(e: InvalidMethod) -> Self {
        Self::InvalidMethod(e)
    }
}
impl From<HTTPError> for Error {
    fn from(e: HTTPError) -> Self {
        Self::Http(e)
    }
}
impl From<serde_qs::Error> for Error {
    fn from(e: serde_qs::Error) -> Self {
        Self::InvalidQueryString(e)
    }
}
impl From<std::convert::Infallible> for Error {
    fn from(_e: std::convert::Infallible) -> Self {
        unreachable!();
    }
}