Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update fetch methods #17521

Merged
merged 10 commits into from Aug 19, 2017
@@ -103,6 +103,7 @@ impl NetworkListener {

self.res_init = Some(ResponseInit {
url: metadata.final_url.clone(),
location_url: metadata.location_url.clone(),
headers: headers.clone().into_inner(),
referrer: metadata.referrer.clone(),
});
@@ -10,14 +10,14 @@ use filemanager_thread::FileManager;
use http_loader::{HttpState, determine_request_referrer, http_fetch};
use http_loader::{set_default_accept, set_default_accept_language};
use hyper::{Error, Result as HyperResult};
use hyper::header::{Accept, AcceptLanguage, ContentLanguage, ContentType};
use hyper::header::{Accept, AcceptLanguage, AccessControlExposeHeaders, ContentLanguage, ContentType};
use hyper::header::{Header, HeaderFormat, HeaderView, Headers, Referer as RefererHeader};
use hyper::method::Method;
use hyper::mime::{Mime, SubLevel, TopLevel};
use hyper::status::StatusCode;
use mime_guess::guess_mime_type;
use net_traits::{FetchTaskTarget, NetworkError, ReferrerPolicy};
use net_traits::request::{Referrer, Request, RequestMode, ResponseTainting};
use net_traits::request::{CredentialsMode, Referrer, Request, RequestMode, ResponseTainting};
use net_traits::request::{Type, Origin, Window};
use net_traits::response::{Response, ResponseBody, ResponseType};
use servo_url::ServoUrl;
@@ -107,9 +107,8 @@ pub fn main_fetch(request: &mut Request,

// Step 2.
if request.local_urls_only {
match request.current_url().scheme() {
"about" | "blob" | "data" | "filesystem" => (), // Ok, the URL is local.
_ => response = Some(Response::network_error(NetworkError::Internal("Non-local scheme".into())))
if !matches!(request.current_url().scheme(), "about" | "blob" | "data" | "filesystem") {
response = Some(Response::network_error(NetworkError::Internal("Non-local scheme".into())));
}
}

@@ -130,8 +129,7 @@ pub fn main_fetch(request: &mut Request,
// TODO: handle request's client's referrer policy.

// Step 7.
let referrer_policy = request.referrer_policy.unwrap_or(ReferrerPolicy::NoReferrerWhenDowngrade);
request.referrer_policy = Some(referrer_policy);
request.referrer_policy = request.referrer_policy.or(Some(ReferrerPolicy::NoReferrerWhenDowngrade));

// Step 8.
{
@@ -147,7 +145,7 @@ pub fn main_fetch(request: &mut Request,
request.headers.remove::<RefererHeader>();
let current_url = request.current_url().clone();
determine_request_referrer(&mut request.headers,
referrer_policy,
request.referrer_policy.unwrap(),
url,
current_url)
}
@@ -168,7 +166,7 @@ pub fn main_fetch(request: &mut Request,
// Not applicable: see fetch_async.

// Step 12.
let response = response.unwrap_or_else(|| {
let mut response = response.unwrap_or_else(|| {
let current_url = request.current_url();
let same_origin = if let Origin::Origin(ref origin) = request.origin {
*origin == current_url.origin()
@@ -178,35 +176,50 @@ pub fn main_fetch(request: &mut Request,

if (same_origin && !cors_flag ) ||
current_url.scheme() == "data" ||
current_url.scheme() == "file" ||

This comment has been minimized.

Copy link
@jdm

jdm Jun 26, 2017

Member

I am 99% sure this is going to hit the same problem as #14515 (comment) .

This comment has been minimized.

Copy link
@KiChjang

KiChjang Jun 26, 2017

Author Member

How unfortunate. Looks like I'll have to drop those changes then.

current_url.scheme() == "file" || // FIXME: Fetch spec has already dropped filtering against file:
// and about: schemes, but CSS tests will break on loading Ahem
// since we load them through a file: URL.
current_url.scheme() == "about" ||
request.mode == RequestMode::Navigate {
basic_fetch(request, cache, target, done_chan, context)
// Substep 1.
request.response_tainting = ResponseTainting::Basic;

// Substep 2.
scheme_fetch(request, cache, target, done_chan, context)

} else if request.mode == RequestMode::SameOrigin {
Response::network_error(NetworkError::Internal("Cross-origin response".into()))

} else if request.mode == RequestMode::NoCors {
// Substep 1.
request.response_tainting = ResponseTainting::Opaque;
basic_fetch(request, cache, target, done_chan, context)

// Substep 2.
scheme_fetch(request, cache, target, done_chan, context)

} else if !matches!(current_url.scheme(), "http" | "https") {
Response::network_error(NetworkError::Internal("Non-http scheme".into()))

} else if request.use_cors_preflight ||
(request.unsafe_request &&
(!is_simple_method(&request.method) ||
request.headers.iter().any(|h| !is_simple_header(&h)))) {
(!is_cors_safelisted_method(&request.method) ||
request.headers.iter().any(|h| !is_cors_safelisted_request_header(&h)))) {
// Substep 1.
request.response_tainting = ResponseTainting::CorsTainting;
// Substep 2.
let response = http_fetch(request, cache, true, true, false,
target, done_chan, context);
// Substep 3.
if response.is_network_error() {
// TODO clear cache entries using request
}
// Substep 4.
response

} else {
// Substep 1.
request.response_tainting = ResponseTainting::CorsTainting;
// Substep 2.
http_fetch(request, cache, true, false, false, target, done_chan, context)
}
});
@@ -217,9 +230,28 @@ pub fn main_fetch(request: &mut Request,
}

// Step 14.
// We don't need to check whether response is a network error,
// given its type would not be `Default` in this case.
let mut response = if response.response_type == ResponseType::Default {
let mut response = if !response.is_network_error() && response.internal_response.is_none() {
// Substep 1.
if request.response_tainting == ResponseTainting::CorsTainting {
// Subsubstep 1.
let header_names = response.headers.get::<AccessControlExposeHeaders>();
match header_names {
// Subsubstep 2.
Some(list) if request.credentials_mode != CredentialsMode::Include => {
if list.len() == 1 && list[0] == "*" {
response.cors_exposed_header_name_list =
response.headers.iter().map(|h| h.name().to_owned()).collect();
}
},
// Subsubstep 3.
Some(list) => {
response.cors_exposed_header_name_list = list.iter().map(|h| (**h).clone()).collect();
},
_ => (),
}
}

// Substep 2.
let response_type = match request.response_tainting {
ResponseTainting::Basic => ResponseType::Basic,
ResponseTainting::CorsTainting => ResponseType::Cors,
@@ -365,7 +397,7 @@ fn wait_for_response(response: &Response, target: Target, done_chan: &mut DoneCh
let body = response.body.lock().unwrap();
if let ResponseBody::Done(ref vec) = *body {
// in case there was no channel to wait for, the body was
// obtained synchronously via basic_fetch for data/file/about/etc
// obtained synchronously via scheme_fetch for data/file/about/etc
// We should still send the body across as a chunk
target.process_response_chunk(vec.clone());
} else {
@@ -374,8 +406,8 @@ fn wait_for_response(response: &Response, target: Target, done_chan: &mut DoneCh
}
}

/// [Basic fetch](https://fetch.spec.whatwg.org#basic-fetch)
fn basic_fetch(request: &mut Request,
/// [Scheme fetch](https://fetch.spec.whatwg.org#scheme-fetch)
fn scheme_fetch(request: &mut Request,
cache: &mut CorsCache,
target: Target,
done_chan: &mut DoneChannel,
@@ -463,7 +495,7 @@ fn basic_fetch(request: &mut Request,
}

/// https://fetch.spec.whatwg.org/#cors-safelisted-request-header
pub fn is_simple_header(h: &HeaderView) -> bool {
pub fn is_cors_safelisted_request_header(h: &HeaderView) -> bool {
if h.is::<ContentType>() {
match h.value() {
Some(&ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) |
@@ -477,7 +509,8 @@ pub fn is_simple_header(h: &HeaderView) -> bool {
}
}

pub fn is_simple_method(m: &Method) -> bool {
/// https://fetch.spec.whatwg.org/#cors-safelisted-method
pub fn is_cors_safelisted_method(m: &Method) -> bool {
match *m {
Method::Get | Method::Head | Method::Post => true,
_ => false
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.