Skip to content

Commit

Permalink
sync fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
crowlKats committed Oct 19, 2024
1 parent 2dcf7d4 commit bfa2b9c
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 35 deletions.
19 changes: 19 additions & 0 deletions runtime/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::ops::os::OsError;
use crate::ops::process::ProcessError;
use crate::ops::signal::SignalError;
use crate::ops::tty::TtyError;
use crate::ops::web_worker::SyncFetchError;
use crate::ops::worker_host::CreateWorkerError;
use deno_broadcast_channel::BroadcastChannelError;
use deno_cache::CacheError;
Expand Down Expand Up @@ -956,6 +957,7 @@ fn get_websocket_upgrade_error(error: &WebSocketUpgradeError) -> &'static str {
WebSocketUpgradeError::UpgradeBufferAlreadyCompleted => "Http",
}
}

fn get_os_error(error: &OsError) -> &'static str {
match error {
OsError::Permission(e) => get_error_class_name(e).unwrap_or("Error"),
Expand All @@ -968,6 +970,22 @@ fn get_os_error(error: &OsError) -> &'static str {
}
}

fn get_sync_fetch_error(error: &SyncFetchError) -> &'static str {
match error {
SyncFetchError::BlobUrlsNotSupportedInContext => "TypeError",
SyncFetchError::Io(e) => get_io_error_class(e),
SyncFetchError::InvalidScriptUrl => "TypeError",
SyncFetchError::InvalidStatusCode(_) => "TypeError",
SyncFetchError::ClassicScriptSchemeUnsupportedInWorkers(_) => "TypeError",
SyncFetchError::InvalidUri(_) => "Error",
SyncFetchError::InvalidMimeType(_) => "DOMExceptionNetworkError",
SyncFetchError::MissingMimeType => "DOMExceptionNetworkError",
SyncFetchError::Fetch(e) => get_fetch_error(e),
SyncFetchError::Join(_) => "Error",
SyncFetchError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
}
}

pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
deno_core::error::get_custom_error_class(e)
.or_else(|| e.downcast_ref::<NApiError>().map(get_napi_error_class))
Expand All @@ -983,6 +1001,7 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
.or_else(|| e.downcast_ref::<HttpStartError>().map(get_http_start_error))
.or_else(|| e.downcast_ref::<ProcessError>().map(get_process_error))
.or_else(|| e.downcast_ref::<OsError>().map(get_os_error))
.or_else(|| e.downcast_ref::<SyncFetchError>().map(get_sync_fetch_error))
.or_else(|| {
e.downcast_ref::<CompressionError>()
.map(get_web_compression_error_class)
Expand Down
1 change: 1 addition & 0 deletions runtime/ops/web_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::cell::RefCell;
use std::rc::Rc;

use self::sync_fetch::op_worker_sync_fetch;
pub use sync_fetch::SyncFetchError;

deno_core::extension!(
deno_web_worker,
Expand Down
90 changes: 55 additions & 35 deletions runtime/ops/web_worker/sync_fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ use std::sync::Arc;

use crate::web_worker::WebWorkerInternalHandle;
use crate::web_worker::WebWorkerType;
use deno_core::error::custom_error;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::futures::StreamExt;
use deno_core::op2;
use deno_core::url::Url;
use deno_core::OpState;
use deno_fetch::data_url::DataUrl;
use deno_fetch::FetchError;
use deno_web::BlobStore;
use http_body_util::BodyExt;
use hyper::body::Bytes;
Expand All @@ -27,6 +25,32 @@ fn mime_type_essence(mime_type: &str) -> String {
essence.trim().to_ascii_lowercase()
}

#[derive(Debug, thiserror::Error)]
pub enum SyncFetchError {
#[error("Blob URLs are not supported in this context.")]
BlobUrlsNotSupportedInContext,
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("Invalid script URL")]
InvalidScriptUrl,
#[error("http status error: {0}")]
InvalidStatusCode(http::StatusCode),
#[error("Classic scripts with scheme {0}: are not supported in workers")]
ClassicScriptSchemeUnsupportedInWorkers(String),
#[error("{0}")]
InvalidUri(#[from] http::uri::InvalidUri),
#[error("Invalid MIME type {0:?}.")]
InvalidMimeType(String),
#[error("Missing MIME type.")]
MissingMimeType,
#[error(transparent)]
Fetch(#[from] FetchError),
#[error(transparent)]
Join(#[from] tokio::task::JoinError),
#[error(transparent)]
Other(deno_core::error::AnyError),
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncFetchScript {
Expand All @@ -40,21 +64,22 @@ pub fn op_worker_sync_fetch(
state: &mut OpState,
#[serde] scripts: Vec<String>,
loose_mime_checks: bool,
) -> Result<Vec<SyncFetchScript>, AnyError> {
) -> Result<Vec<SyncFetchScript>, SyncFetchError> {
let handle = state.borrow::<WebWorkerInternalHandle>().clone();
assert_eq!(handle.worker_type, WebWorkerType::Classic);

// it's not safe to share a client across tokio runtimes, so create a fresh one
// https://github.com/seanmonstar/reqwest/issues/1148#issuecomment-910868788
let options = state.borrow::<deno_fetch::Options>().clone();
let client = deno_fetch::create_client_from_options(&options)?;
let client = deno_fetch::create_client_from_options(&options)
.map_err(FetchError::ClientCreate)?;

// 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::<Arc<BlobStore>>()
.ok_or_else(|| type_error("Blob URLs are not supported in this context."))?
.ok_or(SyncFetchError::BlobUrlsNotSupportedInContext)?
.clone();

// TODO(andreubotella): make the below thread into a resource that can be
Expand All @@ -74,7 +99,7 @@ pub fn op_worker_sync_fetch(
let blob_store = blob_store.clone();
deno_core::unsync::spawn(async move {
let script_url = Url::parse(&script)
.map_err(|_| type_error("Invalid script URL"))?;
.map_err(|_| SyncFetchError::InvalidScriptUrl)?;
let mut loose_mime_checks = loose_mime_checks;

let (body, mime_type, res_url) = match script_url.scheme() {
Expand All @@ -86,15 +111,13 @@ pub fn op_worker_sync_fetch(
);
*req.uri_mut() = script_url.as_str().parse()?;

let resp = client.send(req).await?;
let resp =
client.send(req).await.map_err(FetchError::ClientSend)?;

if resp.status().is_client_error()
|| resp.status().is_server_error()
{
return Err(type_error(format!(
"http status error: {}",
resp.status()
)));
return Err(SyncFetchError::InvalidStatusCode(resp.status()));
}

// TODO(andreubotella) Properly run fetch's "extract a MIME type".
Expand All @@ -107,30 +130,32 @@ pub fn op_worker_sync_fetch(
// Always check the MIME type with HTTP(S).
loose_mime_checks = false;

let body = resp.collect().await?.to_bytes();
let body = resp
.collect()
.await
.map_err(SyncFetchError::Other)?
.to_bytes();

(body, mime_type, script)
}
"data" => {
let data_url = DataUrl::process(&script)
.map_err(|e| type_error(format!("{e:?}")))?;
let data_url =
DataUrl::process(&script).map_err(FetchError::DataUrl)?;

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:?}")))?;
let (body, _) =
data_url.decode_to_vec().map_err(FetchError::Base64)?;

(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 blob = blob_store
.get_object_url(script_url)
.ok_or(FetchError::BlobNotFound)?;

let mime_type = mime_type_essence(&blob.media_type);

Expand All @@ -139,10 +164,11 @@ pub fn op_worker_sync_fetch(
(Bytes::from(body), Some(mime_type), script)
}
_ => {
return Err(type_error(format!(
"Classic scripts with scheme {}: are not supported in workers.",
script_url.scheme()
)))
return Err(
SyncFetchError::ClassicScriptSchemeUnsupportedInWorkers(
script_url.scheme().to_string(),
),
)
}
};

Expand All @@ -151,17 +177,11 @@ pub fn op_worker_sync_fetch(
match mime_type.as_deref() {
Some("application/javascript" | "text/javascript") => {}
Some(mime_type) => {
return Err(custom_error(
"DOMExceptionNetworkError",
format!("Invalid MIME type {mime_type:?}."),
))
}
None => {
return Err(custom_error(
"DOMExceptionNetworkError",
"Missing MIME type.",
return Err(SyncFetchError::InvalidMimeType(
mime_type.to_string(),
))
}
None => return Err(SyncFetchError::MissingMimeType),
}
}

Expand Down

0 comments on commit bfa2b9c

Please sign in to comment.