Introduce NetworkError::from_hyper_error and from_ssl_error

This commit is contained in:
Anthony Ramine 2017-03-29 15:30:12 +02:00
parent 3345b83fb3
commit 3ed32f8078
4 changed files with 72 additions and 61 deletions

View file

@ -22,6 +22,7 @@ extern crate lazy_static;
extern crate log;
extern crate msg;
extern crate num_traits;
extern crate openssl;
extern crate parse_hosts;
extern crate serde;
#[macro_use]
@ -35,16 +36,19 @@ extern crate webrender_traits;
use cookie_rs::Cookie;
use filemanager_thread::FileManagerThreadMsg;
use heapsize::HeapSizeOf;
use hyper::Error as HyperError;
use hyper::header::{ContentType, Headers, ReferrerPolicy as ReferrerPolicyHeader};
use hyper::http::RawStatus;
use hyper::mime::{Attr, Mime};
use hyper_serde::Serde;
use ipc_channel::Error;
use ipc_channel::Error as IpcError;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use openssl::ssl::error::{OpensslError, SslError};
use request::{Request, RequestInit};
use response::{HttpsState, Response};
use servo_url::ServoUrl;
use std::error::Error;
use storage_thread::StorageThreadMsg;
pub mod blob_url_store;
@ -265,7 +269,7 @@ impl<T: FetchResponseListener> Action<T> for FetchResponseMsg {
/// Handle to a resource thread
pub type CoreResourceThread = IpcSender<CoreResourceMsg>;
pub type IpcSendResult = Result<(), Error>;
pub type IpcSendResult = Result<(), IpcError>;
/// Abstraction of the ability to send a particular type of message,
/// used by net_traits::ResourceThreads to ease the use its IpcSender sub-fields
@ -526,6 +530,69 @@ pub enum NetworkError {
SslValidation(ServoUrl, String),
}
impl NetworkError {
pub fn from_hyper_error(url: &ServoUrl, error: HyperError) -> Self {
if let HyperError::Ssl(ref ssl_error) = error {
if let Some(ssl_error) = ssl_error.downcast_ref::<SslError>() {
return NetworkError::from_ssl_error(url, ssl_error);
}
}
NetworkError::Internal(error.description().to_owned())
}
fn from_ssl_error(url: &ServoUrl, error: &SslError) -> Self {
if let SslError::OpenSslErrors(ref errors) = *error {
if errors.iter().any(is_cert_verify_error) {
let mut error_report = vec![format!("ssl error ({}):", openssl::version::version())];
let mut suggestion = None;
for err in errors {
if is_unknown_message_digest_err(err) {
suggestion = Some("<b>Servo recommends upgrading to a newer OpenSSL version.</b>");
}
error_report.push(format_ssl_error(err));
}
if let Some(suggestion) = suggestion {
error_report.push(suggestion.to_owned());
}
let error_report = error_report.join("<br>\n");
return NetworkError::SslValidation(url.clone(), error_report);
}
}
NetworkError::Internal(error.description().to_owned())
}
}
fn format_ssl_error(error: &OpensslError) -> String {
match error {
&OpensslError::UnknownError { ref library, ref function, ref reason } => {
format!("{}: {} - {}", library, function, reason)
}
}
}
// FIXME: This incredibly hacky. Make it more robust, and at least test it.
fn is_cert_verify_error(error: &OpensslError) -> bool {
match error {
&OpensslError::UnknownError { ref library, ref function, ref reason } => {
library == "SSL routines" &&
function.to_uppercase() == "SSL3_GET_SERVER_CERTIFICATE" &&
reason == "certificate verify failed"
}
}
}
fn is_unknown_message_digest_err(error: &OpensslError) -> bool {
match error {
&OpensslError::UnknownError { ref library, ref function, ref reason } => {
library == "asn1 encoding routines" &&
function == "ASN1_item_verify" &&
reason == "unknown message digest algorithm"
}
}
}
/// Normalize `slice`, as defined by
/// [the Fetch Spec](https://fetch.spec.whatwg.org/#concept-header-value-normalize).
pub fn trim_http_whitespace(mut slice: &[u8]) -> &[u8] {