mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Auto merge of #9942 - jdm:load_error, r=ms2ger
Moving the error handling out of network loader Rebase of #8851. Fixes #8678. Fixes #9944. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9942) <!-- Reviewable:end -->
This commit is contained in:
commit
f051028ee8
30 changed files with 396 additions and 189 deletions
|
@ -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) => {
|
||||
|
|
|
@ -9,12 +9,19 @@ 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;
|
||||
use util::resource_files::resources_dir_path;
|
||||
|
||||
fn url_from_non_relative_scheme(load_data: &mut LoadData, filename: &str) {
|
||||
let mut path = resources_dir_path();
|
||||
path.push(filename);
|
||||
assert!(path.exists());
|
||||
load_data.url = Url::from_file_path(&*path).unwrap();
|
||||
}
|
||||
|
||||
pub fn factory(mut load_data: LoadData,
|
||||
start_chan: LoadConsumer,
|
||||
classifier: Arc<MIMEClassifier>,
|
||||
|
@ -41,15 +48,11 @@ pub fn factory(mut load_data: LoadData,
|
|||
return
|
||||
}
|
||||
"crash" => panic!("Loading the about:crash URL."),
|
||||
"failure" | "not-found" => {
|
||||
let mut path = resources_dir_path();
|
||||
let file_name = non_relative_scheme_data.to_owned() + ".html";
|
||||
path.push(&file_name);
|
||||
assert!(path.exists());
|
||||
load_data.url = Url::from_file_path(&*path).unwrap();
|
||||
}
|
||||
"failure" | "not-found" =>
|
||||
url_from_non_relative_scheme(&mut load_data, &(non_relative_scheme_data.to_owned() + ".html")),
|
||||
"sslfail" => url_from_non_relative_scheme(&mut load_data, "badcert.html"),
|
||||
_ => {
|
||||
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
|
||||
}
|
||||
};
|
||||
|
|
47
components/net/chrome_loader.rs
Normal file
47
components/net/chrome_loader.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 file_loader;
|
||||
use mime_classifier::MIMEClassifier;
|
||||
use net_traits::{LoadConsumer, LoadData, NetworkError};
|
||||
use resource_thread::{CancellationListener, send_error};
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use url::Url;
|
||||
use util::resource_files::resources_dir_path;
|
||||
|
||||
pub fn resolve_chrome_url(url: &Url) -> Result<Url, ()> {
|
||||
assert_eq!(url.scheme, "chrome");
|
||||
// Skip the initial //
|
||||
let non_relative_scheme_data = &url.non_relative_scheme_data().unwrap()[2..];
|
||||
let relative_path = Path::new(non_relative_scheme_data);
|
||||
// Don't allow chrome URLs access to files outside of the resources directory.
|
||||
if non_relative_scheme_data.find("..").is_some() ||
|
||||
relative_path.is_absolute() ||
|
||||
relative_path.has_root() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut path = resources_dir_path();
|
||||
path.push(non_relative_scheme_data);
|
||||
assert!(path.exists());
|
||||
return Ok(Url::from_file_path(&*path).unwrap());
|
||||
}
|
||||
|
||||
pub fn factory(mut load_data: LoadData,
|
||||
start_chan: LoadConsumer,
|
||||
classifier: Arc<MIMEClassifier>,
|
||||
cancel_listener: CancellationListener) {
|
||||
let file_url = match resolve_chrome_url(&load_data.url) {
|
||||
Ok(url) => url,
|
||||
Err(_) => {
|
||||
send_error(load_data.url,
|
||||
NetworkError::Internal("Invalid chrome URL.".to_owned()),
|
||||
start_chan);
|
||||
return;
|
||||
}
|
||||
};
|
||||
load_data.url = file_url;
|
||||
file_loader::factory(load_data, start_chan, classifier, cancel_listener)
|
||||
}
|
|
@ -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;
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
},
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
@ -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};
|
||||
|
@ -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(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::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(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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -306,21 +284,23 @@ 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 })
|
||||
|
@ -328,16 +308,33 @@ impl HttpRequest for WrappedHttpRequest {
|
|||
}
|
||||
|
||||
#[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 {
|
||||
pub url: Url,
|
||||
pub error: LoadErrorType,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
impl LoadError {
|
||||
pub fn new(url: Url, error: LoadErrorType, reason: String) -> LoadError {
|
||||
LoadError {
|
||||
url: url,
|
||||
error: error,
|
||||
reason: reason,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, 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) {
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use net_traits::image_cache_thread::ImageResponder;
|
|||
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheCommand, ImageCacheThread, ImageState};
|
||||
use net_traits::image_cache_thread::{ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, UsePlaceholder};
|
||||
use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, LoadData, ResourceThread};
|
||||
use net_traits::{ResponseAction, LoadContext};
|
||||
use net_traits::{ResponseAction, LoadContext, NetworkError};
|
||||
use std::borrow::ToOwned;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
|
@ -44,7 +44,7 @@ struct PendingLoad {
|
|||
metadata: Option<ImageMetadata>,
|
||||
|
||||
// Once loading is complete, the result of the operation.
|
||||
result: Option<Result<(), String>>,
|
||||
result: Option<Result<(), NetworkError>>,
|
||||
listeners: Vec<ImageListener>,
|
||||
|
||||
// The url being loaded. Do not forget that this may be several Mb
|
||||
|
|
|
@ -39,6 +39,7 @@ extern crate webrender_traits;
|
|||
extern crate websocket;
|
||||
|
||||
pub mod about_loader;
|
||||
pub mod chrome_loader;
|
||||
pub mod cookie;
|
||||
pub mod cookie_storage;
|
||||
pub mod data_loader;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
//! A thread that takes a URL and streams back the binary data.
|
||||
|
||||
use about_loader;
|
||||
use chrome_loader;
|
||||
use cookie;
|
||||
use cookie_storage::CookieStorage;
|
||||
use data_loader;
|
||||
|
@ -21,7 +22,7 @@ use net_traits::LoadContext;
|
|||
use net_traits::ProgressMsg::Done;
|
||||
use net_traits::{AsyncResponseTarget, Metadata, ProgressMsg, ResourceThread, ResponseAction};
|
||||
use net_traits::{ControlMsg, CookieSource, LoadConsumer, LoadData, LoadResponse, ResourceId};
|
||||
use net_traits::{WebSocketCommunicate, WebSocketConnectData};
|
||||
use net_traits::{NetworkError, WebSocketCommunicate, WebSocketConnectData};
|
||||
use std::borrow::ToOwned;
|
||||
use std::boxed::FnBox;
|
||||
use std::cell::Cell;
|
||||
|
@ -55,11 +56,11 @@ impl ProgressSender {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn send_error(url: Url, err: String, start_chan: LoadConsumer) {
|
||||
pub fn send_error(url: Url, err: NetworkError, start_chan: LoadConsumer) {
|
||||
let mut metadata: Metadata = Metadata::default(url);
|
||||
metadata.status = None;
|
||||
|
||||
if let Ok(p) = start_sending_opt(start_chan, metadata) {
|
||||
if let Ok(p) = start_sending_opt(start_chan, metadata, Some(err.clone())) {
|
||||
p.send(Done(Err(err))).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -107,16 +108,19 @@ pub fn start_sending_sniffed_opt(start_chan: LoadConsumer, mut metadata: Metadat
|
|||
metadata.content_type = Some(ContentType(Mime(mime_tp, mime_sb, vec![])));
|
||||
}
|
||||
|
||||
start_sending_opt(start_chan, metadata)
|
||||
start_sending_opt(start_chan, metadata, None)
|
||||
}
|
||||
|
||||
/// For use by loaders in responding to a Load message.
|
||||
fn start_sending_opt(start_chan: LoadConsumer, metadata: Metadata) -> Result<ProgressSender, ()> {
|
||||
/// It takes an optional NetworkError, so that we can extract the SSL Validation errors
|
||||
/// and take it to the HTML parser
|
||||
fn start_sending_opt(start_chan: LoadConsumer, metadata: Metadata,
|
||||
network_error: Option<NetworkError>) -> Result<ProgressSender, ()> {
|
||||
match start_chan {
|
||||
LoadConsumer::Channel(start_chan) => {
|
||||
let (progress_chan, progress_port) = ipc::channel().unwrap();
|
||||
let result = start_chan.send(LoadResponse {
|
||||
metadata: metadata,
|
||||
metadata: metadata,
|
||||
progress_port: progress_port,
|
||||
});
|
||||
match result {
|
||||
|
@ -125,7 +129,13 @@ fn start_sending_opt(start_chan: LoadConsumer, metadata: Metadata) -> Result<Pro
|
|||
}
|
||||
}
|
||||
LoadConsumer::Listener(target) => {
|
||||
target.invoke_with_listener(ResponseAction::HeadersAvailable(metadata));
|
||||
match network_error {
|
||||
Some(NetworkError::SslValidation(url)) => {
|
||||
let error = NetworkError::SslValidation(url);
|
||||
target.invoke_with_listener(ResponseAction::HeadersAvailable(Err(error)));
|
||||
}
|
||||
_ => target.invoke_with_listener(ResponseAction::HeadersAvailable(Ok(metadata))),
|
||||
}
|
||||
Ok(ProgressSender::Listener(target))
|
||||
}
|
||||
}
|
||||
|
@ -323,6 +333,7 @@ impl ResourceManager {
|
|||
|
||||
let cancel_listener = CancellationListener::new(cancel_resource);
|
||||
let loader = match &*load_data.url.scheme {
|
||||
"chrome" => from_factory(chrome_loader::factory),
|
||||
"file" => from_factory(file_loader::factory),
|
||||
"http" | "https" | "view-source" => {
|
||||
let http_state = HttpState {
|
||||
|
@ -339,7 +350,7 @@ impl ResourceManager {
|
|||
"about" => from_factory(about_loader::factory),
|
||||
_ => {
|
||||
debug!("resource_thread: no loader for scheme {}", load_data.url.scheme);
|
||||
send_error(load_data.url, "no loader for scheme".to_owned(), consumer);
|
||||
send_error(load_data.url, NetworkError::Internal("no loader for scheme".to_owned()), consumer);
|
||||
return
|
||||
}
|
||||
};
|
||||
|
|
|
@ -113,13 +113,13 @@ pub trait AsyncFetchListener {
|
|||
/// A listener for asynchronous network events. Cancelling the underlying request is unsupported.
|
||||
pub trait AsyncResponseListener {
|
||||
/// The response headers for a request have been received.
|
||||
fn headers_available(&mut self, metadata: Metadata);
|
||||
fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>);
|
||||
/// A portion of the response body has been received. This data is unavailable after
|
||||
/// this method returned, and must be stored accordingly.
|
||||
fn data_available(&mut self, payload: Vec<u8>);
|
||||
/// The response is complete. If the provided status is an Err value, there is no guarantee
|
||||
/// that the response body was completely read.
|
||||
fn response_complete(&mut self, status: Result<(), String>);
|
||||
fn response_complete(&mut self, status: Result<(), NetworkError>);
|
||||
}
|
||||
|
||||
/// Data for passing between threads/processes to indicate a particular action to
|
||||
|
@ -127,11 +127,11 @@ pub trait AsyncResponseListener {
|
|||
#[derive(Deserialize, Serialize)]
|
||||
pub enum ResponseAction {
|
||||
/// Invoke headers_available
|
||||
HeadersAvailable(Metadata),
|
||||
HeadersAvailable(Result<Metadata, NetworkError>),
|
||||
/// Invoke data_available
|
||||
DataAvailable(Vec<u8>),
|
||||
/// Invoke response_complete
|
||||
ResponseComplete(Result<(), String>)
|
||||
ResponseComplete(Result<(), NetworkError>)
|
||||
}
|
||||
|
||||
impl ResponseAction {
|
||||
|
@ -376,7 +376,7 @@ pub enum ProgressMsg {
|
|||
/// Binary data - there may be multiple of these
|
||||
Payload(Vec<u8>),
|
||||
/// Indicates loading is complete, either successfully or not
|
||||
Done(Result<(), String>)
|
||||
Done(Result<(), NetworkError>),
|
||||
}
|
||||
|
||||
/// Convenience function for synchronously loading a whole resource.
|
||||
|
@ -384,7 +384,7 @@ pub fn load_whole_resource(context: LoadContext,
|
|||
resource_thread: &ResourceThread,
|
||||
url: Url,
|
||||
pipeline_id: Option<PipelineId>)
|
||||
-> Result<(Metadata, Vec<u8>), String> {
|
||||
-> Result<(Metadata, Vec<u8>), NetworkError> {
|
||||
let (start_chan, start_port) = ipc::channel().unwrap();
|
||||
resource_thread.send(ControlMsg::Load(LoadData::new(context, url, pipeline_id),
|
||||
LoadConsumer::Channel(start_chan), None)).unwrap();
|
||||
|
@ -413,3 +413,13 @@ pub enum ConstellationMsg {
|
|||
/// Queries whether a pipeline or its ancestors are private
|
||||
IsPrivate(PipelineId, Sender<bool>),
|
||||
}
|
||||
|
||||
/// Network errors that have to be exported out of the loaders
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, HeapSizeOf)]
|
||||
pub enum NetworkError {
|
||||
/// Could be any of the internal errors, like unsupported scheme, load
|
||||
/// cancellation, connection errors, etc.
|
||||
Internal(String),
|
||||
/// SSL validation error that has to be handled in the HTML parser
|
||||
SslValidation(Url),
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use hyper::header::{HeaderView, Headers};
|
|||
use hyper::method::Method;
|
||||
use hyper::mime::{Mime, SubLevel, TopLevel};
|
||||
use hyper::status::StatusClass::Success;
|
||||
use net_traits::{AsyncResponseListener, Metadata, ResponseAction};
|
||||
use net_traits::{AsyncResponseListener, Metadata, NetworkError, ResponseAction};
|
||||
use network_listener::{NetworkListener, PreInvoke};
|
||||
use script_runtime::ScriptChan;
|
||||
use std::ascii::AsciiExt;
|
||||
|
@ -124,13 +124,15 @@ impl CORSRequest {
|
|||
// This is shoe-horning the CORSReponse stuff into the rest of the async network
|
||||
// framework right now. It would be worth redesigning http_fetch to do this properly.
|
||||
impl AsyncResponseListener for CORSContext {
|
||||
fn headers_available(&mut self, _metadata: Metadata) {
|
||||
fn headers_available(&mut self, _metadata: Result<Metadata, NetworkError>) {
|
||||
|
||||
}
|
||||
|
||||
fn data_available(&mut self, _payload: Vec<u8>) {
|
||||
|
||||
}
|
||||
|
||||
fn response_complete(&mut self, _status: Result<(), String>) {
|
||||
fn response_complete(&mut self, _status: Result<(), NetworkError>) {
|
||||
let response = self.response.take().unwrap();
|
||||
self.listener.response_available(response);
|
||||
}
|
||||
|
|
|
@ -57,11 +57,11 @@ use layout_interface::{LayoutChan, LayoutRPC};
|
|||
use libc;
|
||||
use msg::constellation_msg::ConstellationChan;
|
||||
use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData};
|
||||
use net_traits::Metadata;
|
||||
use net_traits::image::base::{Image, ImageMetadata};
|
||||
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
|
||||
use net_traits::response::HttpsState;
|
||||
use net_traits::storage_thread::StorageType;
|
||||
use net_traits::{Metadata, NetworkError};
|
||||
use offscreen_gl_context::GLLimits;
|
||||
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
||||
use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
||||
|
@ -287,6 +287,7 @@ no_jsmanaged_fields!(Size2D<T>);
|
|||
no_jsmanaged_fields!(Arc<T>);
|
||||
no_jsmanaged_fields!(Image, ImageMetadata, ImageCacheChan, ImageCacheThread);
|
||||
no_jsmanaged_fields!(Metadata);
|
||||
no_jsmanaged_fields!(NetworkError);
|
||||
no_jsmanaged_fields!(Atom, Namespace, QualName);
|
||||
no_jsmanaged_fields!(Trusted<T: Reflectable>);
|
||||
no_jsmanaged_fields!(PropertyDeclarationBlock);
|
||||
|
|
|
@ -24,7 +24,7 @@ use ipc_channel::ipc;
|
|||
use ipc_channel::router::ROUTER;
|
||||
use layout_interface::{LayoutChan, Msg};
|
||||
use msg::constellation_msg::ConstellationChan;
|
||||
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata};
|
||||
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
|
||||
use network_listener::{NetworkListener, PreInvoke};
|
||||
use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg};
|
||||
use std::ascii::AsciiExt;
|
||||
|
@ -271,8 +271,8 @@ struct StylesheetContext {
|
|||
impl PreInvoke for StylesheetContext {}
|
||||
|
||||
impl AsyncResponseListener for StylesheetContext {
|
||||
fn headers_available(&mut self, metadata: Metadata) {
|
||||
self.metadata = Some(metadata);
|
||||
fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
|
||||
self.metadata = metadata.ok();
|
||||
}
|
||||
|
||||
fn data_available(&mut self, payload: Vec<u8>) {
|
||||
|
@ -280,9 +280,12 @@ impl AsyncResponseListener for StylesheetContext {
|
|||
self.data.append(&mut payload);
|
||||
}
|
||||
|
||||
fn response_complete(&mut self, _status: Result<(), String>) {
|
||||
fn response_complete(&mut self, _status: Result<(), NetworkError>) {
|
||||
let data = mem::replace(&mut self.data, vec!());
|
||||
let metadata = self.metadata.take().unwrap();
|
||||
let metadata = match self.metadata.take() {
|
||||
Some(meta) => meta,
|
||||
None => return,
|
||||
};
|
||||
// TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding
|
||||
let environment_encoding = UTF_8 as EncodingRef;
|
||||
let protocol_encoding_label = metadata.charset.as_ref().map(|s| &**s);
|
||||
|
|
|
@ -33,7 +33,7 @@ use ipc_channel::ipc;
|
|||
use ipc_channel::router::ROUTER;
|
||||
use js::jsapi::RootedValue;
|
||||
use js::jsval::UndefinedValue;
|
||||
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata};
|
||||
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
|
||||
use network_listener::{NetworkListener, PreInvoke};
|
||||
use script_runtime::ScriptChan;
|
||||
use script_thread::MainThreadScriptChan;
|
||||
|
@ -124,7 +124,7 @@ static SCRIPT_JS_MIMES: StaticStringVec = &[
|
|||
#[derive(HeapSizeOf, JSTraceable)]
|
||||
pub enum ScriptOrigin {
|
||||
Internal(DOMString, Url),
|
||||
External(Result<(Metadata, Vec<u8>), String>),
|
||||
External(Result<(Metadata, Vec<u8>), NetworkError>),
|
||||
}
|
||||
|
||||
/// The context required for asynchronously loading an external script source.
|
||||
|
@ -138,23 +138,25 @@ struct ScriptContext {
|
|||
/// The initial URL requested.
|
||||
url: Url,
|
||||
/// Indicates whether the request failed, and why
|
||||
status: Result<(), String>
|
||||
status: Result<(), NetworkError>
|
||||
}
|
||||
|
||||
impl AsyncResponseListener for ScriptContext {
|
||||
fn headers_available(&mut self, metadata: Metadata) {
|
||||
let status_code = match metadata.status {
|
||||
Some(RawStatus(c, _)) => c,
|
||||
_ => 0
|
||||
};
|
||||
fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
|
||||
self.metadata = metadata.ok();
|
||||
|
||||
let status_code = self.metadata.as_ref().and_then(|m| {
|
||||
match m.status {
|
||||
Some(RawStatus(c, _)) => Some(c),
|
||||
_ => None,
|
||||
}
|
||||
}).unwrap_or(0);
|
||||
|
||||
self.status = match status_code {
|
||||
0 => Err("No http status code received".to_owned()),
|
||||
0 => Err(NetworkError::Internal("No http status code received".to_owned())),
|
||||
200...299 => Ok(()), // HTTP ok status codes
|
||||
_ => Err(format!("HTTP error code {}", status_code))
|
||||
_ => Err(NetworkError::Internal(format!("HTTP error code {}", status_code)))
|
||||
};
|
||||
|
||||
self.metadata = Some(metadata);
|
||||
}
|
||||
|
||||
fn data_available(&mut self, payload: Vec<u8>) {
|
||||
|
@ -164,7 +166,7 @@ impl AsyncResponseListener for ScriptContext {
|
|||
}
|
||||
}
|
||||
|
||||
fn response_complete(&mut self, status: Result<(), String>) {
|
||||
fn response_complete(&mut self, status: Result<(), NetworkError>) {
|
||||
let load = status.and(self.status.clone()).map(|_| {
|
||||
let data = mem::replace(&mut self.data, vec!());
|
||||
let metadata = self.metadata.take().unwrap();
|
||||
|
@ -398,7 +400,7 @@ impl HTMLScriptElement {
|
|||
let (source, external, url) = match load {
|
||||
// Step 2.a.
|
||||
ScriptOrigin::External(Err(e)) => {
|
||||
error!("error loading script {}", e);
|
||||
error!("error loading script {:?}", e);
|
||||
self.dispatch_error_event();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ use hyper::header::ContentType;
|
|||
use hyper::mime::{Mime, SubLevel, TopLevel};
|
||||
use js::jsapi::JSTracer;
|
||||
use msg::constellation_msg::{PipelineId, SubpageId};
|
||||
use net_traits::{AsyncResponseListener, Metadata};
|
||||
use net_traits::{AsyncResponseListener, Metadata, NetworkError};
|
||||
use network_listener::PreInvoke;
|
||||
use parse::Parser;
|
||||
use script_runtime::ScriptChan;
|
||||
|
@ -36,6 +36,7 @@ use std::cell::UnsafeCell;
|
|||
use std::default::Default;
|
||||
use std::ptr;
|
||||
use url::Url;
|
||||
use util::resource_files::read_resource_file;
|
||||
|
||||
#[must_root]
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
|
@ -239,12 +240,23 @@ impl ParserContext {
|
|||
}
|
||||
|
||||
impl AsyncResponseListener for ParserContext {
|
||||
fn headers_available(&mut self, metadata: Metadata) {
|
||||
let content_type = metadata.content_type.clone();
|
||||
|
||||
let parser = ScriptThread::page_fetch_complete(self.id.clone(), self.subpage.clone(),
|
||||
metadata);
|
||||
let parser = match parser {
|
||||
fn headers_available(&mut self, meta_result: Result<Metadata, NetworkError>) {
|
||||
let mut is_ssl_error = false;
|
||||
let metadata = match meta_result {
|
||||
Ok(meta) => Some(meta),
|
||||
Err(NetworkError::SslValidation(url)) => {
|
||||
is_ssl_error = true;
|
||||
let mut meta = Metadata::default(url);
|
||||
let mime: Option<Mime> = "text/html".parse().ok();
|
||||
meta.set_content_type(mime.as_ref());
|
||||
Some(meta)
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
let content_type = metadata.clone().and_then(|meta| meta.content_type);
|
||||
let parser = match ScriptThread::page_fetch_complete(self.id.clone(),
|
||||
self.subpage.clone(),
|
||||
metadata) {
|
||||
Some(parser) => parser,
|
||||
None => return,
|
||||
};
|
||||
|
@ -274,7 +286,15 @@ impl AsyncResponseListener for ParserContext {
|
|||
parser.parse_sync();
|
||||
parser.set_plaintext_state();
|
||||
},
|
||||
Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => {}, // Handle text/html
|
||||
Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html
|
||||
if is_ssl_error {
|
||||
self.is_synthesized_document = true;
|
||||
let page_bytes = read_resource_file("badcert.html").unwrap();
|
||||
let page = String::from_utf8(page_bytes).unwrap();
|
||||
parser.pending_input().borrow_mut().push(page);
|
||||
parser.parse_sync();
|
||||
}
|
||||
},
|
||||
Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) => {}, // Handle text/xml
|
||||
Some(ContentType(Mime(toplevel, sublevel, _))) => {
|
||||
if toplevel.as_str() == "application" && sublevel.as_str() == "xhtml+xml" {
|
||||
|
@ -308,7 +328,7 @@ impl AsyncResponseListener for ParserContext {
|
|||
}
|
||||
}
|
||||
|
||||
fn response_complete(&mut self, status: Result<(), String>) {
|
||||
fn response_complete(&mut self, status: Result<(), NetworkError>) {
|
||||
let parser = match self.parser.as_ref() {
|
||||
Some(parser) => parser.root(),
|
||||
None => return,
|
||||
|
@ -316,7 +336,7 @@ impl AsyncResponseListener for ParserContext {
|
|||
parser.r().document().finish_load(LoadType::PageSource(self.url.clone()));
|
||||
|
||||
if let Err(err) = status {
|
||||
debug!("Failed to load page URL {}, error: {}", self.url.serialize(), err);
|
||||
debug!("Failed to load page URL {}, error: {:?}", self.url.serialize(), err);
|
||||
// TODO(Savago): we should send a notification to callers #5463.
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ use js::jsapi::JS_ClearPendingException;
|
|||
use js::jsapi::{JSContext, JS_ParseJSON, RootedValue};
|
||||
use js::jsval::{JSVal, NullValue, UndefinedValue};
|
||||
use net_traits::ControlMsg::Load;
|
||||
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata};
|
||||
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
|
||||
use net_traits::{LoadConsumer, LoadContext, LoadData, ResourceCORSData, ResourceThread};
|
||||
use network_listener::{NetworkListener, PreInvoke};
|
||||
use parse::html::{ParseContext, parse_html};
|
||||
|
@ -254,7 +254,7 @@ impl XMLHttpRequest {
|
|||
resource_thread: ResourceThread,
|
||||
load_data: LoadData) {
|
||||
impl AsyncResponseListener for XHRContext {
|
||||
fn headers_available(&mut self, metadata: Metadata) {
|
||||
fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
|
||||
let xhr = self.xhr.root();
|
||||
let rv = xhr.process_headers_available(self.cors_request.clone(),
|
||||
self.gen_id,
|
||||
|
@ -269,7 +269,7 @@ impl XMLHttpRequest {
|
|||
self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone());
|
||||
}
|
||||
|
||||
fn response_complete(&mut self, status: Result<(), String>) {
|
||||
fn response_complete(&mut self, status: Result<(), NetworkError>) {
|
||||
let rv = self.xhr.root().process_response_complete(self.gen_id, status);
|
||||
*self.sync_status.borrow_mut() = Some(rv);
|
||||
}
|
||||
|
@ -870,7 +870,15 @@ impl XMLHttpRequest {
|
|||
}
|
||||
|
||||
fn process_headers_available(&self, cors_request: Option<CORSRequest>,
|
||||
gen_id: GenerationId, metadata: Metadata) -> Result<(), Error> {
|
||||
gen_id: GenerationId, metadata: Result<Metadata, NetworkError>)
|
||||
-> Result<(), Error> {
|
||||
let metadata = match metadata {
|
||||
Ok(meta) => meta,
|
||||
Err(_) => {
|
||||
self.process_partial_response(XHRProgress::Errored(gen_id, Error::Network));
|
||||
return Err(Error::Network);
|
||||
},
|
||||
};
|
||||
|
||||
let bypass_cross_origin_check = {
|
||||
// We want to be able to do cross-origin requests in browser.html.
|
||||
|
@ -904,9 +912,7 @@ impl XMLHttpRequest {
|
|||
*self.response_url.borrow_mut() = metadata.final_url.serialize_no_fragment();
|
||||
|
||||
// XXXManishearth Clear cache entries in case of a network error
|
||||
self.process_partial_response(XHRProgress::HeadersReceived(gen_id,
|
||||
metadata.headers,
|
||||
metadata.status));
|
||||
self.process_partial_response(XHRProgress::HeadersReceived(gen_id, metadata.headers, metadata.status));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -914,7 +920,7 @@ impl XMLHttpRequest {
|
|||
self.process_partial_response(XHRProgress::Loading(gen_id, ByteString::new(payload)));
|
||||
}
|
||||
|
||||
fn process_response_complete(&self, gen_id: GenerationId, status: Result<(), String>)
|
||||
fn process_response_complete(&self, gen_id: GenerationId, status: Result<(), NetworkError>)
|
||||
-> ErrorResult {
|
||||
match status {
|
||||
Ok(()) => {
|
||||
|
|
|
@ -492,7 +492,7 @@ pub unsafe extern "C" fn shadow_check_callback(_cx: *mut JSContext,
|
|||
}
|
||||
|
||||
impl ScriptThread {
|
||||
pub fn page_fetch_complete(id: PipelineId, subpage: Option<SubpageId>, metadata: Metadata)
|
||||
pub fn page_fetch_complete(id: PipelineId, subpage: Option<SubpageId>, metadata: Option<Metadata>)
|
||||
-> Option<ParserRoot> {
|
||||
SCRIPT_THREAD_ROOT.with(|root| {
|
||||
let script_thread = unsafe { &*root.borrow().unwrap() };
|
||||
|
@ -1280,7 +1280,7 @@ impl ScriptThread {
|
|||
/// We have received notification that the response associated with a load has completed.
|
||||
/// Kick off the document and frame tree creation process using the result.
|
||||
fn handle_page_fetch_complete(&self, id: PipelineId, subpage: Option<SubpageId>,
|
||||
metadata: Metadata) -> Option<ParserRoot> {
|
||||
metadata: Option<Metadata>) -> Option<ParserRoot> {
|
||||
let idx = self.incomplete_loads.borrow().iter().position(|load| {
|
||||
load.pipeline_id == id && load.parent_info.map(|info| info.1) == subpage
|
||||
});
|
||||
|
@ -1289,7 +1289,7 @@ impl ScriptThread {
|
|||
match idx {
|
||||
Some(idx) => {
|
||||
let load = self.incomplete_loads.borrow_mut().remove(idx);
|
||||
Some(self.load(metadata, load))
|
||||
metadata.map(|meta| self.load(meta, load))
|
||||
}
|
||||
None => {
|
||||
assert!(self.closed_pipelines.borrow().contains(&id));
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
<title>Certificate error</title>
|
||||
</head>
|
||||
<body>
|
||||
<img src="badcert.jpg">
|
||||
<img src="chrome://badcert.jpg">
|
||||
</body>
|
||||
</html>
|
||||
|
|
46
tests/unit/net/chrome_loader.rs
Normal file
46
tests/unit/net/chrome_loader.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 net::chrome_loader::resolve_chrome_url;
|
||||
use url::Url;
|
||||
|
||||
#[test]
|
||||
fn test_relative() {
|
||||
let url = Url::parse("chrome://../something").unwrap();
|
||||
assert!(resolve_chrome_url(&url).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relative_2() {
|
||||
let url = Url::parse("chrome://subdir/../something").unwrap();
|
||||
assert!(resolve_chrome_url(&url).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_absolute() {
|
||||
let url = Url::parse("chrome:///etc/passwd").unwrap();
|
||||
assert!(resolve_chrome_url(&url).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn test_absolute_2() {
|
||||
let url = Url::parse("chrome://C:\\Windows").unwrap();
|
||||
assert!(resolve_chrome_url(&url).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn test_absolute_3() {
|
||||
let url = Url::parse("chrome://\\\\server/C$").unwrap();
|
||||
assert!(resolve_chrome_url(&url).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid() {
|
||||
let url = Url::parse("chrome://badcert.jpg").unwrap();
|
||||
let resolved = resolve_chrome_url(&url).unwrap();
|
||||
assert_eq!(resolved.scheme, "file");
|
||||
}
|
|
@ -7,7 +7,7 @@ extern crate hyper;
|
|||
use ipc_channel::ipc;
|
||||
use net_traits::LoadConsumer::Channel;
|
||||
use net_traits::ProgressMsg::{Payload, Done};
|
||||
use net_traits::{LoadData, LoadContext};
|
||||
use net_traits::{LoadData, LoadContext, NetworkError};
|
||||
use self::hyper::header::ContentType;
|
||||
use self::hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
|
||||
|
||||
|
@ -36,7 +36,7 @@ fn assert_parse(url: &'static str,
|
|||
|
||||
match data {
|
||||
None => {
|
||||
assert_eq!(progress, Done(Err("invalid data uri".to_owned())));
|
||||
assert_eq!(progress, Done(Err(NetworkError::Internal("invalid data uri".to_owned()))));
|
||||
}
|
||||
Some(dat) => {
|
||||
assert_eq!(progress, Payload(dat));
|
||||
|
|
|
@ -20,6 +20,7 @@ use msg::constellation_msg::PipelineId;
|
|||
use net::cookie::Cookie;
|
||||
use net::cookie_storage::CookieStorage;
|
||||
use net::hsts::HstsEntry;
|
||||
use net::http_loader::LoadErrorType;
|
||||
use net::http_loader::{load, LoadError, HttpRequestFactory, HttpRequest, HttpResponse, UIProvider, HttpState};
|
||||
use net::resource_thread::{AuthCacheEntry, CancellationListener};
|
||||
use net_traits::{LoadData, CookieSource, LoadContext, IncludeSubdomains};
|
||||
|
@ -1075,9 +1076,8 @@ fn test_load_errors_when_there_a_redirect_loop() {
|
|||
|
||||
match load(&load_data, &ui_provider, &http_state, None, &Factory,
|
||||
DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) {
|
||||
Err(LoadError::InvalidRedirect(_, msg)) => {
|
||||
assert_eq!(msg, "redirect loop");
|
||||
},
|
||||
Err(ref load_err) if load_err.error == LoadErrorType::InvalidRedirect =>
|
||||
assert_eq!(&load_err.reason, "redirect loop"),
|
||||
_ => panic!("expected max redirects to fail")
|
||||
}
|
||||
}
|
||||
|
@ -1110,10 +1110,11 @@ fn test_load_errors_when_there_is_too_many_redirects() {
|
|||
|
||||
match load(&load_data, &ui_provider, &http_state, None, &Factory,
|
||||
DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None)) {
|
||||
Err(LoadError::MaxRedirects(url, num_redirects)) => {
|
||||
Err(LoadError { error: LoadErrorType::MaxRedirects(num_redirects),
|
||||
url, .. }) => {
|
||||
assert_eq!(num_redirects, redirect_limit as u32);
|
||||
assert_eq!(url.domain().unwrap(), "mozilla.com")
|
||||
},
|
||||
assert_eq!(url.domain().unwrap(), "mozilla.com");
|
||||
}
|
||||
_ => panic!("expected max redirects to fail")
|
||||
}
|
||||
|
||||
|
@ -1166,7 +1167,7 @@ impl HttpRequestFactory for DontConnectFactory {
|
|||
type R = MockRequest;
|
||||
|
||||
fn create(&self, url: Url, _: Method, _: Headers) -> Result<MockRequest, LoadError> {
|
||||
Err(LoadError::Connection(url, "should not have connected".to_owned()))
|
||||
Err(LoadError::new(url, LoadErrorType::Connection, "should not have connected".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1184,7 +1185,7 @@ fn test_load_errors_when_scheme_is_not_http_or_https() {
|
|||
&DontConnectFactory,
|
||||
DEFAULT_USER_AGENT.to_owned(),
|
||||
&CancellationListener::new(None)) {
|
||||
Err(LoadError::UnsupportedScheme(_)) => {}
|
||||
Err(ref load_err) if load_err.error == LoadErrorType::UnsupportedScheme => (),
|
||||
_ => panic!("expected ftp scheme to be unsupported")
|
||||
}
|
||||
}
|
||||
|
@ -1203,7 +1204,7 @@ fn test_load_errors_when_viewing_source_and_inner_url_scheme_is_not_http_or_http
|
|||
&DontConnectFactory,
|
||||
DEFAULT_USER_AGENT.to_owned(),
|
||||
&CancellationListener::new(None)) {
|
||||
Err(LoadError::UnsupportedScheme(_)) => {}
|
||||
Err(ref load_err) if load_err.error == LoadErrorType::UnsupportedScheme => (),
|
||||
_ => panic!("expected ftp scheme to be unsupported")
|
||||
}
|
||||
}
|
||||
|
@ -1245,7 +1246,7 @@ fn test_load_errors_when_cancelled() {
|
|||
&Factory,
|
||||
DEFAULT_USER_AGENT.to_owned(),
|
||||
&cancel_listener) {
|
||||
Err(LoadError::Cancelled(_, _)) => (),
|
||||
Err(ref load_err) if load_err.error == LoadErrorType::Cancelled => (),
|
||||
_ => panic!("expected load cancelled error!")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ extern crate unicase;
|
|||
extern crate url;
|
||||
extern crate util;
|
||||
|
||||
#[cfg(test)] mod chrome_loader;
|
||||
#[cfg(test)] mod cookie;
|
||||
#[cfg(test)] mod data_loader;
|
||||
#[cfg(test)] mod file_loader;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use ipc_channel::ipc;
|
||||
use net::resource_thread::new_resource_thread;
|
||||
use net_traits::hosts::{parse_hostsfile, host_replacement};
|
||||
use net_traits::{ControlMsg, LoadData, LoadConsumer, ProgressMsg, LoadContext};
|
||||
use net_traits::{ControlMsg, LoadData, LoadConsumer, LoadContext, NetworkError, ProgressMsg};
|
||||
use std::borrow::ToOwned;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::channel;
|
||||
|
@ -213,9 +213,7 @@ fn test_cancelled_listener() {
|
|||
// (but, the loading has been cancelled)
|
||||
let _ = body_sender.send(body);
|
||||
let response = receiver.recv().unwrap();
|
||||
match response.progress_port.recv().unwrap() {
|
||||
ProgressMsg::Done(result) => assert_eq!(result.unwrap_err(), "load cancelled".to_owned()),
|
||||
_ => panic!("baaaah!"),
|
||||
}
|
||||
assert_eq!(response.progress_port.recv().unwrap(),
|
||||
ProgressMsg::Done(Err(NetworkError::Internal("load cancelled".to_owned()))));
|
||||
resource_thread.send(ControlMsg::Exit).unwrap();
|
||||
}
|
||||
|
|
|
@ -5779,6 +5779,18 @@
|
|||
"url": "/_mozilla/mozilla/iframe/resize_after_load.html"
|
||||
}
|
||||
],
|
||||
"mozilla/sslfail.html": [
|
||||
{
|
||||
"path": "mozilla/sslfail.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/mozilla/sslfail-ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/mozilla/sslfail.html"
|
||||
}
|
||||
],
|
||||
"mozilla/webgl/clearcolor.html": [
|
||||
{
|
||||
"path": "mozilla/webgl/clearcolor.html",
|
||||
|
@ -12441,6 +12453,18 @@
|
|||
"url": "/_mozilla/mozilla/iframe/resize_after_load.html"
|
||||
}
|
||||
],
|
||||
"mozilla/sslfail.html": [
|
||||
{
|
||||
"path": "mozilla/sslfail.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/mozilla/sslfail-ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/mozilla/sslfail.html"
|
||||
}
|
||||
],
|
||||
"mozilla/webgl/clearcolor.html": [
|
||||
{
|
||||
"path": "mozilla/webgl/clearcolor.html",
|
||||
|
|
|
@ -10,18 +10,14 @@ var t = async_test("Invalid SSL cert noticed");
|
|||
t.step(function() {
|
||||
var target = location.href.replace(HTTP_ORIGIN, HTTPS_ORIGIN)
|
||||
.replace('bad_cert_detected.html',
|
||||
'resources/origin_helpers.js');
|
||||
// Servo currently lacks the ability to introspect any content that is blocked
|
||||
// due to a cert error, so we use a roundabout method to infer that that's happened.
|
||||
// When the worker has a cert failure, that translates into attempting to evaluate the
|
||||
// contents of badcert.html as JS, which triggers an exception that currently does not
|
||||
// propagate to the parent scope. If we _do_ get an error event in the parent scope,
|
||||
// that means that the cert verification was treated no different than any other
|
||||
// network error, since we dispatch an error event in that case.
|
||||
'resources/worker_success.js');
|
||||
var w = new Worker(target);
|
||||
w.addEventListener('error', t.unreached_func("cert not detected as invalid"), false);
|
||||
// We infer that we detected an invalid cert if nothing happens for a few seconds.
|
||||
setTimeout(function() { t.done() }, 3000);
|
||||
// If the script executes successfully, it should send a message. That indicates that
|
||||
// there was no validation failure, which is bad.
|
||||
w.addEventListener('message', t.unreached_func("cert not detected as invalid"), true);
|
||||
// When the worker has a cert failure, that translates into an early error that is reported
|
||||
// to the Worker object.
|
||||
w.addEventListener('error', t.step_func_done(), true);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
var HTTP_PORT = '{{ports[http][0]}}';
|
||||
var HTTPS_PORT = '{{ports[https][0]}}';
|
||||
var ORIGINAL_HOST = '\'{{host}}\'';
|
||||
var ORIGINAL_HOST = '{{host}}';
|
||||
var HTTP_ORIGIN = 'http://' + ORIGINAL_HOST + ':' + HTTP_PORT;
|
||||
var HTTPS_ORIGIN = 'https://' + ORIGINAL_HOST + ':' + HTTPS_PORT;
|
||||
|
|
5
tests/wpt/mozilla/tests/mozilla/resources/ssl.https.html
Normal file
5
tests/wpt/mozilla/tests/mozilla/resources/ssl.https.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
this should be a secure connection
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
postMessage('load succeeded');
|
9
tests/wpt/mozilla/tests/mozilla/sslfail-ref.html
Normal file
9
tests/wpt/mozilla/tests/mozilla/sslfail-ref.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>SSL Failure Reference</title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="about:sslfail"></iframe>
|
||||
</body>
|
||||
</html>
|
17
tests/wpt/mozilla/tests/mozilla/sslfail.html
Normal file
17
tests/wpt/mozilla/tests/mozilla/sslfail.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>SSL Failure</title>
|
||||
<link rel=match href=sslfail-ref.html>
|
||||
<script src="resources/origin_helpers.js?pipe=sub"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
var iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.src = location.href
|
||||
.replace(HTTP_ORIGIN, HTTPS_ORIGIN)
|
||||
.replace('sslfail.html', 'resources/ssl.https.html');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue