diff --git a/awc/Cargo.toml b/awc/Cargo.toml index f316d0620..b43a8d47f 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -29,7 +29,6 @@ cookies = ["cookie", "actix-http/cookies"] [dependencies] actix-service = "0.3.4" actix-http = { git = "https://github.com/actix/actix-http.git" } -actix-codec = "0.1.1" bytes = "0.4" futures = "0.1" log =" 0.4" diff --git a/awc/src/builder.rs b/awc/src/builder.rs index 3104e5244..686948682 100644 --- a/awc/src/builder.rs +++ b/awc/src/builder.rs @@ -2,8 +2,11 @@ use std::cell::RefCell; use std::fmt; use std::rc::Rc; -use actix_http::client::Connector; -use actix_http::http::{header::IntoHeaderValue, HeaderMap, HeaderName, HttpTryFrom}; +use actix_http::client::{ConnectError, Connection, Connector}; +use actix_http::http::{ + header::IntoHeaderValue, HeaderMap, HeaderName, HttpTryFrom, Uri, +}; +use actix_service::Service; use crate::connect::{Connect, ConnectorWrapper}; use crate::Client; @@ -33,6 +36,18 @@ impl ClientBuilder { } } + /// Use custom connector service. + pub fn connector(mut self, connector: T) -> Self + where + T: Service + 'static, + T::Response: Connection, + ::Future: 'static, + T::Future: 'static, + { + self.connector = Rc::new(RefCell::new(ConnectorWrapper(connector))); + self + } + /// Do not follow redirects. /// /// Redirects are allowed by default. diff --git a/awc/src/connect.rs b/awc/src/connect.rs index c52bc34cb..a07662791 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -1,9 +1,11 @@ use actix_http::body::Body; -use actix_http::client::{ClientResponse, ConnectError, Connection, SendRequestError}; +use actix_http::client::{ConnectError, Connection, SendRequestError}; use actix_http::{http, RequestHead}; use actix_service::Service; use futures::Future; +use crate::response::ClientResponse; + pub(crate) struct ConnectorWrapper(pub T); pub(crate) trait Connect { @@ -32,7 +34,8 @@ where .call(head.uri.clone()) .from_err() // send request - .and_then(move |connection| connection.send_request(head, body)), + .and_then(move |connection| connection.send_request(head, body)) + .map(|(head, payload)| ClientResponse::new(head, payload)), ) } } diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 885af48e9..89acf7d58 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -1,9 +1,7 @@ use std::cell::RefCell; use std::rc::Rc; -pub use actix_http::client::{ - ClientResponse, ConnectError, InvalidUrl, SendRequestError, -}; +pub use actix_http::client::{ConnectError, InvalidUrl, SendRequestError}; pub use actix_http::http; use actix_http::client::Connector; @@ -12,9 +10,11 @@ use actix_http::http::{HttpTryFrom, Method, Uri}; mod builder; mod connect; mod request; +mod response; pub use self::builder::ClientBuilder; pub use self::request::ClientRequest; +pub use self::response::ClientResponse; use self::connect::{Connect, ConnectorWrapper}; diff --git a/awc/src/request.rs b/awc/src/request.rs index 90dfebcca..f23aa7ef9 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -12,7 +12,7 @@ use serde::Serialize; use serde_json; use actix_http::body::{Body, BodyStream}; -use actix_http::client::{ClientResponse, InvalidUrl, SendRequestError}; +use actix_http::client::{InvalidUrl, SendRequestError}; use actix_http::http::header::{self, Header, IntoHeaderValue}; use actix_http::http::{ uri, ConnectionType, Error as HttpError, HeaderName, HeaderValue, HttpTryFrom, @@ -20,6 +20,7 @@ use actix_http::http::{ }; use actix_http::{Error, Head, RequestHead}; +use crate::response::ClientResponse; use crate::Connect; /// An HTTP Client request builder @@ -30,18 +31,15 @@ use crate::Connect; /// ```rust /// use futures::future::{Future, lazy}; /// use actix_rt::System; -/// use actix_http::client; /// /// fn main() { /// System::new("test").block_on(lazy(|| { -/// let mut connector = client::Connector::new().service(); -/// -/// client::ClientRequest::get("http://www.rust-lang.org") // <- Create request builder +/// awc::Client::new() +/// .get("http://www.rust-lang.org") // <- Create request builder /// .header("User-Agent", "Actix-web") -/// .finish().unwrap() -/// .send(&mut connector) // <- Send http request +/// .send() // <- Send http request /// .map_err(|_| ()) -/// .and_then(|response| { // <- server http response +/// .and_then(|response| { // <- server http response /// println!("Response: {:?}", response); /// Ok(()) /// }) @@ -137,11 +135,13 @@ impl ClientRequest { /// use actix_http::{client, http}; /// /// fn main() { - /// let req = client::ClientRequest::build() + /// # actix_rt::System::new("test").block_on(futures::future::lazy(|| { + /// let req = awc::Client::new() + /// .get("http://www.rust-lang.org") /// .header("X-TEST", "value") - /// .header(http::header::CONTENT_TYPE, "application/json") - /// .finish() - /// .unwrap(); + /// .header(http::header::CONTENT_TYPE, "application/json"); + /// # Ok::<_, ()>(()) + /// # })); /// } /// ``` pub fn header(mut self, key: K, value: V) -> Self diff --git a/awc/src/response.rs b/awc/src/response.rs new file mode 100644 index 000000000..0ae66df0f --- /dev/null +++ b/awc/src/response.rs @@ -0,0 +1,102 @@ +use std::cell::{Ref, RefMut}; +use std::fmt; + +use bytes::Bytes; +use futures::{Poll, Stream}; + +use actix_http::error::PayloadError; +use actix_http::http::{HeaderMap, StatusCode, Version}; +use actix_http::{Extensions, Head, HttpMessage, Payload, PayloadStream, ResponseHead}; + +/// Client Response +pub struct ClientResponse { + pub(crate) head: ResponseHead, + pub(crate) payload: Payload, +} + +impl HttpMessage for ClientResponse { + type Stream = PayloadStream; + + fn headers(&self) -> &HeaderMap { + &self.head.headers + } + + fn extensions(&self) -> Ref { + self.head.extensions() + } + + fn extensions_mut(&self) -> RefMut { + self.head.extensions_mut() + } + + fn take_payload(&mut self) -> Payload { + std::mem::replace(&mut self.payload, Payload::None) + } +} + +impl ClientResponse { + /// Create new Request instance + pub(crate) fn new(head: ResponseHead, payload: Payload) -> ClientResponse { + ClientResponse { head, payload } + } + + #[inline] + pub(crate) fn head(&self) -> &ResponseHead { + &self.head + } + + #[inline] + pub(crate) fn head_mut(&mut self) -> &mut ResponseHead { + &mut self.head + } + + /// Read the Request Version. + #[inline] + pub fn version(&self) -> Version { + self.head().version + } + + /// Get the status from the server. + #[inline] + pub fn status(&self) -> StatusCode { + self.head().status + } + + #[inline] + /// Returns Request's headers. + pub fn headers(&self) -> &HeaderMap { + &self.head().headers + } + + #[inline] + /// Returns mutable Request's headers. + pub fn headers_mut(&mut self) -> &mut HeaderMap { + &mut self.head_mut().headers + } + + /// Checks if a connection should be kept alive. + #[inline] + pub fn keep_alive(&self) -> bool { + self.head().keep_alive() + } +} + +impl Stream for ClientResponse { + type Item = Bytes; + type Error = PayloadError; + + fn poll(&mut self) -> Poll, Self::Error> { + self.payload.poll() + } +} + +impl fmt::Debug for ClientResponse { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status(),)?; + writeln!(f, " headers:")?; + for (key, val) in self.headers().iter() { + writeln!(f, " {:?}: {:?}", key, val)?; + } + Ok(()) + } +}