From<http::Response> 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<http::Response>` 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
This commit is contained in:
Paul Woolcock
2018-10-04 20:38:26 -04:00
committed by Sean McArthur
parent 22fa725f48
commit 4857a5917d
5 changed files with 106 additions and 3 deletions

View File

@@ -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<T: Into<body::Body>> From<http::Response<T>> for Response {
fn from(r: http::Response<T>) -> 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::<ResponseUrl>()
.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<T> {
concat: Concat2<Decoder>,
_marker: PhantomData<T>,
@@ -166,3 +186,72 @@ impl<T> fmt::Debug for Json<T> {
}
}
#[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<Error>> {
/// 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::<ResponseUrl>(), 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));
}
}