Auto merge of #9525 - nikkisquared:test_filtered_responses, r=asajeffrey

Test filtered responses and implement Cors Check Fetch step

I've been writing tests for creating filtered responses. So far I have three of the four types being made (namely, Basic, CORS, and Opaque), and just need to figure out how to make an OpaqueRedirect filtered response, since it's handled separately from the others. I will also add more tests to ensure the content of the filtered responses matches the limitations placed by the specification.

Along the way I implemented Cors Check, since it's required for the CORS filtered response. @jdm suggested I handle it in here, since it's such a small step, compared to other parts of Fetch.

Since all the tests currently pass, and I've spent a while adding the Cors Check and other pieces, I figured now would be a good time to start having it reviewed.

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.svg" height="40" alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9525)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-02-10 03:11:22 +05:30
commit f1018b84a8
7 changed files with 278 additions and 14 deletions

View file

@ -35,3 +35,4 @@ hyper = "0.7"
url = {version = "0.5.4", features = ["heap_size"]}
time = "0.1"
flate2 = "0.2.0"
unicase = "1.0"

View file

@ -2,16 +2,21 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use hyper::header::{Location};
use hyper::header::{AccessControlAllowHeaders, AccessControlAllowOrigin};
use hyper::header::{CacheControl, ContentLanguage, ContentType, Expires, LastModified};
use hyper::header::{Headers, HttpDate, Location, SetCookie, Pragma};
use hyper::server::{Handler, Listening, Server};
use hyper::server::{Request as HyperRequest, Response as HyperResponse};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use net::fetch::methods::fetch;
use net_traits::request::{Context, Referer, Request};
use net_traits::response::{Response, ResponseBody, ResponseType};
use net_traits::request::{Context, RedirectMode, Referer, Request, RequestMode};
use net_traits::response::{CacheState, Response, ResponseBody, ResponseType};
use std::cell::Cell;
use std::rc::Rc;
use url::Url;
use time::{self, Duration};
use unicase::UniCase;
use url::{Origin, OpaqueOrigin, Url};
// TODO write a struct that impls Handler for storing test values
@ -76,6 +81,180 @@ fn test_fetch_response_body_matches_const_message() {
};
}
#[test]
fn test_fetch_response_is_basic_filtered() {
static MESSAGE: &'static [u8] = b"";
let handler = move |_: HyperRequest, mut response: HyperResponse| {
response.headers_mut().set(SetCookie(vec![]));
// this header is obsoleted, so hyper doesn't implement it, but it's still covered by the spec
response.headers_mut().set_raw("Set-Cookie2", vec![]);
response.send(MESSAGE).unwrap();
};
let (mut server, url) = make_server(handler);
let origin = url.origin();
let mut request = Request::new(url, Context::Fetch, origin, false);
request.referer = Referer::NoReferer;
let wrapped_request = Rc::new(request);
let fetch_response = fetch(wrapped_request, false);
let _ = server.close();
assert!(!Response::is_network_error(&fetch_response));
assert_eq!(fetch_response.response_type, ResponseType::Basic);
let headers = fetch_response.headers;
assert!(!headers.has::<SetCookie>());
assert!(headers.get_raw("Set-Cookie2").is_none());
}
#[test]
fn test_fetch_response_is_cors_filtered() {
static MESSAGE: &'static [u8] = b"";
let handler = move |_: HyperRequest, mut response: HyperResponse| {
// this is mandatory for the Cors Check to pass
// TODO test using different url encodings with this value ie. punycode
response.headers_mut().set(AccessControlAllowOrigin::Any);
// these are the headers that should be kept after filtering
response.headers_mut().set(CacheControl(vec![]));
response.headers_mut().set(ContentLanguage(vec![]));
response.headers_mut().set(ContentType::html());
response.headers_mut().set(Expires(HttpDate(time::now() + Duration::days(1))));
response.headers_mut().set(LastModified(HttpDate(time::now())));
response.headers_mut().set(Pragma::NoCache);
// these headers should not be kept after filtering, even though they are given a pass
response.headers_mut().set(SetCookie(vec![]));
response.headers_mut().set_raw("Set-Cookie2", vec![]);
response.headers_mut().set(
AccessControlAllowHeaders(vec![
UniCase("set-cookie".to_owned()),
UniCase("set-cookie2".to_owned())
])
);
response.send(MESSAGE).unwrap();
};
let (mut server, url) = make_server(handler);
// an origin mis-match will stop it from defaulting to a basic filtered response
let origin = Origin::UID(OpaqueOrigin::new());
let mut request = Request::new(url, Context::Fetch, origin, false);
request.referer = Referer::NoReferer;
request.mode = RequestMode::CORSMode;
let wrapped_request = Rc::new(request);
let fetch_response = fetch(wrapped_request, false);
let _ = server.close();
assert!(!Response::is_network_error(&fetch_response));
assert_eq!(fetch_response.response_type, ResponseType::CORS);
let headers = fetch_response.headers;
assert!(headers.has::<CacheControl>());
assert!(headers.has::<ContentLanguage>());
assert!(headers.has::<ContentType>());
assert!(headers.has::<Expires>());
assert!(headers.has::<LastModified>());
assert!(headers.has::<Pragma>());
assert!(!headers.has::<AccessControlAllowOrigin>());
assert!(!headers.has::<SetCookie>());
assert!(headers.get_raw("Set-Cookie2").is_none());
}
#[test]
fn test_fetch_response_is_opaque_filtered() {
static MESSAGE: &'static [u8] = b"";
let handler = move |_: HyperRequest, response: HyperResponse| {
response.send(MESSAGE).unwrap();
};
let (mut server, url) = make_server(handler);
// an origin mis-match will fall through to an Opaque filtered response
let origin = Origin::UID(OpaqueOrigin::new());
let mut request = Request::new(url, Context::Fetch, origin, false);
request.referer = Referer::NoReferer;
let wrapped_request = Rc::new(request);
let fetch_response = fetch(wrapped_request, false);
let _ = server.close();
assert!(!Response::is_network_error(&fetch_response));
assert_eq!(fetch_response.response_type, ResponseType::Opaque);
assert!(fetch_response.url_list.into_inner().len() == 0);
assert!(fetch_response.url.is_none());
// this also asserts that status message is "the empty byte sequence"
assert!(fetch_response.status.is_none());
assert_eq!(fetch_response.headers, Headers::new());
match fetch_response.body.into_inner() {
ResponseBody::Empty => { },
_ => panic!()
}
match fetch_response.cache_state {
CacheState::None => { },
_ => panic!()
}
}
#[test]
fn test_fetch_response_is_opaque_redirect_filtered() {
static MESSAGE: &'static [u8] = b"";
let handler = move |request: HyperRequest, mut response: HyperResponse| {
let redirects = match request.uri {
RequestUri::AbsolutePath(url) =>
url.split("/").collect::<String>().parse::<u32>().unwrap_or(0),
RequestUri::AbsoluteUri(url) =>
url.path().unwrap().last().unwrap().split("/").collect::<String>().parse::<u32>().unwrap_or(0),
_ => panic!()
};
if redirects == 1 {
response.send(MESSAGE).unwrap();
} else {
*response.status_mut() = StatusCode::Found;
let url = format!("{}", 1);
response.headers_mut().set(Location(url.to_owned()));
}
};
let (mut server, url) = make_server(handler);
let origin = url.origin();
let mut request = Request::new(url, Context::Fetch, origin, false);
request.referer = Referer::NoReferer;
request.redirect_mode = Cell::new(RedirectMode::Manual);
let wrapped_request = Rc::new(request);
let fetch_response = fetch(wrapped_request, false);
let _ = server.close();
assert!(!Response::is_network_error(&fetch_response));
assert_eq!(fetch_response.response_type, ResponseType::OpaqueRedirect);
// this also asserts that status message is "the empty byte sequence"
assert!(fetch_response.status.is_none());
assert_eq!(fetch_response.headers, Headers::new());
match fetch_response.body.into_inner() {
ResponseBody::Empty => { },
_ => panic!()
}
match fetch_response.cache_state {
CacheState::None => { },
_ => panic!()
}
}
fn test_fetch_redirect_count(message: &'static [u8], redirect_cap: u32) -> Response {
let handler = move |request: HyperRequest, mut response: HyperResponse| {

View file

@ -14,6 +14,7 @@ extern crate msg;
extern crate net;
extern crate net_traits;
extern crate time;
extern crate unicase;
extern crate url;
extern crate util;