From 4857a5917dd5445a3f5ed04edcff01b95eda7823 Mon Sep 17 00:00:00 2001 From: Paul Woolcock Date: Thu, 4 Oct 2018 20:38:26 -0400 Subject: [PATCH] From for Response (#360) This adds an implementation to convert a `Response` type from the `http` crate to the `async_impl::Response` type. This is the first step to allow us to convert `http::Response` objects to `request::Response` objects This also adds an extension trait for the `http::response::Builder` type. The `http::Response` object does not provide a way to access the "final" url that the response is derived from, so we can't easily provide that in the `From` implementation. For users who are manually constructing `http::Response` objects for use in tests, etc, they can import this extension trait, which adds a `.url()` builder method that will allow them to pass a `Url`, which we then convert to our newtype'd Url and add to the `http::Response`'s `extensions`. Then, when converting from `http::Response` to `async_impl::Response` we can pull that value out of the `extensions` and use it to construct the `async_impl::Response` Closes #333 --- src/async_impl/mod.rs | 2 +- src/async_impl/response.rs | 89 ++++++++++++++++++++++++++++++++++++++ src/client.rs | 10 ++++- src/lib.rs | 1 + src/response.rs | 7 +++ 5 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/async_impl/mod.rs b/src/async_impl/mod.rs index 5f71a97..cbeed0f 100644 --- a/src/async_impl/mod.rs +++ b/src/async_impl/mod.rs @@ -2,7 +2,7 @@ pub use self::body::{Body, Chunk}; pub use self::decoder::{Decoder, ReadableChunks}; pub use self::client::{Client, ClientBuilder}; pub use self::request::{Request, RequestBuilder}; -pub use self::response::Response; +pub use self::response::{Response, ResponseBuilderExt}; pub mod body; pub mod client; diff --git a/src/async_impl/response.rs b/src/async_impl/response.rs index 942b263..7dd11fb 100644 --- a/src/async_impl/response.rs +++ b/src/async_impl/response.rs @@ -8,6 +8,7 @@ use hyper::{HeaderMap, StatusCode, Version}; use serde::de::DeserializeOwned; use serde_json; use url::Url; +use http; use super::{decoder, body, Decoder}; @@ -144,6 +145,25 @@ impl fmt::Debug for Response { } } +impl> From> for Response { + fn from(r: http::Response) -> Response { + let (mut parts, body) = r.into_parts(); + let body = body.into(); + let body = decoder::detect(&mut parts.headers, body, false); + let url = parts.extensions + .remove::() + .unwrap_or_else(|| ResponseUrl(Url::parse("http://no.url.provided.local").unwrap())); + let url = url.0; + Response { + status: parts.status, + headers: parts.headers, + url: Box::new(url), + body: body, + version: parts.version, + } + } +} + pub struct Json { concat: Concat2, _marker: PhantomData, @@ -166,3 +186,72 @@ impl fmt::Debug for Json { } } +#[derive(Debug, Clone, PartialEq)] +struct ResponseUrl(Url); + +/// Extension trait for http::response::Builder objects +/// +/// Allows the user to add a `Url` to the http::Response +pub trait ResponseBuilderExt { + /// A builder method for the `http::response::Builder` type that allows the user to add a `Url` + /// to the `http::Response` + /// + /// # Example + /// + /// ``` + /// # extern crate url; + /// # extern crate http; + /// # extern crate reqwest; + /// # use std::error::Error; + /// use url::Url; + /// use http::response::Builder; + /// use reqwest::async::ResponseBuilderExt; + /// # fn main() -> Result<(), Box> { + /// let response = Builder::new() + /// .status(200) + /// .url(Url::parse("http://example.com")?) + /// .body(())?; + /// + /// # Ok(()) + /// # } + fn url(&mut self, url: Url) -> &mut Self; +} + +impl ResponseBuilderExt for http::response::Builder { + fn url(&mut self, url: Url) -> &mut Self { + self.extension(ResponseUrl(url)) + } +} + +#[cfg(test)] +mod tests { + use url::Url; + use http::response::Builder; + use super::{Response, ResponseUrl, ResponseBuilderExt}; + + #[test] + fn test_response_builder_ext() { + let url = Url::parse("http://example.com").unwrap(); + let response = Builder::new() + .status(200) + .url(url.clone()) + .body(()) + .unwrap(); + + assert_eq!(response.extensions().get::(), Some(&ResponseUrl(url))); + } + + #[test] + fn test_from_http_response() { + let url = Url::parse("http://example.com").unwrap(); + let response = Builder::new() + .status(200) + .url(url.clone()) + .body("foo") + .unwrap(); + let response = Response::from(response); + + assert_eq!(response.status, 200); + assert_eq!(response.url, Box::new(url)); + } +} diff --git a/src/client.rs b/src/client.rs index 19a4358..f59f777 100644 --- a/src/client.rs +++ b/src/client.rs @@ -500,7 +500,7 @@ impl ClientHandle { } }; res.map(|res| { - response::new(res, self.timeout.0, KeepCoreThreadAlive(self.inner.clone())) + response::new(res, self.timeout.0, KeepCoreThreadAlive(Some(self.inner.clone()))) }) } } @@ -517,4 +517,10 @@ impl Default for Timeout { // pub(crate) -pub struct KeepCoreThreadAlive(Arc); +pub struct KeepCoreThreadAlive(Option>); + +impl KeepCoreThreadAlive { + pub(crate) fn empty() -> KeepCoreThreadAlive { + KeepCoreThreadAlive(None) + } +} diff --git a/src/lib.rs b/src/lib.rs index 5f5042e..fe159cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -203,6 +203,7 @@ pub mod async { Request, RequestBuilder, Response, + ResponseBuilderExt, }; } diff --git a/src/response.rs b/src/response.rs index 9373945..bfe5c8d 100644 --- a/src/response.rs +++ b/src/response.rs @@ -6,6 +6,7 @@ use std::borrow::Cow; use encoding_rs::{Encoding, UTF_8}; use futures::{Async, Poll, Stream}; +use http; use mime::Mime; use serde::de::DeserializeOwned; use serde_json; @@ -336,3 +337,9 @@ pub fn new(mut res: async_impl::Response, timeout: Option, thread: Kee } } +impl> From> for Response { + fn from(r: http::Response) -> Response { + let response = async_impl::Response::from(r); + new(response, None, KeepCoreThreadAlive::empty()) + } +}