diff options
Diffstat (limited to 'runtime/ops')
-rw-r--r-- | runtime/ops/mod.rs | 2 | ||||
-rw-r--r-- | runtime/ops/web_worker.rs | 16 | ||||
-rw-r--r-- | runtime/ops/web_worker/sync_fetch.rs | 155 | ||||
-rw-r--r-- | runtime/ops/worker_host.rs | 16 |
4 files changed, 189 insertions, 0 deletions
diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs index e08ddd1c0..ee2bc0a1e 100644 --- a/runtime/ops/mod.rs +++ b/runtime/ops/mod.rs @@ -84,3 +84,5 @@ pub fn check_unstable2(state: &Rc<RefCell<OpState>>, api_name: &str) { let state = state.borrow(); state.borrow::<UnstableChecker>().check_unstable(api_name) } + +pub struct TestingFeaturesEnabled(pub bool); diff --git a/runtime/ops/web_worker.rs b/runtime/ops/web_worker.rs index 026e38157..8439e4384 100644 --- a/runtime/ops/web_worker.rs +++ b/runtime/ops/web_worker.rs @@ -1,6 +1,9 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +mod sync_fetch; + use crate::web_worker::WebWorkerInternalHandle; +use crate::web_worker::WebWorkerType; use crate::web_worker::WorkerControlEvent; use deno_core::error::generic_error; use deno_core::error::AnyError; @@ -13,6 +16,8 @@ use deno_web::JsMessageData; use std::cell::RefCell; use std::rc::Rc; +use self::sync_fetch::op_worker_sync_fetch; + pub fn init() -> Extension { Extension::builder() .ops(vec![ @@ -25,6 +30,8 @@ pub fn init() -> Extension { "op_worker_unhandled_error", op_sync(op_worker_unhandled_error), ), + ("op_worker_get_type", op_sync(op_worker_get_type)), + ("op_worker_sync_fetch", op_sync(op_worker_sync_fetch)), ]) .build() } @@ -79,3 +86,12 @@ fn op_worker_unhandled_error( .expect("Failed to propagate error event to parent worker"); Ok(()) } + +fn op_worker_get_type( + state: &mut OpState, + _: (), + _: (), +) -> Result<WebWorkerType, AnyError> { + let handle = state.borrow::<WebWorkerInternalHandle>().clone(); + Ok(handle.worker_type) +} diff --git a/runtime/ops/web_worker/sync_fetch.rs b/runtime/ops/web_worker/sync_fetch.rs new file mode 100644 index 000000000..6ad6edba7 --- /dev/null +++ b/runtime/ops/web_worker/sync_fetch.rs @@ -0,0 +1,155 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +use crate::web_worker::WebWorkerInternalHandle; +use crate::web_worker::WebWorkerType; +use deno_core::error::type_error; +use deno_core::error::AnyError; +use deno_core::url::Url; +use deno_core::OpState; +use deno_fetch::data_url::DataUrl; +use deno_fetch::reqwest; +use deno_web::BlobStore; +use deno_websocket::DomExceptionNetworkError; +use hyper::body::Bytes; +use serde::{Deserialize, Serialize}; +use tokio::task::JoinHandle; + +// TODO(andreubotella) Properly parse the MIME type +fn mime_type_essence(mime_type: &str) -> String { + let essence = match mime_type.split_once(";") { + Some((essence, _)) => essence, + None => mime_type, + }; + essence.trim().to_ascii_lowercase() +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SyncFetchScript { + url: String, + script: String, +} + +pub fn op_worker_sync_fetch( + state: &mut OpState, + scripts: Vec<String>, + mut loose_mime_checks: bool, +) -> Result<Vec<SyncFetchScript>, AnyError> { + let handle = state.borrow::<WebWorkerInternalHandle>().clone(); + assert_eq!(handle.worker_type, WebWorkerType::Classic); + + // TODO(andreubotella) Make the runtime into a resource and add a new op to + // block on each request, so a script can run while the next loads. + + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_io() + .enable_time() + .build() + .unwrap(); + + // TODO(andreubotella) It's not good to throw an exception related to blob + // URLs when none of the script URLs use the blob scheme. + // Also, in which contexts are blob URLs not supported? + let blob_store = state.try_borrow::<BlobStore>().ok_or_else(|| { + type_error("Blob URLs are not supported in this context.") + })?; + + let handles: Vec<_> = scripts + .into_iter() + .map(|script| -> JoinHandle<Result<SyncFetchScript, AnyError>> { + let blob_store = blob_store.clone(); + runtime.spawn(async move { + let script_url = + Url::parse(&script).map_err(|_| type_error("Invalid script URL"))?; + + let (body, mime_type, res_url) = match script_url.scheme() { + "http" | "https" => { + let resp = reqwest::get(script_url).await?.error_for_status()?; + + let res_url = resp.url().to_string(); + + // TODO(andreubotella) Properly run fetch's "extract a MIME type". + let mime_type = resp + .headers() + .get("Content-Type") + .and_then(|v| v.to_str().ok()) + .map(mime_type_essence); + + // Always check the MIME type with HTTP(S). + loose_mime_checks = false; + + let body = resp.bytes().await?; + + (body, mime_type, res_url) + } + "data" => { + let data_url = DataUrl::process(&script) + .map_err(|e| type_error(format!("{:?}", e)))?; + + let mime_type = { + let mime = data_url.mime_type(); + format!("{}/{}", mime.type_, mime.subtype) + }; + + let (body, _) = data_url + .decode_to_vec() + .map_err(|e| type_error(format!("{:?}", e)))?; + + (Bytes::from(body), Some(mime_type), script) + } + "blob" => { + let blob = blob_store + .get_object_url(script_url)? + .ok_or_else(|| type_error("Blob for the given URL not found."))?; + + let mime_type = mime_type_essence(&blob.media_type); + + let body = blob.read_all().await?; + + (Bytes::from(body), Some(mime_type), script) + } + _ => { + return Err(type_error(format!( + "Classic scripts with scheme {}: are not supported in workers.", + script_url.scheme() + ))) + } + }; + + if !loose_mime_checks { + // TODO(andreubotella) Check properly for a Javascript MIME type. + match mime_type.as_deref() { + Some("application/javascript" | "text/javascript") => {} + Some(mime_type) => { + return Err( + DomExceptionNetworkError { + msg: format!("Invalid MIME type {:?}.", mime_type), + } + .into(), + ) + } + None => { + return Err( + DomExceptionNetworkError::new("Missing MIME type.").into(), + ) + } + } + } + + let (text, _) = encoding_rs::UTF_8.decode_with_bom_removal(&body); + + Ok(SyncFetchScript { + url: res_url, + script: text.into_owned(), + }) + }) + }) + .collect(); + + let mut ret = Vec::with_capacity(handles.len()); + for handle in handles { + let script = runtime.block_on(handle)??; + ret.push(script); + } + Ok(ret) +} diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs index 5315ff5c7..d80a39502 100644 --- a/runtime/ops/worker_host.rs +++ b/runtime/ops/worker_host.rs @@ -1,5 +1,6 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use crate::ops::TestingFeaturesEnabled; use crate::permissions::resolve_read_allowlist; use crate::permissions::resolve_write_allowlist; use crate::permissions::EnvDescriptor; @@ -16,6 +17,7 @@ use crate::web_worker::run_web_worker; use crate::web_worker::SendableWebWorkerHandle; use crate::web_worker::WebWorker; use crate::web_worker::WebWorkerHandle; +use crate::web_worker::WebWorkerType; use crate::web_worker::WorkerControlEvent; use crate::web_worker::WorkerId; use deno_core::error::custom_error; @@ -48,6 +50,7 @@ pub struct CreateWebWorkerArgs { pub permissions: Permissions, pub main_module: ModuleSpecifier, pub use_deno_namespace: bool, + pub worker_type: WebWorkerType, } pub type CreateWebWorkerCb = dyn Fn(CreateWebWorkerArgs) -> (WebWorker, SendableWebWorkerHandle) @@ -460,6 +463,7 @@ pub struct CreateWorkerArgs { source_code: String, specifier: String, use_deno_namespace: bool, + worker_type: WebWorkerType, } /// Create worker as the host @@ -479,6 +483,17 @@ fn op_create_worker( if use_deno_namespace { super::check_unstable(state, "Worker.deno.namespace"); } + let worker_type = args.worker_type; + if let WebWorkerType::Classic = worker_type { + if let TestingFeaturesEnabled(false) = state.borrow() { + return Err( + deno_webstorage::DomExceptionNotSupportedError::new( + "Classic workers are not supported.", + ) + .into(), + ); + } + } let parent_permissions = state.borrow::<Permissions>().clone(); let worker_permissions = if let Some(permissions) = args.permissions { super::check_unstable(state, "Worker.deno.permissions"); @@ -518,6 +533,7 @@ fn op_create_worker( permissions: worker_permissions, main_module: module_specifier.clone(), use_deno_namespace, + worker_type, }); // Send thread safe handle from newly created worker to host thread |