Adds tests for gzip/deflate response decoding

This commit is contained in:
Sam Gibson 2015-08-16 17:16:10 +10:00
parent 6064ee8756
commit 2eaac7e3f9
5 changed files with 112 additions and 59 deletions

View file

@ -110,9 +110,6 @@ fn load_for_consumer(load_data: LoadData,
Err(LoadError::MaxRedirects(url)) => {
send_error(url, "too many redirects".to_string(), start_chan)
}
Err(LoadError::UnsupportedContentEncodings(url)) => {
send_error(url, "no valid content encodings supported".to_string(), start_chan)
}
Err(LoadError::Cors(url, msg)) |
Err(LoadError::InvalidRedirect(url, msg)) |
Err(LoadError::Decoding(url, msg)) => {
@ -139,6 +136,23 @@ pub trait HttpResponse: Read {
fn headers(&self) -> &Headers;
fn status(&self) -> StatusCode;
fn status_raw(&self) -> &RawStatus;
fn content_encoding(&self) -> Option<Encoding> {
match self.headers().get::<ContentEncoding>() {
Some(&ContentEncoding(ref encodings)) => {
if encodings.contains(&Encoding::Gzip) {
Some(Encoding::Gzip)
} else if encodings.contains(&Encoding::Deflate) {
Some(Encoding::Deflate)
} else {
// TODO: Is this the correct behaviour?
None
}
}
None => None
}
}
}
struct WrappedHttpResponse {
@ -268,8 +282,7 @@ pub enum LoadError {
Ssl(Url, String),
InvalidRedirect(Url, String),
Decoding(Url, String),
MaxRedirects(Url),
UnsupportedContentEncodings(Url)
MaxRedirects(Url)
}
#[inline(always)]
@ -357,6 +370,7 @@ pub struct LoadResponse<R: HttpResponse> {
pub metadata: Metadata
}
impl<R: HttpResponse> Read for LoadResponse<R> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
@ -372,6 +386,29 @@ impl<R: HttpResponse> LoadResponse<R> {
fn new(m: Metadata, d: Decoders<R>) -> LoadResponse<R> {
LoadResponse { metadata: m, decoder: d }
}
fn from_http_response(response: R, m: Metadata) -> Result<LoadResponse<R>, LoadError> {
match response.content_encoding() {
Some(Encoding::Gzip) => {
let result = GzDecoder::new(response);
match result {
Ok(response_decoding) => {
return Ok(LoadResponse::new(m, Decoders::Gzip(response_decoding)));
}
Err(err) => {
return Err(LoadError::Decoding(m.final_url, err.to_string()));
}
}
}
Some(Encoding::Deflate) => {
let response_decoding = DeflateDecoder::new(response);
return Ok(LoadResponse::new(m, Decoders::Deflate(response_decoding)));
}
_ => {
return Ok(LoadResponse::new(m, Decoders::Plain(response)));
}
}
}
}
enum Decoders<R: Read> {
@ -578,42 +615,7 @@ pub fn load<A>(mut load_data: LoadData,
net_event_response))).unwrap();
}
let selected_content_encoding = match response.headers().get::<ContentEncoding>() {
Some(&ContentEncoding(ref encodings)) => {
if encodings.contains(&Encoding::Gzip) {
Some(Encoding::Gzip)
} else if encodings.contains(&Encoding::Deflate) {
Some(Encoding::Deflate)
} else {
return Err(LoadError::UnsupportedContentEncodings(url.clone()))
}
}
None => {
None
}
};
match selected_content_encoding {
Some(Encoding::Gzip) => {
let result = GzDecoder::new(response);
match result {
Ok(response_decoding) => {
return Ok(LoadResponse::new(metadata, Decoders::Gzip(response_decoding)));
}
Err(err) => {
return Err(LoadError::Decoding(metadata.final_url, err.to_string()));
}
}
}
Some(Encoding::Deflate) => {
let response_decoding = DeflateDecoder::new(response);
return Ok(LoadResponse::new(metadata, Decoders::Deflate(response_decoding)));
}
None => {
return Ok(LoadResponse::new(metadata, Decoders::Plain(response)));
}
_ => return Err(LoadError::UnsupportedContentEncodings(url.clone()))
}
return LoadResponse::from_http_response(response, metadata)
}
}

View file

@ -1053,6 +1053,7 @@ name = "net_tests"
version = "0.0.1"
dependencies = [
"cookie 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)",
"net 0.0.1",

View file

@ -25,3 +25,4 @@ cookie = "0.1"
hyper = "0.6"
url = "0.2"
time = "0.1"
flate2 = "0.2.0"

View file

@ -12,18 +12,19 @@ use hyper::method::Method;
use hyper::http::RawStatus;
use hyper::status::StatusCode;
use hyper::header::{Headers, Location, ContentLength};
use std::io::{self, Read};
use std::cmp::{self};
use std::io::{self, Write, Read, Cursor};
use flate2::write::{GzEncoder, DeflateEncoder};
use flate2::Compression;
use std::borrow::Cow;
fn respond_with(body: Vec<u8>) -> MockResponse {
let mut headers = Headers::new();
headers.set(ContentLength(body.len() as u64));
respond_with_headers(body, headers)
respond_with_headers(body, &mut headers)
}
fn respond_with_headers(body: Vec<u8>, headers: Headers) -> MockResponse {
fn respond_with_headers(body: Vec<u8>, headers: &mut Headers) -> MockResponse {
headers.set(ContentLength(body.len() as u64));
MockResponse::new(
headers.clone(),
StatusCode::Ok,
@ -40,7 +41,7 @@ fn read_response(reader: &mut Read) -> String {
String::from_utf8(buf).unwrap()
},
Ok(_) => "".to_string(),
_ => panic!("problem reading response")
Err(e) => panic!("problem reading response {}", e)
}
}
@ -48,23 +49,18 @@ struct MockResponse {
h: Headers,
sc: StatusCode,
sr: RawStatus,
msg: Vec<u8>
msg: Cursor<Vec<u8>>
}
impl MockResponse {
fn new(h: Headers, sc: StatusCode, sr: RawStatus, msg: Vec<u8>) -> MockResponse {
MockResponse { h: h, sc: sc, sr: sr, msg: msg }
MockResponse { h: h, sc: sc, sr: sr, msg: Cursor::new(msg) }
}
}
impl Read for MockResponse {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let buf_len = buf.len();
for (a, b) in buf.iter_mut().zip(&self.msg[0 .. cmp::min(buf_len, self.msg.len())]) {
*a = *b
}
Ok(cmp::min(buf.len(), self.msg.len()))
self.msg.read(buf)
}
}
@ -112,8 +108,8 @@ fn response_for_request_type(t: RequestType) -> Result<MockResponse, LoadError>
RequestType::Text(b) => {
Ok(respond_with(b))
},
RequestType::WithHeaders(b, h) => {
Ok(respond_with_headers(b, h))
RequestType::WithHeaders(b, mut h) => {
Ok(respond_with_headers(b, &mut h))
}
}
}
@ -212,6 +208,58 @@ impl HttpRequest for AssertMustHaveBodyRequest {
}
}
#[test]
fn test_load_should_decode_the_response_as_deflate_when_response_headers_have_content_encoding_deflate() {
struct Factory;
impl HttpRequestFactory for Factory {
type R=MockRequest;
fn create(&self, _: Url, _: Method) -> Result<MockRequest, LoadError> {
let mut e = DeflateEncoder::new(Vec::new(), Compression::Default);
e.write(b"Yay!").unwrap();
let encoded_content = e.finish().unwrap();
let mut headers = Headers::new();
headers.set_raw("Content-Encoding", vec![b"deflate".to_vec()]);
Ok(MockRequest::new(RequestType::WithHeaders(encoded_content, headers)))
}
}
let url = Url::parse("http://mozilla.com").unwrap();
let resource_mgr = new_resource_task(None, None);
let load_data = LoadData::new(url.clone(), None);
let mut response = load::<MockRequest>(load_data, resource_mgr.clone(), None, &Factory).unwrap();
assert_eq!(read_response(&mut response), "Yay!");
}
#[test]
fn test_load_should_decode_the_response_as_gzip_when_response_headers_have_content_encoding_gzip() {
struct Factory;
impl HttpRequestFactory for Factory {
type R=MockRequest;
fn create(&self, _: Url, _: Method) -> Result<MockRequest, LoadError> {
let mut e = GzEncoder::new(Vec::new(), Compression::Default);
e.write(b"Yay!").unwrap();
let encoded_content = e.finish().unwrap();
let mut headers = Headers::new();
headers.set_raw("Content-Encoding", vec![b"gzip".to_vec()]);
Ok(MockRequest::new(RequestType::WithHeaders(encoded_content, headers)))
}
}
let url = Url::parse("http://mozilla.com").unwrap();
let resource_mgr = new_resource_task(None, None);
let load_data = LoadData::new(url.clone(), None);
let mut response = load::<MockRequest>(load_data, resource_mgr.clone(), None, &Factory).unwrap();
assert_eq!(read_response(&mut response), "Yay!");
}
#[test]
fn test_load_doesnt_send_request_body_on_any_redirect() {
struct Factory;
@ -494,7 +542,7 @@ fn test_load_follows_a_redirect() {
let load_data = LoadData::new(url.clone(), None);
match load::<MockRequest>(load_data, resource_mgr, None, &Factory) {
Err(_) => panic!("expected to follow a redirect"),
Err(e) => panic!("expected to follow a redirect {:?}", e),
Ok(mut lr) => {
let response = read_response(&mut lr);
assert_eq!(response, "Yay!".to_string());

View file

@ -11,6 +11,7 @@ extern crate url;
extern crate util;
extern crate time;
extern crate hyper;
extern crate flate2;
#[cfg(test)] mod cookie;
#[cfg(test)] mod data_loader;