Skip to content

Commit

Permalink
Implement cancellation listener for cancelling network requests
Browse files Browse the repository at this point in the history
  • Loading branch information
wafflespeanut committed Nov 12, 2015
1 parent 92f9e58 commit 10f5584
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 53 deletions.
9 changes: 6 additions & 3 deletions components/net/about_loader.rs
Expand Up @@ -9,13 +9,16 @@ use hyper::mime::{Mime, SubLevel, TopLevel};
use mime_classifier::MIMEClassifier;
use net_traits::ProgressMsg::Done;
use net_traits::{LoadConsumer, LoadData, Metadata};
use resource_task::{send_error, start_sending_sniffed_opt};
use resource_task::{CancellationListener, send_error, start_sending_sniffed_opt};
use std::fs::PathExt;
use std::sync::Arc;
use url::Url;
use util::resource_files::resources_dir_path;

pub fn factory(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEClassifier>) {
pub fn factory(mut load_data: LoadData,
start_chan: LoadConsumer,
classifier: Arc<MIMEClassifier>,
cancel_listener: CancellationListener) {
match load_data.url.non_relative_scheme_data().unwrap() {
"blank" => {
let metadata = Metadata {
Expand All @@ -42,5 +45,5 @@ pub fn factory(mut load_data: LoadData, start_chan: LoadConsumer, classifier: Ar
return
}
};
file_loader::factory(load_data, start_chan, classifier)
file_loader::factory(load_data, start_chan, classifier, cancel_listener)
}
19 changes: 14 additions & 5 deletions components/net/data_loader.rs
Expand Up @@ -6,21 +6,27 @@ use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use mime_classifier::MIMEClassifier;
use net_traits::ProgressMsg::{Done, Payload};
use net_traits::{LoadConsumer, LoadData, Metadata};
use resource_task::{send_error, start_sending_sniffed_opt};
use resource_task::{CancellationListener, send_error, start_sending_sniffed_opt};
use rustc_serialize::base64::FromBase64;
use std::sync::Arc;
use url::SchemeData;
use url::percent_encoding::percent_decode;

pub fn factory(load_data: LoadData, senders: LoadConsumer, classifier: Arc<MIMEClassifier>) {
pub fn factory(load_data: LoadData,
senders: LoadConsumer,
classifier: Arc<MIMEClassifier>,
cancel_listener: CancellationListener) {
// NB: we don't spawn a new task.
// Hypothesis: data URLs are too small for parallel base64 etc. to be worth it.
// Should be tested at some point.
// Left in separate function to allow easy moving to a task, if desired.
load(load_data, senders, classifier)
load(load_data, senders, classifier, cancel_listener)
}

pub fn load(load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEClassifier>) {
pub fn load(load_data: LoadData,
start_chan: LoadConsumer,
classifier: Arc<MIMEClassifier>,
cancel_listener: CancellationListener) {
let url = load_data.url;
assert!(&*url.scheme == "data");

Expand Down Expand Up @@ -63,8 +69,11 @@ pub fn load(load_data: LoadData, start_chan: LoadConsumer, classifier: Arc<MIMEC
vec!((Attr::Charset, Value::Ext("US-ASCII".to_owned())))));
}

let bytes = percent_decode(parts[1].as_bytes());
if cancel_listener.is_cancelled() {
return;
}

let bytes = percent_decode(parts[1].as_bytes());
let bytes = if is_base64 {
// FIXME(#2909): It’s unclear what to do with non-alphabet characters,
// but Acid 3 apparently depends on spaces being ignored.
Expand Down
35 changes: 28 additions & 7 deletions components/net/file_loader.rs
Expand Up @@ -5,7 +5,8 @@
use mime_classifier::MIMEClassifier;
use net_traits::ProgressMsg::{Done, Payload};
use net_traits::{LoadConsumer, LoadData, Metadata};
use resource_task::{ProgressSender, send_error, start_sending_sniffed, start_sending_sniffed_opt};
use resource_task::{CancellationListener, ProgressSender};
use resource_task::{send_error, start_sending_sniffed, start_sending_sniffed_opt};
use std::borrow::ToOwned;
use std::error::Error;
use std::fs::File;
Expand All @@ -21,6 +22,11 @@ enum ReadStatus {
EOF,
}

enum LoadResult {
Cancelled,
Finished,
}

fn read_block(reader: &mut File) -> Result<ReadStatus, String> {
let mut buf = vec![0; READ_SIZE];
match reader.read(&mut buf) {
Expand All @@ -33,17 +39,24 @@ fn read_block(reader: &mut File) -> Result<ReadStatus, String> {
}
}

fn read_all(reader: &mut File, progress_chan: &ProgressSender)
-> Result<(), String> {
fn read_all(reader: &mut File, progress_chan: &ProgressSender, cancel_listener: &CancellationListener)
-> Result<LoadResult, String> {
loop {
if cancel_listener.is_cancelled() {
return Ok(LoadResult::Cancelled);
}

match try!(read_block(reader)) {
ReadStatus::Partial(buf) => progress_chan.send(Payload(buf)).unwrap(),
ReadStatus::EOF => return Ok(()),
ReadStatus::EOF => return Ok(LoadResult::Finished),
}
}
}

pub fn factory(load_data: LoadData, senders: LoadConsumer, classifier: Arc<MIMEClassifier>) {
pub fn factory(load_data: LoadData,
senders: LoadConsumer,
classifier: Arc<MIMEClassifier>,
cancel_listener: CancellationListener) {
let url = load_data.url;
assert!(&*url.scheme == "file");
spawn_named("file_loader".to_owned(), move || {
Expand All @@ -52,14 +65,22 @@ pub fn factory(load_data: LoadData, senders: LoadConsumer, classifier: Arc<MIMEC
Ok(file_path) => {
match File::open(&file_path) {
Ok(ref mut reader) => {
if cancel_listener.is_cancelled() {
return;
}
match read_block(reader) {
Ok(ReadStatus::Partial(buf)) => {
let metadata = Metadata::default(url);
let progress_chan = start_sending_sniffed(senders, metadata,
classifier, &buf);
progress_chan.send(Payload(buf)).unwrap();
let res = read_all(reader, &progress_chan);
let _ = progress_chan.send(Done(res));
let read_result = read_all(reader, &progress_chan, &cancel_listener);
if let Ok(load_result) = read_result {
match load_result {
LoadResult::Cancelled => return,
LoadResult::Finished => progress_chan.send(Done(Ok(()))).unwrap(),
}
}
}
Ok(ReadStatus::EOF) => {
let metadata = Metadata::default(url);
Expand Down
24 changes: 17 additions & 7 deletions components/net/http_loader.rs
Expand Up @@ -28,7 +28,7 @@ use net_traits::ProgressMsg::{Done, Payload};
use net_traits::hosts::replace_hosts;
use net_traits::{CookieSource, IncludeSubdomains, LoadConsumer, LoadData, Metadata};
use openssl::ssl::{SSL_VERIFY_PEER, SslContext, SslMethod};
use resource_task::{send_error, start_sending_sniffed_opt};
use resource_task::{CancellationListener, send_error, start_sending_sniffed_opt};
use std::borrow::ToOwned;
use std::boxed::FnBox;
use std::collections::HashSet;
Expand Down Expand Up @@ -59,8 +59,11 @@ pub fn factory(user_agent: String,
cookie_jar: Arc<RwLock<CookieStorage>>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
connector: Arc<Pool<Connector>>)
-> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> {
box move |load_data: LoadData, senders, classifier| {
-> Box<FnBox(LoadData,
LoadConsumer,
Arc<MIMEClassifier>,
CancellationListener) + Send> {
box move |load_data: LoadData, senders, classifier, cancel_listener| {
spawn_named(format!("http_loader for {}", load_data.url.serialize()), move || {
load_for_consumer(load_data,
senders,
Expand All @@ -69,6 +72,7 @@ pub fn factory(user_agent: String,
hsts_list,
cookie_jar,
devtools_chan,
cancel_listener,
user_agent)
})
}
Expand Down Expand Up @@ -104,6 +108,7 @@ fn load_for_consumer(load_data: LoadData,
hsts_list: Arc<RwLock<HSTSList>>,
cookie_jar: Arc<RwLock<CookieStorage>>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
cancel_listener: CancellationListener,
user_agent: String) {

let factory = NetworkHttpRequestFactory {
Expand Down Expand Up @@ -132,13 +137,12 @@ fn load_for_consumer(load_data: LoadData,
image.push("badcert.html");
let load_data = LoadData::new(Url::from_file_path(&*image).unwrap(), None);

file_loader::factory(load_data, start_chan, classifier)

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(&mut load_response, start_chan, metadata, classifier)
send_data(&mut load_response, start_chan, metadata, classifier, cancel_listener)
}
}
}
Expand Down Expand Up @@ -717,7 +721,8 @@ pub fn load<A>(load_data: LoadData,
fn send_data<R: Read>(reader: &mut R,
start_chan: LoadConsumer,
metadata: Metadata,
classifier: Arc<MIMEClassifier>) {
classifier: Arc<MIMEClassifier>,
cancel_listener: CancellationListener) {
let (progress_chan, mut chunk) = {
let buf = match read_block(reader) {
Ok(ReadResult::Payload(buf)) => buf,
Expand All @@ -731,6 +736,11 @@ fn send_data<R: Read>(reader: &mut R,
};

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

if progress_chan.send(Payload(chunk)).is_err() {
// The send errors when the receiver is out of scope,
// which will happen if the fetch has timed out (or has been aborted)
Expand Down
3 changes: 2 additions & 1 deletion components/net/image_cache_task.rs
Expand Up @@ -428,7 +428,8 @@ impl ImageCache {
sender: action_sender,
};
let msg = ControlMsg::Load(load_data,
LoadConsumer::Listener(response_target));
LoadConsumer::Listener(response_target),
None);
let progress_sender = self.progress_sender.clone();
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
let action: ResponseAction = message.to().unwrap();
Expand Down

0 comments on commit 10f5584

Please sign in to comment.