Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement async fetching of extenal script sources via interruptible parsing. #5197

Closed
wants to merge 14 commits into from

Remove old implementation of XHR's fetch.

  • Loading branch information
jdm committed Mar 5, 2015
commit ae37b88010796dc1fb0f24d9e452f51f30a33b8f
@@ -26,7 +26,7 @@ use dom::urlsearchparams::URLSearchParamsHelpers;
use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget;
use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTargetTypeId;
use dom::xmlhttprequestupload::XMLHttpRequestUpload;
use script_task::{ScriptTask, ScriptChan, ScriptMsg, Runnable, ScriptPort};
use script_task::{ScriptChan, ScriptMsg, Runnable, ScriptPort};

use encoding::all::UTF_8;
use encoding::label::encoding_from_whatwg_label;
@@ -46,7 +46,7 @@ use net::resource_task::{ResourceTask, ResourceCORSData, LoadData, LoadConsumer,
use net::resource_task::{AsyncResponseListener, AsyncResponseTarget};
use net::resource_task::ListenerWrapper;
use net::resource_task::ControlMsg::Load;
use net::resource_task::ProgressMsg::{Payload, Done};
use net::resource_task::ProgressMsg::Done;
use cors::{allow_cross_origin_request, CORSRequest, RequestMode, AsyncCORSResponseListener};
use cors::{AsyncCORSResponseTarget, CORSListenerWrapper, CORSResponse};
use util::str::DOMString;
@@ -55,7 +55,6 @@ use util::task::spawn_named;
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::{RefCell, Cell};
use std::sync::mpsc::{Sender, Receiver, channel};
use std::default::Default;
use std::old_io::Timer;
use std::str::FromStr;
@@ -115,11 +114,6 @@ impl XHRProgress {
}
}

enum TerminateReason {
AbortedOrReopened,
TimedOut,
}

#[dom_struct]
pub struct XMLHttpRequest {
eventtarget: XMLHttpRequestEventTarget,
@@ -148,7 +142,6 @@ pub struct XMLHttpRequest {
global: GlobalField,
timer: DOMRefCell<Timer>,
fetch_time: Cell<i64>,
terminate_sender: DOMRefCell<Option<Sender<TerminateReason>>>,
timeout_target: DOMRefCell<Option<Box<ScriptChan+Send>>>,
generation_id: Cell<GenerationId>,
response_status: Cell<Result<(), ()>>,
@@ -183,7 +176,6 @@ impl XMLHttpRequest {
global: GlobalField::from_rooted(&global),
timer: DOMRefCell::new(Timer::new().unwrap()),
fetch_time: Cell::new(0),
terminate_sender: DOMRefCell::new(None),
timeout_target: DOMRefCell::new(None),
generation_id: Cell::new(GenerationId(0)),
response_status: Cell::new(Ok(())),
@@ -339,128 +331,6 @@ impl XMLHttpRequest {
};
resource_task.send(Load(load_data, LoadConsumer::Listener(listener))).unwrap();
}

#[allow(unsafe_blocks)]
fn fetch(xhr: JSRef<XMLHttpRequest>, resource_task: ResourceTask,
mut load_data: LoadData, terminate_receiver: Receiver<TerminateReason>,
cors_request: Result<Option<CORSRequest>,()>, gen_id: GenerationId)
-> ErrorResult {

macro_rules! notify_error_and_return(
($err:expr) => ({
xhr.process_partial_response(XHRProgress::Errored(gen_id, $err));
return Err($err)
});
);

macro_rules! terminate(
($reason:expr) => (
match $reason {
TerminateReason::AbortedOrReopened => {
return Err(Abort)
}
TerminateReason::TimedOut => {
notify_error_and_return!(Timeout);
}
}
);
);


match cors_request {
Err(_) => {
// Happens in case of cross-origin non-http URIs
notify_error_and_return!(Network);
}

Ok(Some(ref req)) => {
let (chan, cors_port) = channel();
let req2 = req.clone();
// TODO: this exists only to make preflight check non-blocking
// perhaps should be handled by the resource_loader?
spawn_named("XHR:Cors".to_owned(), move || {
let response = req2.http_fetch();
chan.send(response).unwrap();
});

select! (
response = cors_port.recv() => {
let response = response.unwrap();
if response.network_error {
notify_error_and_return!(Network);
} else {
load_data.cors = Some(ResourceCORSData {
preflight: req.preflight_flag,
origin: req.origin.clone()
});
}
},
reason = terminate_receiver.recv() => terminate!(reason.unwrap())
);
}
_ => {}
}

// Step 10, 13
let (start_chan, start_port) = channel();
resource_task.send(Load(load_data, LoadConsumer::Channel(start_chan))).unwrap();


let progress_port;
select! (
response = start_port.recv() => {
let response = response.unwrap();
match cors_request {
Ok(Some(ref req)) => {
match response.metadata.headers {
Some(ref h) if allow_cross_origin_request(req, h) => {},
_ => notify_error_and_return!(Network)
}
},

_ => {}
};
// XXXManishearth Clear cache entries in case of a network error
xhr.process_partial_response(XHRProgress::HeadersReceived(gen_id,
response.metadata.headers.clone(), response.metadata.status.clone()));

progress_port = response.progress_port;
},
reason = terminate_receiver.recv() => terminate!(reason.unwrap())
);

let mut buf = vec!();
loop {
// Under most circumstances, progress_port will contain lots of Payload
// events. Since select! does not have any fairness or priority, it
// might always remove the progress_port event, even when there is
// a terminate event waiting in the terminate_receiver. If this happens,
// a timeout or abort will take too long to be processed. To avoid this,
// in each iteration, we check for a terminate event before we block.
match terminate_receiver.try_recv() {
Ok(reason) => terminate!(reason),
Err(_) => ()
};

select! (
progress = progress_port.recv() => match progress.unwrap() {
Payload(data) => {
buf.push_all(data.as_slice());
xhr.process_partial_response(XHRProgress::Loading(
gen_id, ByteString::new(buf.clone())));
},
Done(Ok(())) => {
xhr.process_partial_response(XHRProgress::Done(gen_id));
return Ok(());
},
Done(Err(_)) => {
notify_error_and_return!(Network);
}
},
reason = terminate_receiver.recv() => terminate!(reason.unwrap())
);
}
}
}

impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
@@ -709,8 +579,6 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
}

load_data.method = (*self.request_method.borrow()).clone();
let (terminate_sender, terminate_receiver) = channel();
*self.terminate_sender.borrow_mut() = Some(terminate_sender);

// CORS stuff
let global = self.global.root();
@@ -1065,7 +933,6 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> {
fn terminate_ongoing_fetch(self) {
let GenerationId(prev_id) = self.generation_id.get();
self.generation_id.set(GenerationId(prev_id + 1));
self.terminate_sender.borrow().as_ref().map(|s| s.send(TerminateReason::AbortedOrReopened));
*self.timeout_target.borrow_mut() = None;
self.response_status.set(Ok(()));
}
@@ -1124,7 +991,6 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> {
// This will cancel all previous timeouts
let oneshot = self.timer.borrow_mut()
.oneshot(Duration::milliseconds(timeout as i64));
let terminate_sender = (*self.terminate_sender.borrow()).clone();
let timeout_target = (*self.timeout_target.borrow().as_ref().unwrap()).clone();
let global = self.global.root();
let xhr = Trusted::new(global.r().get_cx(), self, global.r().script_chan());
@@ -1136,7 +1002,6 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> {
xhr: xhr,
gen_id: gen_id,
}));
terminate_sender.map(|s| s.send(TerminateReason::TimedOut));
},
Err(_) => {
// This occurs if xhr.timeout (the sender) goes out of scope (i.e, xhr went out of scope)
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.