diff options
author | Jeena Lee <ijeenalee@gmail.com> | 2016-09-06 10:54:06 -0700 |
---|---|---|
committer | Jeena Lee <ijeenalee@gmail.com> | 2016-09-29 08:19:41 -0700 |
commit | 3216009731664d5099049267bca7a79e6af22196 (patch) | |
tree | f31a83d63aaa1b6208120c0cc2fe31753a7c32f7 /components/script/fetch.rs | |
parent | a03a5e814a7003ba9697aa37d7492cf9e6425ea4 (diff) | |
download | servo-3216009731664d5099049267bca7a79e6af22196.tar.gz servo-3216009731664d5099049267bca7a79e6af22196.zip |
Implement the Fetch method
Diffstat (limited to 'components/script/fetch.rs')
-rw-r--r-- | components/script/fetch.rs | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/components/script/fetch.rs b/components/script/fetch.rs new file mode 100644 index 00000000000..1117d050319 --- /dev/null +++ b/components/script/fetch.rs @@ -0,0 +1,172 @@ +/* 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 dom::bindings::codegen::Bindings::RequestBinding::RequestInit; +use dom::bindings::codegen::Bindings::ResponseBinding::ResponseBinding::ResponseMethods; +use dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType; +use dom::bindings::codegen::UnionTypes::RequestOrUSVString; +use dom::bindings::error::Error; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::Root; +use dom::bindings::refcounted::{Trusted, TrustedPromise}; +use dom::bindings::reflector::Reflectable; +use dom::headers::Guard; +use dom::promise::Promise; +use dom::request::Request; +use dom::response::Response; +use ipc_channel::ipc; +use ipc_channel::router::ROUTER; +use js::jsapi::JSAutoCompartment; +use net_traits::{FetchResponseListener, NetworkError}; +use net_traits::{FilteredMetadata, FetchMetadata, Metadata}; +use net_traits::CoreResourceMsg::Fetch as NetTraitsFetch; +use net_traits::request::Request as NetTraitsRequest; +use net_traits::request::RequestInit as NetTraitsRequestInit; +use network_listener::{NetworkListener, PreInvoke}; +use std::rc::Rc; +use std::sync::{Arc, Mutex}; +use url::Url; + +struct FetchContext { + fetch_promise: Option<TrustedPromise>, + response_object: Trusted<Response>, +} + +fn from_referrer_to_referrer_url(request: &NetTraitsRequest) -> Option<Url> { + let referrer = request.referrer.borrow(); + referrer.to_url().map(|url| url.clone()) +} + +fn request_init_from_request(request: NetTraitsRequest) -> NetTraitsRequestInit { + NetTraitsRequestInit { + method: request.method.borrow().clone(), + url: request.url(), + headers: request.headers.borrow().clone(), + unsafe_request: request.unsafe_request, + same_origin_data: request.same_origin_data.get(), + body: request.body.borrow().clone(), + type_: request.type_, + destination: request.destination, + synchronous: request.synchronous, + mode: request.mode, + use_cors_preflight: request.use_cors_preflight, + credentials_mode: request.credentials_mode, + use_url_credentials: request.use_url_credentials, + // TODO: NetTraitsRequestInit and NetTraitsRequest have different "origin" + // ... NetTraitsRequestInit.origin: Url + // ... NetTraitsRequest.origin: RefCell<Origin> + origin: request.url(), + referrer_url: from_referrer_to_referrer_url(&request), + referrer_policy: request.referrer_policy.get(), + pipeline_id: request.pipeline_id.get(), + } +} + +// https://fetch.spec.whatwg.org/#fetch-method +#[allow(unrooted_must_root)] +pub fn Fetch(global: GlobalRef, input: RequestOrUSVString, init: &RequestInit) -> Rc<Promise> { + let core_resource_thread = global.core_resource_thread(); + + // Step 1 + let promise = Promise::new(global); + let response = Response::new(global); + + // Step 2 + let request = match Request::Constructor(global, input, init) { + Err(e) => { + promise.reject_error(promise.global().r().get_cx(), e); + return promise; + }, + Ok(r) => r.get_request(), + }; + let request_init = request_init_from_request(request); + + // Step 3 + response.Headers().set_guard(Guard::Immutable); + + // Step 4 + let (action_sender, action_receiver) = ipc::channel().unwrap(); + let fetch_context = Arc::new(Mutex::new(FetchContext { + fetch_promise: Some(TrustedPromise::new(promise.clone())), + response_object: Trusted::new(&*response), + })); + let listener = NetworkListener { + context: fetch_context, + script_chan: global.networking_task_source(), + wrapper: None, + }; + + ROUTER.add_route(action_receiver.to_opaque(), box move |message| { + listener.notify_fetch(message.to().unwrap()); + }); + core_resource_thread.send(NetTraitsFetch(request_init, action_sender)).unwrap(); + + promise +} + +impl PreInvoke for FetchContext {} + +impl FetchResponseListener for FetchContext { + fn process_request_body(&mut self) { + // TODO + } + + fn process_request_eof(&mut self) { + // TODO + } + + #[allow(unrooted_must_root)] + fn process_response(&mut self, fetch_metadata: Result<FetchMetadata, NetworkError>) { + let promise = self.fetch_promise.take().expect("fetch promise is missing").root(); + + // JSAutoCompartment needs to be manually made. + // Otherwise, Servo will crash. + let promise_cx = promise.global().r().get_cx(); + let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get()); + match fetch_metadata { + // Step 4.1 + Err(_) => { + promise.reject_error( + promise.global().r().get_cx(), + Error::Type("Network error occurred".to_string())); + self.fetch_promise = Some(TrustedPromise::new(promise)); + return; + }, + // Step 4.2 + Ok(metadata) => { + match metadata { + FetchMetadata::Unfiltered(m) => + fill_headers_with_metadata(self.response_object.root(), m), + FetchMetadata::Filtered { filtered, .. } => match filtered { + FilteredMetadata::Transparent(m) => + fill_headers_with_metadata(self.response_object.root(), m), + FilteredMetadata::Opaque => + self.response_object.root().set_type(DOMResponseType::Opaque), + } + } + } + } + // Step 4.3 + promise.resolve_native( + promise_cx, + &self.response_object.root()); + self.fetch_promise = Some(TrustedPromise::new(promise)); + } + + fn process_response_chunk(&mut self, mut chunk: Vec<u8>) { + // TODO when body is implemented + // ... this will append the chunk to Response's body. + } + + fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + // TODO + // ... trailerObject is not supported in Servo yet. + } +} + +fn fill_headers_with_metadata(r: Root<Response>, m: Metadata) { + r.set_headers(m.headers); + r.set_raw_status(m.status); + r.set_final_url(m.final_url); +} |