Add Response::text() to async Client
This commit is contained in:
@@ -2,13 +2,16 @@ use std::fmt;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use encoding_rs::{Encoding, UTF_8};
|
||||||
use futures::{Async, Future, Poll, Stream};
|
use futures::{Async, Future, Poll, Stream};
|
||||||
use futures::stream::Concat2;
|
use futures::stream::Concat2;
|
||||||
use http;
|
use http;
|
||||||
use hyper::{HeaderMap, StatusCode, Version};
|
use hyper::{HeaderMap, StatusCode, Version};
|
||||||
use hyper::client::connect::HttpInfo;
|
use hyper::client::connect::HttpInfo;
|
||||||
use hyper::header::{CONTENT_LENGTH};
|
use hyper::header::{CONTENT_LENGTH};
|
||||||
|
use mime::Mime;
|
||||||
use tokio::timer::Delay;
|
use tokio::timer::Delay;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
@@ -135,6 +138,35 @@ impl Response {
|
|||||||
self.version
|
self.version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the response text
|
||||||
|
pub fn text(&mut self) -> impl Future<Item = String, Error = ::Error> {
|
||||||
|
self.text_with_charset("utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the response text given a specific encoding
|
||||||
|
pub fn text_with_charset(&mut self, default_encoding: &str) -> impl Future<Item = String, Error = ::Error> {
|
||||||
|
let body = mem::replace(&mut self.body, Decoder::empty());
|
||||||
|
let content_type = self.headers.get(::header::CONTENT_TYPE)
|
||||||
|
.and_then(|value| {
|
||||||
|
value.to_str().ok()
|
||||||
|
})
|
||||||
|
.and_then(|value| {
|
||||||
|
value.parse::<Mime>().ok()
|
||||||
|
});
|
||||||
|
let encoding_name = content_type
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|mime| {
|
||||||
|
mime
|
||||||
|
.get_param("charset")
|
||||||
|
.map(|charset| charset.as_str())
|
||||||
|
})
|
||||||
|
.unwrap_or(default_encoding);
|
||||||
|
let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
|
||||||
|
Text {
|
||||||
|
concat: body.concat2(),
|
||||||
|
encoding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Try to deserialize the response body as JSON using `serde`.
|
/// Try to deserialize the response body as JSON using `serde`.
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -261,6 +293,33 @@ impl<T> fmt::Debug for Json<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Text {
|
||||||
|
concat: Concat2<Decoder>,
|
||||||
|
encoding: &'static Encoding,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for Text {
|
||||||
|
type Item = String;
|
||||||
|
type Error = ::Error;
|
||||||
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
|
let bytes = try_ready!(self.concat.poll());
|
||||||
|
// a block because of borrow checker
|
||||||
|
{
|
||||||
|
let (text, _, _) = self.encoding.decode(&bytes);
|
||||||
|
match text {
|
||||||
|
Cow::Owned(s) => return Ok(Async::Ready(s)),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
// decoding returned Cow::Borrowed, meaning these bytes
|
||||||
|
// are already valid utf8
|
||||||
|
Ok(Async::Ready(String::from_utf8_unchecked(bytes.to_vec())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
struct ResponseUrl(Url);
|
struct ResponseUrl(Url);
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,42 @@ fn gzip_single_byte_chunks() {
|
|||||||
gzip_case(10, 1);
|
gzip_case(10, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn response_text() {
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
|
||||||
|
let server = server! {
|
||||||
|
request: b"\
|
||||||
|
GET /text HTTP/1.1\r\n\
|
||||||
|
user-agent: $USERAGENT\r\n\
|
||||||
|
accept: */*\r\n\
|
||||||
|
accept-encoding: gzip\r\n\
|
||||||
|
host: $HOST\r\n\
|
||||||
|
\r\n\
|
||||||
|
",
|
||||||
|
response: b"\
|
||||||
|
HTTP/1.1 200 OK\r\n\
|
||||||
|
Content-Length: 5\r\n\
|
||||||
|
\r\n\
|
||||||
|
Hello\
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rt = Runtime::new().expect("new rt");
|
||||||
|
|
||||||
|
let client = Client::new();
|
||||||
|
|
||||||
|
let res_future = client.get(&format!("http://{}/text", server.addr()))
|
||||||
|
.send()
|
||||||
|
.and_then(|mut res| res.text())
|
||||||
|
.and_then(|text| {
|
||||||
|
assert_eq!("Hello", text);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
rt.block_on(res_future).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multipart() {
|
fn multipart() {
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
|
|||||||
Reference in New Issue
Block a user