Store the NetworkError in network error Responses.

This commit is contained in:
Ms2ger 2016-10-19 15:11:12 +02:00
parent 6af63d6af1
commit 784c45a306
3 changed files with 69 additions and 55 deletions

View file

@ -11,7 +11,7 @@ use filemanager_thread::{FileManager, UIProvider};
use http_loader::{HttpState, set_default_accept_encoding, set_request_cookies}; use http_loader::{HttpState, set_default_accept_encoding, set_request_cookies};
use http_loader::{NetworkHttpRequestFactory, ReadResult, StreamedResponse, obtain_response, read_block}; use http_loader::{NetworkHttpRequestFactory, ReadResult, StreamedResponse, obtain_response, read_block};
use http_loader::{auth_from_cache, determine_request_referrer}; use http_loader::{auth_from_cache, determine_request_referrer};
use http_loader::{send_response_to_devtools, send_request_to_devtools}; use http_loader::{send_response_to_devtools, send_request_to_devtools, LoadErrorType};
use hyper::header::{Accept, AcceptLanguage, Authorization, AccessControlAllowCredentials}; use hyper::header::{Accept, AcceptLanguage, Authorization, AccessControlAllowCredentials};
use hyper::header::{AccessControlAllowOrigin, AccessControlAllowHeaders, AccessControlAllowMethods}; use hyper::header::{AccessControlAllowOrigin, AccessControlAllowHeaders, AccessControlAllowMethods};
use hyper::header::{AccessControlRequestHeaders, AccessControlMaxAge, AccessControlRequestMethod, Basic}; use hyper::header::{AccessControlRequestHeaders, AccessControlMaxAge, AccessControlRequestMethod, Basic};
@ -24,7 +24,7 @@ use hyper::status::StatusCode;
use hyper_serde::Serde; use hyper_serde::Serde;
use mime_guess::guess_mime_type; use mime_guess::guess_mime_type;
use msg::constellation_msg::ReferrerPolicy; use msg::constellation_msg::ReferrerPolicy;
use net_traits::{FetchTaskTarget, FetchMetadata}; use net_traits::{FetchTaskTarget, FetchMetadata, NetworkError};
use net_traits::request::{CacheMode, CredentialsMode, Destination}; use net_traits::request::{CacheMode, CredentialsMode, Destination};
use net_traits::request::{RedirectMode, Referrer, Request, RequestMode, ResponseTainting}; use net_traits::request::{RedirectMode, Referrer, Request, RequestMode, ResponseTainting};
use net_traits::request::{Type, Origin, Window}; use net_traits::request::{Type, Origin, Window};
@ -33,6 +33,7 @@ use net_traits::response::{Response, ResponseBody, ResponseType};
use resource_thread::CancellationListener; use resource_thread::CancellationListener;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::iter::FromIterator; use std::iter::FromIterator;
@ -155,7 +156,7 @@ fn main_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
if request.local_urls_only { if request.local_urls_only {
match request.current_url().scheme() { match request.current_url().scheme() {
"about" | "blob" | "data" | "filesystem" => (), // Ok, the URL is local. "about" | "blob" | "data" | "filesystem" => (), // Ok, the URL is local.
_ => response = Some(Response::network_error()) _ => response = Some(Response::network_error(NetworkError::Internal("Non-local scheme".into())))
} }
} }
@ -214,14 +215,14 @@ fn main_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
basic_fetch(request.clone(), cache, target, done_chan, context) basic_fetch(request.clone(), cache, target, done_chan, context)
} else if request.mode == RequestMode::SameOrigin { } else if request.mode == RequestMode::SameOrigin {
Response::network_error() Response::network_error(NetworkError::Internal("Cross-origin response".into()))
} else if request.mode == RequestMode::NoCORS { } else if request.mode == RequestMode::NoCORS {
request.response_tainting.set(ResponseTainting::Opaque); request.response_tainting.set(ResponseTainting::Opaque);
basic_fetch(request.clone(), cache, target, done_chan, context) basic_fetch(request.clone(), cache, target, done_chan, context)
} else if !matches!(current_url.scheme(), "http" | "https") { } else if !matches!(current_url.scheme(), "http" | "https") {
Response::network_error() Response::network_error(NetworkError::Internal("Non-http scheme".into()))
} else if request.use_cors_preflight || } else if request.use_cors_preflight ||
(request.unsafe_request && (request.unsafe_request &&
@ -263,8 +264,9 @@ fn main_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
{ {
// Step 14 // Step 14
let network_error_res = Response::network_error(); let network_error_res;
let internal_response = if response.is_network_error() { let internal_response = if let Some(error) = response.get_network_error() {
network_error_res = Response::network_error(error.clone());
&network_error_res &network_error_res
} else { } else {
response.actual_response() response.actual_response()
@ -434,10 +436,10 @@ fn basic_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
response.headers.set(ContentType(mime)); response.headers.set(ContentType(mime));
response response
}, },
Err(_) => Response::network_error() Err(_) => Response::network_error(NetworkError::Internal("Decoding data URL failed".into()))
} }
} else { } else {
Response::network_error() Response::network_error(NetworkError::Internal("Unexpected method for data".into()))
} }
}, },
@ -445,23 +447,26 @@ fn basic_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
if *request.method.borrow() == Method::Get { if *request.method.borrow() == Method::Get {
match url.to_file_path() { match url.to_file_path() {
Ok(file_path) => { Ok(file_path) => {
File::open(file_path.clone()).ok().map_or(Response::network_error(), |mut file| { match File::open(file_path.clone()) {
let mut bytes = vec![]; Ok(mut file) => {
let _ = file.read_to_end(&mut bytes); let mut bytes = vec![];
let mime = guess_mime_type(file_path); let _ = file.read_to_end(&mut bytes);
let mime = guess_mime_type(file_path);
let mut response = Response::new(); let mut response = Response::new();
// https://github.com/whatwg/fetch/issues/312 // https://github.com/whatwg/fetch/issues/312
response.url = Some(url.clone()); response.url = Some(url.clone());
*response.body.lock().unwrap() = ResponseBody::Done(bytes); *response.body.lock().unwrap() = ResponseBody::Done(bytes);
response.headers.set(ContentType(mime)); response.headers.set(ContentType(mime));
response response
}) },
_ => Response::network_error(NetworkError::Internal("Opening file failed".into())),
}
}, },
_ => Response::network_error() _ => Response::network_error(NetworkError::Internal("Constructing file path failed".into()))
} }
} else { } else {
Response::network_error() Response::network_error(NetworkError::Internal("Unexpected method for file".into()))
} }
}, },
@ -469,7 +474,7 @@ fn basic_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
println!("Loading blob {}", url.as_str()); println!("Loading blob {}", url.as_str());
// Step 2. // Step 2.
if *request.method.borrow() != Method::Get { if *request.method.borrow() != Method::Get {
return Response::network_error(); return Response::network_error(NetworkError::Internal("Unexpected method for blob".into()));
} }
match load_blob_sync(url.clone(), context.filemanager.clone()) { match load_blob_sync(url.clone(), context.filemanager.clone()) {
@ -482,7 +487,7 @@ fn basic_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
}, },
Err(e) => { Err(e) => {
debug!("Failed to load {}: {:?}", url, e); debug!("Failed to load {}: {:?}", url, e);
Response::network_error() Response::network_error(e)
}, },
} }
}, },
@ -492,7 +497,7 @@ fn basic_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
panic!("Unimplemented scheme for Fetch") panic!("Unimplemented scheme for Fetch")
}, },
_ => Response::network_error() _ => Response::network_error(NetworkError::Internal("Unexpected scheme".into()))
} }
} }
@ -530,8 +535,8 @@ fn http_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
request.redirect_mode.get() != RedirectMode::Manual) || request.redirect_mode.get() != RedirectMode::Manual) ||
(res.url_list.borrow().len() > 1 && (res.url_list.borrow().len() > 1 &&
request.redirect_mode.get() != RedirectMode::Follow) || request.redirect_mode.get() != RedirectMode::Follow) ||
res.response_type == ResponseType::Error { res.is_network_error() {
return Response::network_error(); return Response::network_error(NetworkError::Internal("Request failed".into()));
} }
// Substep 4 // Substep 4
@ -563,8 +568,8 @@ fn http_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
if method_mismatch || header_mismatch { if method_mismatch || header_mismatch {
let preflight_result = cors_preflight_fetch(request.clone(), cache, context); let preflight_result = cors_preflight_fetch(request.clone(), cache, context);
// Sub-substep 2 // Sub-substep 2
if preflight_result.response_type == ResponseType::Error { if let Some(e) = preflight_result.get_network_error() {
return Response::network_error(); return Response::network_error(e.clone());
} }
} }
} }
@ -578,7 +583,7 @@ fn http_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
// Substep 4 // Substep 4
if cors_flag && cors_check(request.clone(), &fetch_result).is_err() { if cors_flag && cors_check(request.clone(), &fetch_result).is_err() {
return Response::network_error(); return Response::network_error(NetworkError::Internal("CORS check failed".into()));
} }
fetch_result.return_internal.set(false); fetch_result.return_internal.set(false);
@ -597,7 +602,7 @@ fn http_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
Some(StatusCode::TemporaryRedirect) | Some(StatusCode::TemporaryRedirect) |
Some(StatusCode::PermanentRedirect) => { Some(StatusCode::PermanentRedirect) => {
response = match request.redirect_mode.get() { response = match request.redirect_mode.get() {
RedirectMode::Error => Response::network_error(), RedirectMode::Error => Response::network_error(NetworkError::Internal("Redirect mode error".into())),
RedirectMode::Manual => { RedirectMode::Manual => {
response.to_filtered(ResponseType::OpaqueRedirect) response.to_filtered(ResponseType::OpaqueRedirect)
}, },
@ -691,13 +696,13 @@ fn http_redirect_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
// Step 3 // Step 3
let location = match response.actual_response().headers.get::<Location>() { let location = match response.actual_response().headers.get::<Location>() {
Some(&Location(ref location)) => location.clone(), Some(&Location(ref location)) => location.clone(),
_ => return Response::network_error() _ => return Response::network_error(NetworkError::Internal("Location header parsing failure".into()))
}; };
let response_url = response.actual_response().url.as_ref().unwrap(); let response_url = response.actual_response().url.as_ref().unwrap();
let location_url = response_url.join(&*location); let location_url = response_url.join(&*location);
let location_url = match location_url { let location_url = match location_url {
Ok(url) => url, Ok(url) => url,
_ => return Response::network_error() _ => return Response::network_error(NetworkError::Internal("Location URL parsing failure".into()))
}; };
// Step 4 // Step 4
@ -705,7 +710,7 @@ fn http_redirect_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
// Step 5 // Step 5
if request.redirect_count.get() >= 20 { if request.redirect_count.get() >= 20 {
return Response::network_error(); return Response::network_error(NetworkError::Internal("Too many redirects".into()));
} }
// Step 6 // Step 6
@ -720,12 +725,12 @@ fn http_redirect_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
let has_credentials = has_credentials(&location_url); let has_credentials = has_credentials(&location_url);
if request.mode == RequestMode::CORSMode && !same_origin && has_credentials { if request.mode == RequestMode::CORSMode && !same_origin && has_credentials {
return Response::network_error(); return Response::network_error(NetworkError::Internal("Cross-origin credentials check failed".into()));
} }
// Step 8 // Step 8
if cors_flag && has_credentials { if cors_flag && has_credentials {
return Response::network_error(); return Response::network_error(NetworkError::Internal("Credentials check failed".into()));
} }
// Step 9 // Step 9
@ -1194,7 +1199,7 @@ fn cors_preflight_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
match response.headers.get::<AccessControlAllowMethods>() { match response.headers.get::<AccessControlAllowMethods>() {
Some(&AccessControlAllowMethods(ref m)) => m.clone(), Some(&AccessControlAllowMethods(ref m)) => m.clone(),
// Substep 3 // Substep 3
None => return Response::network_error() None => return Response::network_error(NetworkError::Internal("CORS ACAM check failed".into()))
} }
} else { } else {
vec![] vec![]
@ -1205,7 +1210,7 @@ fn cors_preflight_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
match response.headers.get::<AccessControlAllowHeaders>() { match response.headers.get::<AccessControlAllowHeaders>() {
Some(&AccessControlAllowHeaders(ref hn)) => hn.clone(), Some(&AccessControlAllowHeaders(ref hn)) => hn.clone(),
// Substep 3 // Substep 3
None => return Response::network_error() None => return Response::network_error(NetworkError::Internal("CORS ACAH check failed".into()))
} }
} else { } else {
vec![] vec![]
@ -1221,7 +1226,7 @@ fn cors_preflight_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
methods, request.method.borrow()); methods, request.method.borrow());
if methods.iter().all(|method| *method != *request.method.borrow()) && if methods.iter().all(|method| *method != *request.method.borrow()) &&
!is_simple_method(&*request.method.borrow()) { !is_simple_method(&*request.method.borrow()) {
return Response::network_error(); return Response::network_error(NetworkError::Internal("CORS method check failed".into()));
} }
// Substep 6 // Substep 6
@ -1230,7 +1235,7 @@ fn cors_preflight_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
let set: HashSet<&UniCase<String>> = HashSet::from_iter(header_names.iter()); let set: HashSet<&UniCase<String>> = HashSet::from_iter(header_names.iter());
if request.headers.borrow().iter().any(|ref hv| !set.contains(&UniCase(hv.name().to_owned())) && if request.headers.borrow().iter().any(|ref hv| !set.contains(&UniCase(hv.name().to_owned())) &&
!is_simple_header(hv)) { !is_simple_header(hv)) {
return Response::network_error(); return Response::network_error(NetworkError::Internal("CORS headers check failed".into()));
} }
// Substep 7, 8 // Substep 7, 8
@ -1253,7 +1258,7 @@ fn cors_preflight_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
} }
// Step 8 // Step 8
Response::network_error() Response::network_error(NetworkError::Internal("CORS check failed".into()))
} }
/// [CORS check](https://fetch.spec.whatwg.org#concept-cors-check) /// [CORS check](https://fetch.spec.whatwg.org#concept-cors-check)

View file

@ -14,12 +14,12 @@ use std::sync::{Arc, Mutex};
use url::Url; use url::Url;
/// [Response type](https://fetch.spec.whatwg.org/#concept-response-type) /// [Response type](https://fetch.spec.whatwg.org/#concept-response-type)
#[derive(Clone, PartialEq, Copy, Debug, Deserialize, Serialize, HeapSizeOf)] #[derive(Clone, PartialEq, Debug, Deserialize, Serialize, HeapSizeOf)]
pub enum ResponseType { pub enum ResponseType {
Basic, Basic,
CORS, CORS,
Default, Default,
Error, Error(NetworkError),
Opaque, Opaque,
OpaqueRedirect OpaqueRedirect
} }
@ -116,9 +116,9 @@ impl Response {
} }
} }
pub fn network_error() -> Response { pub fn network_error(e: NetworkError) -> Response {
Response { Response {
response_type: ResponseType::Error, response_type: ResponseType::Error(e),
termination_reason: None, termination_reason: None,
url: None, url: None,
url_list: RefCell::new(vec![]), url_list: RefCell::new(vec![]),
@ -135,11 +135,18 @@ impl Response {
pub fn is_network_error(&self) -> bool { pub fn is_network_error(&self) -> bool {
match self.response_type { match self.response_type {
ResponseType::Error => true, ResponseType::Error(..) => true,
_ => false _ => false
} }
} }
pub fn get_network_error(&self) -> Option<&NetworkError> {
match self.response_type {
ResponseType::Error(ref e) => Some(e),
_ => None,
}
}
pub fn actual_response(&self) -> &Response { pub fn actual_response(&self) -> &Response {
if self.return_internal.get() && self.internal_response.is_some() { if self.return_internal.get() && self.internal_response.is_some() {
&**self.internal_response.as_ref().unwrap() &**self.internal_response.as_ref().unwrap()
@ -159,13 +166,15 @@ impl Response {
/// Convert to a filtered response, of type `filter_type`. /// Convert to a filtered response, of type `filter_type`.
/// Do not use with type Error or Default /// Do not use with type Error or Default
pub fn to_filtered(self, filter_type: ResponseType) -> Response { pub fn to_filtered(self, filter_type: ResponseType) -> Response {
assert!(filter_type != ResponseType::Error); match filter_type {
assert!(filter_type != ResponseType::Default); ResponseType::Default | ResponseType::Error(..) => panic!(),
_ => (),
}
let old_response = self.to_actual(); let old_response = self.to_actual();
if Response::is_network_error(&old_response) { if let ResponseType::Error(e) = old_response.response_type {
return Response::network_error(); return Response::network_error(e);
} }
let old_headers = old_response.headers.clone(); let old_headers = old_response.headers.clone();
@ -173,8 +182,8 @@ impl Response {
response.internal_response = Some(Box::new(old_response)); response.internal_response = Some(Box::new(old_response));
response.response_type = filter_type; response.response_type = filter_type;
match filter_type { match response.response_type {
ResponseType::Default | ResponseType::Error => unreachable!(), ResponseType::Default | ResponseType::Error(..) => unreachable!(),
ResponseType::Basic => { ResponseType::Basic => {
let headers = old_headers.iter().filter(|header| { let headers = old_headers.iter().filter(|header| {
@ -238,8 +247,8 @@ impl Response {
metadata metadata
}; };
if self.is_network_error() { if let Some(error) = self.get_network_error() {
return Err(NetworkError::Internal("Cannot extract metadata from network error".to_owned())); return Err(error.clone());
} }
let metadata = self.url.as_ref().map(|url| init_metadata(self, url)); let metadata = self.url.as_ref().map(|url| init_metadata(self, url));

View file

@ -708,7 +708,7 @@ fn response_is_done(response: &Response) -> bool {
(*response.body.lock().unwrap()).is_done() (*response.body.lock().unwrap()).is_done()
} }
// if the internal response cannot have a body, it shouldn't block the "done" state // if the internal response cannot have a body, it shouldn't block the "done" state
ResponseType::Opaque | ResponseType::OpaqueRedirect | ResponseType::Error => true ResponseType::Opaque | ResponseType::OpaqueRedirect | ResponseType::Error(..) => true
}; };
let internal_complete = if let Some(ref res) = response.internal_response { let internal_complete = if let Some(ref res) = response.internal_response {