use crate::body::Body;
use crate::error::Error;
use crate::internal::agent;
use crate::internal::request;
use crate::middleware::Middleware;
use crate::options::*;
use futures::executor;
use futures::prelude::*;
use http::{Request, Response};
use lazy_static::lazy_static;
use std::sync::Arc;
lazy_static! {
static ref USER_AGENT: String = format!(
"curl/{} chttp/{}",
curl::Version::get().version(),
env!("CARGO_PKG_VERSION")
);
}
pub(crate) fn global() -> &'static Client {
lazy_static! {
static ref CLIENT: Client = Client::new().unwrap();
}
&CLIENT
}
pub struct ClientBuilder {
default_options: Options,
middleware: Vec<Box<dyn Middleware>>,
}
impl Default for ClientBuilder {
fn default() -> Self {
Self::new()
}
}
impl ClientBuilder {
pub fn new() -> Self {
Self {
default_options: Options::default(),
middleware: Vec::new(),
}
}
pub fn options(mut self, options: Options) -> Self {
self.default_options = options;
self
}
#[cfg(feature = "cookies")]
pub fn with_cookies(self) -> Self {
self.with_middleware_impl(crate::cookies::CookieJar::default())
}
#[cfg(feature = "middleware-api")]
pub fn with_middleware(self, middleware: impl Middleware) -> Self {
self.with_middleware_impl(middleware)
}
#[allow(unused)]
fn with_middleware_impl(mut self, middleware: impl Middleware) -> Self {
self.middleware.push(Box::new(middleware));
self
}
pub fn build(&mut self) -> Result<Client, Error> {
let agent = agent::create()?;
Ok(Client {
agent: agent,
default_options: self.default_options.clone(),
middleware: Arc::new(self.middleware.drain(..).collect()),
})
}
}
pub struct Client {
agent: agent::Handle,
default_options: Options,
middleware: Arc<Vec<Box<dyn Middleware>>>,
}
impl Client {
pub fn new() -> Result<Self, Error> {
ClientBuilder::default().build()
}
pub fn builder() -> ClientBuilder {
ClientBuilder::new()
}
pub fn get<U>(&self, uri: U) -> Result<Response<Body>, Error> where http::Uri: http::HttpTryFrom<U> {
let request = http::Request::get(uri).body(Body::default())?;
self.send(request)
}
pub fn head<U>(&self, uri: U) -> Result<Response<Body>, Error> where http::Uri: http::HttpTryFrom<U> {
let request = http::Request::head(uri).body(Body::default())?;
self.send(request)
}
pub fn post<U>(&self, uri: U, body: impl Into<Body>) -> Result<Response<Body>, Error> where http::Uri: http::HttpTryFrom<U> {
let request = http::Request::post(uri).body(body)?;
self.send(request)
}
pub fn put<U>(&self, uri: U, body: impl Into<Body>) -> Result<Response<Body>, Error> where http::Uri: http::HttpTryFrom<U> {
let request = http::Request::put(uri).body(body)?;
self.send(request)
}
pub fn delete<U>(&self, uri: U) -> Result<Response<Body>, Error> where http::Uri: http::HttpTryFrom<U> {
let request = http::Request::delete(uri).body(Body::default())?;
self.send(request)
}
pub fn send<B: Into<Body>>(&self, request: Request<B>) -> Result<Response<Body>, Error> {
executor::block_on(self.send_async_impl(request))
}
#[cfg(feature = "async-api")]
pub fn send_async<B: Into<Body>>(&self, request: Request<B>) -> impl Future<Item=Response<Body>, Error=Error> {
self.send_async_impl(request)
}
fn send_async_impl<B: Into<Body>>(&self, request: Request<B>) -> impl Future<Item=Response<Body>, Error=Error> {
let mut request = request.map(Into::into);
request.headers_mut()
.entry(http::header::USER_AGENT)
.unwrap()
.or_insert(USER_AGENT.parse().unwrap());
let uri = request.uri().clone();
let middleware = self.middleware.clone();
for middleware in middleware.iter().rev() {
request = middleware.filter_request(request);
}
let options = request.extensions_mut().remove::<Options>();
let options = options.as_ref().unwrap_or(&self.default_options);
return request::create(request, options)
.and_then(|(request, future)| {
self.agent.begin_execute(request).map(|_| future)
})
.into_future()
.flatten()
.map(move |mut response| {
response.extensions_mut().insert(uri);
for middleware in middleware.iter() {
response = middleware.filter_response(response);
}
response
});
}
}