Skip to content

Commit

Permalink
Propagating the load errors from network loader
Browse files Browse the repository at this point in the history
  • Loading branch information
wafflespeanut authored and jdm committed Apr 20, 2016
1 parent 8d988f2 commit 5e6f32a
Show file tree
Hide file tree
Showing 15 changed files with 208 additions and 152 deletions.
22 changes: 14 additions & 8 deletions components/gfx/font_cache_thread.rs
Expand Up @@ -183,15 +183,21 @@ impl FontCache {
ROUTER.add_route(data_receiver.to_opaque(), box move |message| {
let response: ResponseAction = message.to().unwrap();
match response {
ResponseAction::HeadersAvailable(metadata) => {
let is_response_valid =
metadata.content_type.as_ref().map_or(false, |content_type| {
let mime = &content_type.0;
is_supported_font_type(&mime.0, &mime.1)
});
info!("{} font with MIME type {:?}",
ResponseAction::HeadersAvailable(meta_result) => {
let is_response_valid = match meta_result {
Ok(ref metadata) => {
metadata.content_type.as_ref().map_or(false, |content_type| {
let mime = &content_type.0;
is_supported_font_type(&mime.0, &mime.1)
})
}
Err(_) => false,
};

info!("{} font with MIME type {}",
if is_response_valid { "Loading" } else { "Ignoring" },
metadata.content_type);
meta_result.map(|ref meta| format!("{:?}", meta.content_type))
.unwrap_or(format!("<Network Error>")));
*response_valid.lock().unwrap() = is_response_valid;
}
ResponseAction::DataAvailable(new_bytes) => {
Expand Down
4 changes: 2 additions & 2 deletions components/net/about_loader.rs
Expand Up @@ -9,7 +9,7 @@ use hyper::mime::{Mime, SubLevel, TopLevel};
use mime_classifier::MIMEClassifier;
use net_traits::ProgressMsg::Done;
use net_traits::response::HttpsState;
use net_traits::{LoadConsumer, LoadData, Metadata};
use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError};
use resource_thread::{CancellationListener, send_error, start_sending_sniffed_opt};
use std::sync::Arc;
use url::Url;
Expand Down Expand Up @@ -49,7 +49,7 @@ pub fn factory(mut load_data: LoadData,
load_data.url = Url::from_file_path(&*path).unwrap();
}
_ => {
send_error(load_data.url, "Unknown about: URL.".to_owned(), start_chan);
send_error(load_data.url, NetworkError::Internal("Unknown about: URL.".to_owned()), start_chan);
return
}
};
Expand Down
8 changes: 5 additions & 3 deletions components/net/data_loader.rs
Expand Up @@ -6,7 +6,7 @@ use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use mime_classifier::MIMEClassifier;
use net_traits::LoadConsumer;
use net_traits::ProgressMsg::{Payload, Done};
use net_traits::{LoadData, Metadata};
use net_traits::{LoadData, Metadata, NetworkError};
use resource_thread::{CancellationListener, send_error, start_sending_sniffed_opt};
use rustc_serialize::base64::FromBase64;
use std::sync::Arc;
Expand Down Expand Up @@ -110,7 +110,9 @@ pub fn load(load_data: LoadData,
let _ = chan.send(Done(Ok(())));
}
},
Err(DecodeError::InvalidDataUri) => send_error(url, "invalid data uri".to_owned(), start_chan),
Err(DecodeError::NonBase64DataUri) => send_error(url, "non-base64 data uri".to_owned(), start_chan),
Err(DecodeError::InvalidDataUri) =>
send_error(url, NetworkError::Internal("invalid data uri".to_owned()), start_chan),
Err(DecodeError::NonBase64DataUri) =>
send_error(url, NetworkError::Internal("non-base64 data uri".to_owned()), start_chan),
}
}
10 changes: 5 additions & 5 deletions components/net/file_loader.rs
Expand Up @@ -6,7 +6,7 @@ use about_loader;
use mime_classifier::MIMEClassifier;
use mime_guess::guess_mime_type;
use net_traits::ProgressMsg::{Done, Payload};
use net_traits::{LoadConsumer, LoadData, Metadata};
use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError};
use resource_thread::{CancellationListener, ProgressSender};
use resource_thread::{send_error, start_sending_sniffed_opt};
use std::borrow::ToOwned;
Expand Down Expand Up @@ -50,7 +50,7 @@ fn read_all(reader: &mut File, progress_chan: &ProgressSender, cancel_listener:
ReadStatus::EOF => return Ok(LoadResult::Finished),
}
}
let _ = progress_chan.send(Done(Err("load cancelled".to_owned())));
let _ = progress_chan.send(Done(Err(NetworkError::Internal("load cancelled".to_owned()))));
Ok(LoadResult::Cancelled)
}

Expand All @@ -72,7 +72,7 @@ pub fn factory(load_data: LoadData,
let file_path = match load_data.url.to_file_path() {
Ok(file_path) => file_path,
Err(_) => {
send_error(load_data.url, "Could not parse path".to_owned(), senders);
send_error(load_data.url, NetworkError::Internal("Could not parse path".to_owned()), senders);
return;
},
};
Expand All @@ -92,7 +92,7 @@ pub fn factory(load_data: LoadData,
if cancel_listener.is_cancelled() {
if let Ok(progress_chan) = get_progress_chan(load_data, file_path,
senders, classifier, &[]) {
let _ = progress_chan.send(Done(Err("load cancelled".to_owned())));
let _ = progress_chan.send(Done(Err(NetworkError::Internal("load cancelled".to_owned()))));
}
return;
}
Expand All @@ -116,7 +116,7 @@ pub fn factory(load_data: LoadData,
}
}
Err(e) => {
send_error(load_data.url, e, senders);
send_error(load_data.url, NetworkError::Internal(e), senders);
}
}
});
Expand Down
143 changes: 69 additions & 74 deletions components/net/http_loader.rs
Expand Up @@ -8,7 +8,6 @@ use cookie;
use cookie_storage::CookieStorage;
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest};
use devtools_traits::{HttpResponse as DevtoolsHttpResponse, NetworkEvent};
use file_loader;
use flate2::read::{DeflateDecoder, GzDecoder};
use hsts::{HstsEntry, HstsList, secure_url};
use hyper::Error as HttpError;
Expand All @@ -28,7 +27,8 @@ use msg::constellation_msg::{PipelineId};
use net_traits::ProgressMsg::{Done, Payload};
use net_traits::hosts::replace_hosts;
use net_traits::response::HttpsState;
use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadContext, LoadData, Metadata};
use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadContext, LoadData};
use net_traits::{Metadata, NetworkError};
use openssl::ssl::error::{SslError, OpensslError};
use openssl::ssl::{SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SSL_VERIFY_PEER, SslContext, SslMethod};
use resource_thread::{CancellationListener, send_error, start_sending_sniffed_opt, AuthCacheEntry};
Expand Down Expand Up @@ -158,32 +158,15 @@ fn load_for_consumer(load_data: LoadData,
match load(&load_data, &ui_provider, &http_state,
devtools_chan, &factory,
user_agent, &cancel_listener) {
Err(LoadError::UnsupportedScheme(url)) => {
let s = format!("{} request, but we don't support that scheme", &*url.scheme);
send_error(url, s, start_chan)
}
Err(LoadError::Connection(url, e)) => {
send_error(url, e, start_chan)
}
Err(LoadError::MaxRedirects(url, _)) => {
send_error(url, "too many redirects".to_owned(), start_chan)
}
Err(LoadError::Cors(url, msg)) |
Err(LoadError::Cancelled(url, msg)) |
Err(LoadError::InvalidRedirect(url, msg)) |
Err(LoadError::Decoding(url, msg)) => {
send_error(url, msg, start_chan)
}
Err(LoadError::Ssl(url, msg)) => {
info!("ssl validation error {}, '{}'", url.serialize(), msg);

let mut image = resources_dir_path();
image.push("badcert.html");
let load_data = LoadData::new(load_data.context, Url::from_file_path(&*image).unwrap(), None);

file_loader::factory(load_data, start_chan, classifier, cancel_listener)
Err(error) => {
match error.error {
LoadErrorType::ConnectionAborted => unreachable!(),
LoadErrorType::Ssl => send_error(error.url.clone(),
NetworkError::SslValidation(error.url),
start_chan),
_ => send_error(error.url, NetworkError::Internal(error.reason), start_chan)
}
}
Err(LoadError::ConnectionAborted(_)) => unreachable!(),
Ok(mut load_response) => {
let metadata = load_response.metadata.clone();
send_data(load_data.context, &mut load_response, start_chan, metadata, classifier, &cancel_listener)
Expand Down Expand Up @@ -268,20 +251,15 @@ impl HttpRequestFactory for NetworkHttpRequestFactory {
let error: &(Error + Send + 'static) = &**error;
if let Some(&SslError::OpenSslErrors(ref errors)) = error.downcast_ref::<SslError>() {
if errors.iter().any(is_cert_verify_error) {
return Err(
LoadError::Ssl(url, format!("ssl error: {:?} {:?}",
error.description(),
error.cause())));
let msg = format!("ssl error: {:?} {:?}", error.description(), error.cause());
return Err(LoadError::new(url, LoadErrorType::Ssl, msg));
}
}
}

let mut request = match connection {
Ok(req) => req,

Err(e) => {
return Err(LoadError::Connection(url, e.description().to_owned()))
}
Err(e) => return Err(LoadError::new(url, LoadErrorType::Connection, e.description().to_owned())),
};
*request.headers_mut() = headers;

Expand All @@ -306,38 +284,57 @@ impl HttpRequest for WrappedHttpRequest {
let url = self.request.url.clone();
let mut request_writer = match self.request.start() {
Ok(streaming) => streaming,
Err(e) => return Err(LoadError::Connection(url, e.description().to_owned()))
Err(e) => return Err(LoadError::new(url, LoadErrorType::Connection, e.description().to_owned())),
};

if let Some(ref data) = *body {
if let Err(e) = request_writer.write_all(&data) {
return Err(LoadError::Connection(url, e.description().to_owned()))
return Err(LoadError::new(url, LoadErrorType::Connection, e.description().to_owned()))
}
}

let response = match request_writer.send() {
Ok(w) => w,
Err(HttpError::Io(ref io_error)) if io_error.kind() == io::ErrorKind::ConnectionAborted => {
return Err(LoadError::ConnectionAborted(io_error.description().to_owned()));
return Err(LoadError::new(url, LoadErrorType::ConnectionAborted,
io_error.description().to_owned()));
},
Err(e) => return Err(LoadError::Connection(url, e.description().to_owned()))
Err(e) => return Err(LoadError::new(url, LoadErrorType::Connection,
e.description().to_owned())),
};

Ok(WrappedHttpResponse { response: response })
}
}

#[derive(Debug)]
pub enum LoadError {
UnsupportedScheme(Url),
Connection(Url, String),
Cors(Url, String),
Ssl(Url, String),
InvalidRedirect(Url, String),
Decoding(Url, String),
MaxRedirects(Url, u32), // u32 indicates number of redirects that occurred
ConnectionAborted(String),
Cancelled(Url, String),
pub struct LoadError {
url: Url,
error: LoadErrorType,
reason: String,
}

impl LoadError {
fn new(url: Url, error: LoadErrorType, reason: String) -> LoadError {
LoadError {
url: url,
error: error,
reason: reason,
}
}
}

#[derive(Debug)]
pub enum LoadErrorType {
Cancelled,
Connection,
ConnectionAborted,
Cors,
Decoding,
InvalidRedirect,
MaxRedirects(u32), // u32 indicates number of redirects that occurred
Ssl,
UnsupportedScheme,
}

fn set_default_accept_encoding(headers: &mut Headers) {
Expand Down Expand Up @@ -456,12 +453,8 @@ impl<R: HttpResponse> StreamedResponse<R> {
Some(Encoding::Gzip) => {
let result = GzDecoder::new(response);
match result {
Ok(response_decoding) => {
Ok(StreamedResponse::new(m, Decoder::Gzip(response_decoding)))
}
Err(err) => {
Err(LoadError::Decoding(m.final_url, err.to_string()))
}
Ok(response_decoding) => Ok(StreamedResponse::new(m, Decoder::Gzip(response_decoding))),
Err(err) => Err(LoadError::new(m.final_url, LoadErrorType::Decoding, err.to_string())),
}
}
Some(Encoding::Deflate) => {
Expand Down Expand Up @@ -670,7 +663,7 @@ pub fn obtain_response<A>(request_factory: &HttpRequestFactory<R=A>,
headers.clone()));

if cancel_listener.is_cancelled() {
return Err(LoadError::Cancelled(connection_url.clone(), "load cancelled".to_owned()));
return Err(LoadError::new(connection_url.clone(), LoadErrorType::Cancelled, "load cancelled".to_owned()));
}

let maybe_response = req.send(request_body);
Expand All @@ -685,11 +678,14 @@ pub fn obtain_response<A>(request_factory: &HttpRequestFactory<R=A>,

response = match maybe_response {
Ok(r) => r,
Err(LoadError::ConnectionAborted(reason)) => {
debug!("connection aborted ({:?}), possibly stale, trying new connection", reason);
continue;
}
Err(e) => return Err(e),
Err(e) => {
if let LoadErrorType::ConnectionAborted = e.error {
debug!("connection aborted ({:?}), possibly stale, trying new connection", e.reason);
continue;
} else {
return Err(e)
}
},
};

// if no ConnectionAborted, break the loop
Expand Down Expand Up @@ -736,7 +732,7 @@ pub fn load<A, B>(load_data: &LoadData,
let mut new_auth_header: Option<Authorization<Basic>> = None;

if cancel_listener.is_cancelled() {
return Err(LoadError::Cancelled(doc_url, "load cancelled".to_owned()));
return Err(LoadError::new(doc_url, LoadErrorType::Cancelled, "load cancelled".to_owned()));
}

// If the URL is a view-source scheme then the scheme data contains the
Expand All @@ -758,15 +754,17 @@ pub fn load<A, B>(load_data: &LoadData,
}

if iters > max_redirects {
return Err(LoadError::MaxRedirects(doc_url, iters - 1));
return Err(LoadError::new(doc_url, LoadErrorType::MaxRedirects(iters - 1),
"too many redirects".to_owned()));
}

if &*doc_url.scheme != "http" && &*doc_url.scheme != "https" {
return Err(LoadError::UnsupportedScheme(doc_url));
let s = format!("{} request, but we don't support that scheme", &*doc_url.scheme);
return Err(LoadError::new(doc_url, LoadErrorType::UnsupportedScheme, s));
}

if cancel_listener.is_cancelled() {
return Err(LoadError::Cancelled(doc_url, "load cancelled".to_owned()));
return Err(LoadError::new(doc_url, LoadErrorType::Cancelled, "load cancelled".to_owned()));
}

info!("requesting {}", doc_url.serialize());
Expand Down Expand Up @@ -832,10 +830,9 @@ pub fn load<A, B>(load_data: &LoadData,
// CORS (https://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10)
if let Some(ref c) = load_data.cors {
if c.preflight {
return Err(
LoadError::Cors(
doc_url,
"Preflight fetch inconsistent with main fetch".to_owned()));
return Err(LoadError::new(doc_url,
LoadErrorType::Cors,
"Preflight fetch inconsistent with main fetch".to_owned()));
} else {
// XXXManishearth There are some CORS-related steps here,
// but they don't seem necessary until credentials are implemented
Expand All @@ -844,9 +841,7 @@ pub fn load<A, B>(load_data: &LoadData,

let new_doc_url = match doc_url.join(&new_url) {
Ok(u) => u,
Err(e) => {
return Err(LoadError::InvalidRedirect(doc_url, e.to_string()));
}
Err(e) => return Err(LoadError::new(doc_url, LoadErrorType::InvalidRedirect, e.to_string())),
};

// According to https://tools.ietf.org/html/rfc7231#section-6.4.2,
Expand All @@ -858,7 +853,7 @@ pub fn load<A, B>(load_data: &LoadData,
}

if redirected_to.contains(&new_doc_url) {
return Err(LoadError::InvalidRedirect(doc_url, "redirect loop".to_owned()));
return Err(LoadError::new(doc_url, LoadErrorType::InvalidRedirect, "redirect loop".to_owned()));
}

info!("redirecting to {}", new_doc_url);
Expand Down Expand Up @@ -921,7 +916,7 @@ fn send_data<R: Read>(context: LoadContext,

loop {
if cancel_listener.is_cancelled() {
let _ = progress_chan.send(Done(Err("load cancelled".to_owned())));
let _ = progress_chan.send(Done(Err(NetworkError::Internal("load cancelled".to_owned()))));
return;
}

Expand Down

0 comments on commit 5e6f32a

Please sign in to comment.