mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Update main fetch
This commit is contained in:
parent
c9d9ed8e96
commit
ed9c16575c
3 changed files with 63 additions and 24 deletions
|
@ -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,16 +176,25 @@ pub fn main_fetch(request: &mut Request,
|
|||
|
||||
if (same_origin && !cors_flag ) ||
|
||||
current_url.scheme() == "data" ||
|
||||
current_url.scheme() == "file" ||
|
||||
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 {
|
||||
// Substep 1.
|
||||
request.response_tainting = ResponseTainting::Basic;
|
||||
|
||||
// Substep 2.
|
||||
basic_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;
|
||||
|
||||
// Substep 2.
|
||||
basic_fetch(request, cache, target, done_chan, context)
|
||||
|
||||
} else if !matches!(current_url.scheme(), "http" | "https") {
|
||||
|
@ -195,18 +202,24 @@ pub fn main_fetch(request: &mut Request,
|
|||
|
||||
} 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,
|
||||
|
@ -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
|
||||
|
|
|
@ -9,7 +9,8 @@ use cookie_storage::CookieStorage;
|
|||
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest};
|
||||
use devtools_traits::{HttpResponse as DevtoolsHttpResponse, NetworkEvent};
|
||||
use fetch::cors_cache::CorsCache;
|
||||
use fetch::methods::{Data, DoneChannel, FetchContext, Target, is_simple_header, is_simple_method, main_fetch};
|
||||
use fetch::methods::{Data, DoneChannel, FetchContext, Target};
|
||||
use fetch::methods::{is_cors_safelisted_request_header, is_cors_safelisted_method, main_fetch};
|
||||
use flate2::read::{DeflateDecoder, GzDecoder};
|
||||
use hsts::HstsList;
|
||||
use hyper::Error as HttpError;
|
||||
|
@ -573,10 +574,10 @@ pub fn http_fetch(request: &mut Request,
|
|||
let method_cache_match = cache.match_method(&*request,
|
||||
request.method.clone());
|
||||
|
||||
let method_mismatch = !method_cache_match && (!is_simple_method(&request.method) ||
|
||||
let method_mismatch = !method_cache_match && (!is_cors_safelisted_method(&request.method) ||
|
||||
request.use_cors_preflight);
|
||||
let header_mismatch = request.headers.iter().any(|view|
|
||||
!cache.match_header(&*request, view.name()) && !is_simple_header(&view)
|
||||
!cache.match_header(&*request, view.name()) && !is_cors_safelisted_request_header(&view)
|
||||
);
|
||||
|
||||
// Sub-substep 1
|
||||
|
@ -1269,7 +1270,7 @@ fn cors_preflight_fetch(request: &Request,
|
|||
// Step 3, 4
|
||||
let mut value = request.headers
|
||||
.iter()
|
||||
.filter(|view| !is_simple_header(view))
|
||||
.filter(|view| !is_cors_safelisted_request_header(view))
|
||||
.map(|view| UniCase(view.name().to_owned()))
|
||||
.collect::<Vec<UniCase<String>>>();
|
||||
value.sort();
|
||||
|
@ -1315,14 +1316,15 @@ fn cors_preflight_fetch(request: &Request,
|
|||
debug!("CORS check: Allowed methods: {:?}, current method: {:?}",
|
||||
methods, request.method);
|
||||
if methods.iter().all(|method| *method != request.method) &&
|
||||
!is_simple_method(&request.method) {
|
||||
!is_cors_safelisted_method(&request.method) {
|
||||
return Response::network_error(NetworkError::Internal("CORS method check failed".into()));
|
||||
}
|
||||
|
||||
// Substep 6
|
||||
debug!("CORS check: Allowed headers: {:?}, current headers: {:?}", header_names, request.headers);
|
||||
let set: HashSet<&UniCase<String>> = HashSet::from_iter(header_names.iter());
|
||||
if request.headers.iter().any(|ref hv| !set.contains(&UniCase(hv.name().to_owned())) && !is_simple_header(hv)) {
|
||||
if request.headers.iter().any(
|
||||
|ref hv| !set.contains(&UniCase(hv.name().to_owned())) && !is_cors_safelisted_request_header(hv)) {
|
||||
return Response::network_error(NetworkError::Internal("CORS headers check failed".into()));
|
||||
}
|
||||
|
||||
|
|
|
@ -102,12 +102,14 @@ pub struct Response {
|
|||
pub cache_state: CacheState,
|
||||
pub https_state: HttpsState,
|
||||
pub referrer: Option<ServoUrl>,
|
||||
pub referrer_policy: Option<ReferrerPolicy>,
|
||||
/// [CORS-exposed header-name list](https://fetch.spec.whatwg.org/#concept-response-cors-exposed-header-name-list)
|
||||
pub cors_exposed_header_name_list: Vec<String>,
|
||||
/// [Internal response](https://fetch.spec.whatwg.org/#concept-internal-response), only used if the Response
|
||||
/// is a filtered response
|
||||
pub internal_response: Option<Box<Response>>,
|
||||
/// whether or not to try to return the internal_response when asked for actual_response
|
||||
pub return_internal: bool,
|
||||
pub referrer_policy: Option<ReferrerPolicy>,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
|
@ -125,6 +127,7 @@ impl Response {
|
|||
https_state: HttpsState::None,
|
||||
referrer: None,
|
||||
referrer_policy: None,
|
||||
cors_exposed_header_name_list: vec![],
|
||||
internal_response: None,
|
||||
return_internal: true,
|
||||
}
|
||||
|
@ -151,6 +154,7 @@ impl Response {
|
|||
https_state: HttpsState::None,
|
||||
referrer: None,
|
||||
referrer_policy: None,
|
||||
cors_exposed_header_name_list: vec![],
|
||||
internal_response: None,
|
||||
return_internal: true,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue