mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Adds tests for gzip/deflate response decoding
This commit is contained in:
parent
6064ee8756
commit
2eaac7e3f9
5 changed files with 112 additions and 59 deletions
|
@ -110,9 +110,6 @@ fn load_for_consumer(load_data: LoadData,
|
||||||
Err(LoadError::MaxRedirects(url)) => {
|
Err(LoadError::MaxRedirects(url)) => {
|
||||||
send_error(url, "too many redirects".to_string(), start_chan)
|
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::Cors(url, msg)) |
|
||||||
Err(LoadError::InvalidRedirect(url, msg)) |
|
Err(LoadError::InvalidRedirect(url, msg)) |
|
||||||
Err(LoadError::Decoding(url, msg)) => {
|
Err(LoadError::Decoding(url, msg)) => {
|
||||||
|
@ -139,6 +136,23 @@ pub trait HttpResponse: Read {
|
||||||
fn headers(&self) -> &Headers;
|
fn headers(&self) -> &Headers;
|
||||||
fn status(&self) -> StatusCode;
|
fn status(&self) -> StatusCode;
|
||||||
fn status_raw(&self) -> &RawStatus;
|
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 {
|
struct WrappedHttpResponse {
|
||||||
|
@ -268,8 +282,7 @@ pub enum LoadError {
|
||||||
Ssl(Url, String),
|
Ssl(Url, String),
|
||||||
InvalidRedirect(Url, String),
|
InvalidRedirect(Url, String),
|
||||||
Decoding(Url, String),
|
Decoding(Url, String),
|
||||||
MaxRedirects(Url),
|
MaxRedirects(Url)
|
||||||
UnsupportedContentEncodings(Url)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -357,6 +370,7 @@ pub struct LoadResponse<R: HttpResponse> {
|
||||||
pub metadata: Metadata
|
pub metadata: Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<R: HttpResponse> Read for LoadResponse<R> {
|
impl<R: HttpResponse> Read for LoadResponse<R> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
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> {
|
fn new(m: Metadata, d: Decoders<R>) -> LoadResponse<R> {
|
||||||
LoadResponse { metadata: m, decoder: d }
|
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> {
|
enum Decoders<R: Read> {
|
||||||
|
@ -578,42 +615,7 @@ pub fn load<A>(mut load_data: LoadData,
|
||||||
net_event_response))).unwrap();
|
net_event_response))).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let selected_content_encoding = match response.headers().get::<ContentEncoding>() {
|
return LoadResponse::from_http_response(response, metadata)
|
||||||
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()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
components/servo/Cargo.lock
generated
1
components/servo/Cargo.lock
generated
|
@ -1053,6 +1053,7 @@ name = "net_tests"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cookie 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)",
|
||||||
"net 0.0.1",
|
"net 0.0.1",
|
||||||
|
|
|
@ -25,3 +25,4 @@ cookie = "0.1"
|
||||||
hyper = "0.6"
|
hyper = "0.6"
|
||||||
url = "0.2"
|
url = "0.2"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
|
flate2 = "0.2.0"
|
||||||
|
|
|
@ -12,18 +12,19 @@ use hyper::method::Method;
|
||||||
use hyper::http::RawStatus;
|
use hyper::http::RawStatus;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
use hyper::header::{Headers, Location, ContentLength};
|
use hyper::header::{Headers, Location, ContentLength};
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Write, Read, Cursor};
|
||||||
use std::cmp::{self};
|
use flate2::write::{GzEncoder, DeflateEncoder};
|
||||||
|
use flate2::Compression;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
fn respond_with(body: Vec<u8>) -> MockResponse {
|
fn respond_with(body: Vec<u8>) -> MockResponse {
|
||||||
let mut headers = Headers::new();
|
let mut headers = Headers::new();
|
||||||
headers.set(ContentLength(body.len() as u64));
|
respond_with_headers(body, &mut headers)
|
||||||
|
|
||||||
respond_with_headers(body, 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(
|
MockResponse::new(
|
||||||
headers.clone(),
|
headers.clone(),
|
||||||
StatusCode::Ok,
|
StatusCode::Ok,
|
||||||
|
@ -40,7 +41,7 @@ fn read_response(reader: &mut Read) -> String {
|
||||||
String::from_utf8(buf).unwrap()
|
String::from_utf8(buf).unwrap()
|
||||||
},
|
},
|
||||||
Ok(_) => "".to_string(),
|
Ok(_) => "".to_string(),
|
||||||
_ => panic!("problem reading response")
|
Err(e) => panic!("problem reading response {}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,23 +49,18 @@ struct MockResponse {
|
||||||
h: Headers,
|
h: Headers,
|
||||||
sc: StatusCode,
|
sc: StatusCode,
|
||||||
sr: RawStatus,
|
sr: RawStatus,
|
||||||
msg: Vec<u8>
|
msg: Cursor<Vec<u8>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MockResponse {
|
impl MockResponse {
|
||||||
fn new(h: Headers, sc: StatusCode, sr: RawStatus, msg: Vec<u8>) -> 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 {
|
impl Read for MockResponse {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
let buf_len = buf.len();
|
self.msg.read(buf)
|
||||||
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()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +108,8 @@ fn response_for_request_type(t: RequestType) -> Result<MockResponse, LoadError>
|
||||||
RequestType::Text(b) => {
|
RequestType::Text(b) => {
|
||||||
Ok(respond_with(b))
|
Ok(respond_with(b))
|
||||||
},
|
},
|
||||||
RequestType::WithHeaders(b, h) => {
|
RequestType::WithHeaders(b, mut h) => {
|
||||||
Ok(respond_with_headers(b, 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]
|
#[test]
|
||||||
fn test_load_doesnt_send_request_body_on_any_redirect() {
|
fn test_load_doesnt_send_request_body_on_any_redirect() {
|
||||||
struct Factory;
|
struct Factory;
|
||||||
|
@ -494,7 +542,7 @@ fn test_load_follows_a_redirect() {
|
||||||
let load_data = LoadData::new(url.clone(), None);
|
let load_data = LoadData::new(url.clone(), None);
|
||||||
|
|
||||||
match load::<MockRequest>(load_data, resource_mgr, None, &Factory) {
|
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) => {
|
Ok(mut lr) => {
|
||||||
let response = read_response(&mut lr);
|
let response = read_response(&mut lr);
|
||||||
assert_eq!(response, "Yay!".to_string());
|
assert_eq!(response, "Yay!".to_string());
|
||||||
|
|
|
@ -11,6 +11,7 @@ extern crate url;
|
||||||
extern crate util;
|
extern crate util;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
extern crate flate2;
|
||||||
|
|
||||||
#[cfg(test)] mod cookie;
|
#[cfg(test)] mod cookie;
|
||||||
#[cfg(test)] mod data_loader;
|
#[cfg(test)] mod data_loader;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue