mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
Refactors http_loader::load to be synchronous w/ an async wrapper
This simplifies the arguments that are passed in and should make testing errors/responses easier once they're mocked servo/servo#6727
This commit is contained in:
parent
67cbda4be3
commit
610ef40105
1 changed files with 85 additions and 59 deletions
|
@ -11,6 +11,9 @@ use net_traits::{ControlMsg, CookieSource, LoadData, Metadata, LoadConsumer, Inc
|
||||||
use resource_task::{start_sending_opt, start_sending_sniffed_opt};
|
use resource_task::{start_sending_opt, start_sending_sniffed_opt};
|
||||||
|
|
||||||
use file_loader;
|
use file_loader;
|
||||||
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
|
use log;
|
||||||
|
use std::collections::HashSet;
|
||||||
use flate2::read::{DeflateDecoder, GzDecoder};
|
use flate2::read::{DeflateDecoder, GzDecoder};
|
||||||
use hyper::Error as HttpError;
|
use hyper::Error as HttpError;
|
||||||
use hyper::client::Request;
|
use hyper::client::Request;
|
||||||
|
@ -37,14 +40,15 @@ use util::task::spawn_named;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::boxed::FnBox;
|
use std::boxed::FnBox;
|
||||||
use uuid;
|
use uuid;
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
pub fn factory(resource_mgr_chan: IpcSender<ControlMsg>,
|
pub fn factory(resource_mgr_chan: IpcSender<ControlMsg>,
|
||||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||||
hsts_list: Arc<Mutex<HSTSList>>)
|
hsts_list: Arc<Mutex<HSTSList>>)
|
||||||
-> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> {
|
-> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> {
|
||||||
box move |load_data: LoadData, senders, classifier| {
|
box move |load_data, senders, classifier| {
|
||||||
spawn_named(format!("http_loader for {}", load_data.url.serialize()),
|
spawn_named(format!("http_loader for {}", load_data.url.serialize()),
|
||||||
move || load(load_data, senders, classifier, resource_mgr_chan, devtools_chan, hsts_list))
|
move || load_for_consumer(load_data, senders, classifier, resource_mgr_chan, devtools_chan, hsts_list))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,12 +89,54 @@ fn request_must_be_secured(hsts_list: &HSTSList, url: &Url) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(mut load_data: LoadData,
|
fn inner_url(url: &Url) -> Url {
|
||||||
|
let inner_url = url.non_relative_scheme_data().unwrap();
|
||||||
|
Url::parse(inner_url).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_for_consumer(load_data: LoadData,
|
||||||
start_chan: LoadConsumer,
|
start_chan: LoadConsumer,
|
||||||
classifier: Arc<MIMEClassifier>,
|
classifier: Arc<MIMEClassifier>,
|
||||||
resource_mgr_chan: IpcSender<ControlMsg>,
|
resource_mgr_chan: IpcSender<ControlMsg>,
|
||||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||||
hsts_list: Arc<Mutex<HSTSList>>) {
|
hsts_list: Arc<Mutex<HSTSList>>) {
|
||||||
|
match load(load_data, resource_mgr_chan, devtools_chan, hsts_list) {
|
||||||
|
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::Client(url, e)) => {
|
||||||
|
send_error(url, e, start_chan)
|
||||||
|
}
|
||||||
|
Err(LoadError::MaxRedirects(url)) => {
|
||||||
|
send_error(url, "too many redirects".to_string(), start_chan)
|
||||||
|
}
|
||||||
|
Err(LoadError::Cors(url, msg)) |
|
||||||
|
Err(LoadError::InvalidRedirect(url, msg)) |
|
||||||
|
Err(LoadError::Decoding(url, msg)) |
|
||||||
|
Err(LoadError::InvalidFile(url, msg)) => {
|
||||||
|
send_error(url, msg, start_chan)
|
||||||
|
}
|
||||||
|
Ok((mut response_reader, metadata)) => {
|
||||||
|
send_data(&mut response_reader, start_chan, metadata, classifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LoadError {
|
||||||
|
UnsupportedScheme(Url),
|
||||||
|
Client(Url, String),
|
||||||
|
Cors(Url, String),
|
||||||
|
InvalidRedirect(Url, String),
|
||||||
|
Decoding(Url, String),
|
||||||
|
InvalidFile(Url, String),
|
||||||
|
MaxRedirects(Url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load(mut load_data: LoadData,
|
||||||
|
resource_mgr_chan: IpcSender<ControlMsg>,
|
||||||
|
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||||
|
hsts_list: Arc<Mutex<HSTSList>>) -> Result<(Box<Read>, Metadata), LoadError> {
|
||||||
// FIXME: At the time of writing this FIXME, servo didn't have any central
|
// FIXME: At the time of writing this FIXME, servo didn't have any central
|
||||||
// location for configuration. If you're reading this and such a
|
// location for configuration. If you're reading this and such a
|
||||||
// repository DOES exist, please update this constant to use it.
|
// repository DOES exist, please update this constant to use it.
|
||||||
|
@ -109,17 +155,13 @@ fn load(mut load_data: LoadData,
|
||||||
// the source rather than rendering the contents of the URL.
|
// the source rather than rendering the contents of the URL.
|
||||||
let viewing_source = url.scheme == "view-source";
|
let viewing_source = url.scheme == "view-source";
|
||||||
if viewing_source {
|
if viewing_source {
|
||||||
let inner_url = load_data.url.non_relative_scheme_data().unwrap();
|
let inner_url = replace_hosts(&inner_url(&load_data.url));
|
||||||
doc_url = Url::parse(inner_url).unwrap();
|
doc_url = inner_url.clone();
|
||||||
url = replace_hosts(&doc_url);
|
if &*inner_url.scheme != "http" && &*inner_url.scheme != "https" {
|
||||||
match &*url.scheme {
|
return Err(LoadError::UnsupportedScheme(inner_url));
|
||||||
"http" | "https" => {}
|
} else {
|
||||||
_ => {
|
url = inner_url;
|
||||||
let s = format!("The {} scheme with view-source is not supported", url.scheme);
|
|
||||||
send_error(url, s, start_chan);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop to handle redirects.
|
// Loop to handle redirects.
|
||||||
|
@ -132,21 +174,16 @@ fn load(mut load_data: LoadData,
|
||||||
}
|
}
|
||||||
|
|
||||||
if iters > max_redirects {
|
if iters > max_redirects {
|
||||||
send_error(url, "too many redirects".to_string(), start_chan);
|
return Err(LoadError::MaxRedirects(url));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match &*url.scheme {
|
if &*url.scheme != "http" && &*url.scheme != "https" {
|
||||||
"http" | "https" => {}
|
return Err(LoadError::UnsupportedScheme(url));
|
||||||
_ => {
|
|
||||||
let s = format!("{} request, but we don't support that scheme", url.scheme);
|
|
||||||
send_error(url, s, start_chan);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("requesting {}", url.serialize());
|
info!("requesting {}", url.serialize());
|
||||||
|
|
||||||
|
// TODO - Is no ssl still needed?
|
||||||
let ssl_err_string = "Some(OpenSslErrors([UnknownError { library: \"SSL routines\", \
|
let ssl_err_string = "Some(OpenSslErrors([UnknownError { library: \"SSL routines\", \
|
||||||
function: \"SSL3_GET_SERVER_CERTIFICATE\", \
|
function: \"SSL3_GET_SERVER_CERTIFICATE\", \
|
||||||
reason: \"certificate verify failed\" }]))";
|
reason: \"certificate verify failed\" }]))";
|
||||||
|
@ -171,14 +208,15 @@ reason: \"certificate verify failed\" }]))";
|
||||||
) => {
|
) => {
|
||||||
let mut image = resources_dir_path();
|
let mut image = resources_dir_path();
|
||||||
image.push("badcert.html");
|
image.push("badcert.html");
|
||||||
let load_data = LoadData::new(Url::from_file_path(&*image).unwrap(), None);
|
let file_url = Url::from_file_path(&*image).unwrap();
|
||||||
file_loader::factory(load_data, start_chan, classifier);
|
|
||||||
return;
|
match File::open(image.clone()) {
|
||||||
|
Ok(f) => return Ok((Box::new(f), Metadata::default(file_url))),
|
||||||
|
Err(_) => return Err(LoadError::InvalidFile(file_url, image.to_str().unwrap().to_string()))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{:?}", e);
|
return Err(LoadError::Client(url, e.description().to_string()));
|
||||||
send_error(url, e.description().to_string(), start_chan);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -237,17 +275,17 @@ reason: \"certificate verify failed\" }]))";
|
||||||
let writer = match load_data.data {
|
let writer = match load_data.data {
|
||||||
Some(ref data) if iters == 1 => {
|
Some(ref data) if iters == 1 => {
|
||||||
req.headers_mut().set(ContentLength(data.len() as u64));
|
req.headers_mut().set(ContentLength(data.len() as u64));
|
||||||
|
|
||||||
let mut writer = match req.start() {
|
let mut writer = match req.start() {
|
||||||
Ok(w) => w,
|
Ok(w) => w,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
send_error(url, e.description().to_string(), start_chan);
|
return Err(LoadError::Client(url, e.description().to_string()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match writer.write_all(&*data) {
|
match writer.write_all(&*data) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
send_error(url, e.description().to_string(), start_chan);
|
return Err(LoadError::Client(url, e.description().to_string()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
@ -261,8 +299,7 @@ reason: \"certificate verify failed\" }]))";
|
||||||
match req.start() {
|
match req.start() {
|
||||||
Ok(w) => w,
|
Ok(w) => w,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
send_error(url, e.description().to_string(), start_chan);
|
return Err(LoadError::Client(url, e.description().to_string()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,11 +318,10 @@ reason: \"certificate verify failed\" }]))";
|
||||||
net_event))).unwrap();
|
net_event))).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut response = match writer.send() {
|
let response = match writer.send() {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
send_error(url, e.description().to_string(), start_chan);
|
return Err(LoadError::Client(url, e.description().to_string()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -337,11 +373,7 @@ reason: \"certificate verify failed\" }]))";
|
||||||
match load_data.cors {
|
match load_data.cors {
|
||||||
Some(ref c) => {
|
Some(ref c) => {
|
||||||
if c.preflight {
|
if c.preflight {
|
||||||
// The preflight lied
|
return Err(LoadError::Cors(url, "Preflight fetch inconsistent with main fetch".to_string()));
|
||||||
send_error(url,
|
|
||||||
"Preflight fetch inconsistent with main fetch".to_string(),
|
|
||||||
start_chan);
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
// XXXManishearth There are some CORS-related steps here,
|
// XXXManishearth There are some CORS-related steps here,
|
||||||
// but they don't seem necessary until credentials are implemented
|
// but they don't seem necessary until credentials are implemented
|
||||||
|
@ -352,8 +384,7 @@ reason: \"certificate verify failed\" }]))";
|
||||||
let new_doc_url = match UrlParser::new().base_url(&doc_url).parse(&new_url) {
|
let new_doc_url = match UrlParser::new().base_url(&doc_url).parse(&new_url) {
|
||||||
Ok(u) => u,
|
Ok(u) => u,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
send_error(doc_url, e.to_string(), start_chan);
|
return Err(LoadError::InvalidRedirect(doc_url, e.to_string()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
info!("redirecting to {}", new_doc_url);
|
info!("redirecting to {}", new_doc_url);
|
||||||
|
@ -368,9 +399,8 @@ reason: \"certificate verify failed\" }]))";
|
||||||
load_data.method = Method::Get;
|
load_data.method = Method::Get;
|
||||||
}
|
}
|
||||||
|
|
||||||
if redirected_to.contains(&doc_url) {
|
if redirected_to.contains(&url) {
|
||||||
send_error(doc_url, "redirect loop".to_string(), start_chan);
|
return Err(LoadError::InvalidRedirect(doc_url, "redirect loop".to_string()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
redirected_to.insert(doc_url.clone());
|
redirected_to.insert(doc_url.clone());
|
||||||
|
@ -384,7 +414,7 @@ reason: \"certificate verify failed\" }]))";
|
||||||
if viewing_source {
|
if viewing_source {
|
||||||
adjusted_headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
|
adjusted_headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
|
||||||
}
|
}
|
||||||
let mut metadata: Metadata = Metadata::default(doc_url);
|
let mut metadata: Metadata = Metadata::default(doc_url.clone());
|
||||||
metadata.set_content_type(match adjusted_headers.get() {
|
metadata.set_content_type(match adjusted_headers.get() {
|
||||||
Some(&ContentType(ref mime)) => Some(mime),
|
Some(&ContentType(ref mime)) => Some(mime),
|
||||||
None => None
|
None => None
|
||||||
|
@ -422,26 +452,22 @@ reason: \"certificate verify failed\" }]))";
|
||||||
if encoding == "gzip" {
|
if encoding == "gzip" {
|
||||||
let result = GzDecoder::new(response);
|
let result = GzDecoder::new(response);
|
||||||
match result {
|
match result {
|
||||||
Ok(mut response_decoding) => {
|
Ok(response_decoding) => {
|
||||||
send_data(&mut response_decoding, start_chan, metadata, classifier);
|
return Ok((Box::new(response_decoding), metadata));
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
send_error(metadata.final_url, err.to_string(), start_chan);
|
return Err(LoadError::Decoding(metadata.final_url, err.to_string()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if encoding == "deflate" {
|
} else if encoding == "deflate" {
|
||||||
let mut response_decoding = DeflateDecoder::new(response);
|
let response_decoding = DeflateDecoder::new(response);
|
||||||
send_data(&mut response_decoding, start_chan, metadata, classifier);
|
return Ok((Box::new(response_decoding), metadata));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
send_data(&mut response, start_chan, metadata, classifier);
|
return Ok((Box::new(response), metadata));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We didn't get redirected.
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue