mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
refactor(runtime/ops): use concrete error types (#26409)
This commit is contained in:
parent
67280f8b55
commit
f26c8bcf31
17 changed files with 574 additions and 250 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2047,6 +2047,7 @@ dependencies = [
|
|||
"signal-hook-registry",
|
||||
"tempfile",
|
||||
"test_server",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-metrics",
|
||||
"twox-hash",
|
||||
|
|
|
@ -121,6 +121,7 @@ serde.workspace = true
|
|||
signal-hook = "0.3.17"
|
||||
signal-hook-registry = "1.4.0"
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-metrics.workspace = true
|
||||
twox-hash.workspace = true
|
||||
|
|
|
@ -9,6 +9,14 @@
|
|||
//! Diagnostics are compile-time type errors, whereas JsErrors are runtime
|
||||
//! exceptions.
|
||||
|
||||
use crate::ops::fs_events::FsEventsError;
|
||||
use crate::ops::http::HttpStartError;
|
||||
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;
|
||||
use deno_canvas::CanvasError;
|
||||
|
@ -49,6 +57,7 @@ use deno_web::WebError;
|
|||
use deno_websocket::HandshakeError;
|
||||
use deno_websocket::WebsocketError;
|
||||
use deno_webstorage::WebStorageError;
|
||||
use rustyline::error::ReadlineError;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
|
@ -806,6 +815,102 @@ fn get_net_map_error(error: &deno_net::io::MapError) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_create_worker_error(error: &CreateWorkerError) -> &'static str {
|
||||
match error {
|
||||
CreateWorkerError::ClassicWorkers => "DOMExceptionNotSupportedError",
|
||||
CreateWorkerError::Permission(e) => {
|
||||
get_error_class_name(e).unwrap_or("Error")
|
||||
}
|
||||
CreateWorkerError::ModuleResolution(e) => {
|
||||
get_module_resolution_error_class(e)
|
||||
}
|
||||
CreateWorkerError::Io(e) => get_io_error_class(e),
|
||||
CreateWorkerError::MessagePort(e) => get_web_message_port_error_class(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_tty_error(error: &TtyError) -> &'static str {
|
||||
match error {
|
||||
TtyError::Resource(e) | TtyError::Other(e) => {
|
||||
get_error_class_name(e).unwrap_or("Error")
|
||||
}
|
||||
TtyError::Io(e) => get_io_error_class(e),
|
||||
#[cfg(unix)]
|
||||
TtyError::Nix(e) => get_nix_error_class(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_readline_error(error: &ReadlineError) -> &'static str {
|
||||
match error {
|
||||
ReadlineError::Io(e) => get_io_error_class(e),
|
||||
ReadlineError::Eof => "Error",
|
||||
ReadlineError::Interrupted => "Error",
|
||||
#[cfg(unix)]
|
||||
ReadlineError::Errno(e) => get_nix_error_class(e),
|
||||
ReadlineError::WindowResized => "Error",
|
||||
#[cfg(windows)]
|
||||
ReadlineError::Decode(_) => "Error",
|
||||
#[cfg(windows)]
|
||||
ReadlineError::SystemError(_) => "Error",
|
||||
_ => "Error",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_signal_error(error: &SignalError) -> &'static str {
|
||||
match error {
|
||||
SignalError::InvalidSignalStr(_) => "TypeError",
|
||||
SignalError::InvalidSignalInt(_) => "TypeError",
|
||||
SignalError::SignalNotAllowed(_) => "TypeError",
|
||||
SignalError::Io(e) => get_io_error_class(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fs_events_error(error: &FsEventsError) -> &'static str {
|
||||
match error {
|
||||
FsEventsError::Resource(e) | FsEventsError::Permission(e) => {
|
||||
get_error_class_name(e).unwrap_or("Error")
|
||||
}
|
||||
FsEventsError::Notify(e) => get_notify_error_class(e),
|
||||
FsEventsError::Canceled(e) => {
|
||||
let io_err: io::Error = e.to_owned().into();
|
||||
get_io_error_class(&io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_http_start_error(error: &HttpStartError) -> &'static str {
|
||||
match error {
|
||||
HttpStartError::TcpStreamInUse => "Busy",
|
||||
HttpStartError::TlsStreamInUse => "Busy",
|
||||
HttpStartError::UnixSocketInUse => "Busy",
|
||||
HttpStartError::ReuniteTcp(_) => "Error",
|
||||
#[cfg(unix)]
|
||||
HttpStartError::ReuniteUnix(_) => "Error",
|
||||
HttpStartError::Io(e) => get_io_error_class(e),
|
||||
HttpStartError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_process_error(error: &ProcessError) -> &'static str {
|
||||
match error {
|
||||
ProcessError::SpawnFailed { error, .. } => get_process_error(error),
|
||||
ProcessError::FailedResolvingCwd(e) | ProcessError::Io(e) => {
|
||||
get_io_error_class(e)
|
||||
}
|
||||
ProcessError::Permission(e) | ProcessError::Resource(e) => {
|
||||
get_error_class_name(e).unwrap_or("Error")
|
||||
}
|
||||
ProcessError::BorrowMut(_) => "Error",
|
||||
ProcessError::Which(_) => "Error",
|
||||
ProcessError::ChildProcessAlreadyTerminated => "TypeError",
|
||||
ProcessError::Signal(e) => get_signal_error(e),
|
||||
ProcessError::MissingCmd => "Error",
|
||||
ProcessError::InvalidPid => "TypeError",
|
||||
#[cfg(unix)]
|
||||
ProcessError::Nix(e) => get_nix_error_class(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_http_error(error: &HttpError) -> &'static str {
|
||||
match error {
|
||||
HttpError::Canceled(e) => {
|
||||
|
@ -859,10 +964,50 @@ fn get_websocket_upgrade_error(error: &WebSocketUpgradeError) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_os_error(error: &OsError) -> &'static str {
|
||||
match error {
|
||||
OsError::Permission(e) => get_error_class_name(e).unwrap_or("Error"),
|
||||
OsError::InvalidUtf8(_) => "InvalidData",
|
||||
OsError::EnvEmptyKey => "TypeError",
|
||||
OsError::EnvInvalidKey(_) => "TypeError",
|
||||
OsError::EnvInvalidValue(_) => "TypeError",
|
||||
OsError::Io(e) => get_io_error_class(e),
|
||||
OsError::Var(e) => get_env_var_error_class(e),
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
.or_else(|| e.downcast_ref::<WebError>().map(get_web_error_class))
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<CreateWorkerError>()
|
||||
.map(get_create_worker_error)
|
||||
})
|
||||
.or_else(|| e.downcast_ref::<TtyError>().map(get_tty_error))
|
||||
.or_else(|| e.downcast_ref::<ReadlineError>().map(get_readline_error))
|
||||
.or_else(|| e.downcast_ref::<SignalError>().map(get_signal_error))
|
||||
.or_else(|| e.downcast_ref::<FsEventsError>().map(get_fs_events_error))
|
||||
.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)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::AsyncRefCell;
|
||||
use deno_core::CancelFuture;
|
||||
|
@ -37,7 +36,7 @@ deno_core::extension!(
|
|||
struct FsEventsResource {
|
||||
#[allow(unused)]
|
||||
watcher: RecommendedWatcher,
|
||||
receiver: AsyncRefCell<mpsc::Receiver<Result<FsEvent, AnyError>>>,
|
||||
receiver: AsyncRefCell<mpsc::Receiver<Result<FsEvent, NotifyError>>>,
|
||||
cancel: CancelHandle,
|
||||
}
|
||||
|
||||
|
@ -93,6 +92,18 @@ impl From<NotifyEvent> for FsEvent {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum FsEventsError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Permission(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Notify(#[from] NotifyError),
|
||||
#[error(transparent)]
|
||||
Canceled(#[from] deno_core::Canceled),
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct OpenArgs {
|
||||
recursive: bool,
|
||||
|
@ -104,12 +115,12 @@ pub struct OpenArgs {
|
|||
fn op_fs_events_open(
|
||||
state: &mut OpState,
|
||||
#[serde] args: OpenArgs,
|
||||
) -> Result<ResourceId, AnyError> {
|
||||
let (sender, receiver) = mpsc::channel::<Result<FsEvent, AnyError>>(16);
|
||||
) -> Result<ResourceId, FsEventsError> {
|
||||
let (sender, receiver) = mpsc::channel::<Result<FsEvent, NotifyError>>(16);
|
||||
let sender = Mutex::new(sender);
|
||||
let mut watcher: RecommendedWatcher = Watcher::new(
|
||||
move |res: Result<NotifyEvent, NotifyError>| {
|
||||
let res2 = res.map(FsEvent::from).map_err(AnyError::from);
|
||||
let res2 = res.map(FsEvent::from);
|
||||
let sender = sender.lock();
|
||||
// Ignore result, if send failed it means that watcher was already closed,
|
||||
// but not all messages have been flushed.
|
||||
|
@ -125,7 +136,8 @@ fn op_fs_events_open(
|
|||
for path in &args.paths {
|
||||
let path = state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_read(path, "Deno.watchFs()")?;
|
||||
.check_read(path, "Deno.watchFs()")
|
||||
.map_err(FsEventsError::Permission)?;
|
||||
watcher.watch(&path, recursive_mode)?;
|
||||
}
|
||||
let resource = FsEventsResource {
|
||||
|
@ -142,14 +154,18 @@ fn op_fs_events_open(
|
|||
async fn op_fs_events_poll(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<Option<FsEvent>, AnyError> {
|
||||
let resource = state.borrow().resource_table.get::<FsEventsResource>(rid)?;
|
||||
) -> Result<Option<FsEvent>, FsEventsError> {
|
||||
let resource = state
|
||||
.borrow()
|
||||
.resource_table
|
||||
.get::<FsEventsResource>(rid)
|
||||
.map_err(FsEventsError::Resource)?;
|
||||
let mut receiver = RcRef::map(&resource, |r| &r.receiver).borrow_mut().await;
|
||||
let cancel = RcRef::map(resource, |r| &r.cancel);
|
||||
let maybe_result = receiver.recv().or_cancel(cancel).await?;
|
||||
match maybe_result {
|
||||
Some(Ok(value)) => Ok(Some(value)),
|
||||
Some(Err(err)) => Err(err),
|
||||
Some(Err(err)) => Err(FsEventsError::Notify(err)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
use std::rc::Rc;
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::ResourceId;
|
||||
|
@ -16,12 +13,31 @@ pub const UNSTABLE_FEATURE_NAME: &str = "http";
|
|||
|
||||
deno_core::extension!(deno_http_runtime, ops = [op_http_start],);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum HttpStartError {
|
||||
#[error("TCP stream is currently in use")]
|
||||
TcpStreamInUse,
|
||||
#[error("TLS stream is currently in use")]
|
||||
TlsStreamInUse,
|
||||
#[error("Unix socket is currently in use")]
|
||||
UnixSocketInUse,
|
||||
#[error(transparent)]
|
||||
ReuniteTcp(#[from] tokio::net::tcp::ReuniteError),
|
||||
#[cfg(unix)]
|
||||
#[error(transparent)]
|
||||
ReuniteUnix(#[from] tokio::net::unix::ReuniteError),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
Other(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
#[smi]
|
||||
fn op_http_start(
|
||||
state: &mut OpState,
|
||||
#[smi] tcp_stream_rid: ResourceId,
|
||||
) -> Result<ResourceId, AnyError> {
|
||||
) -> Result<ResourceId, HttpStartError> {
|
||||
if let Ok(resource_rc) = state
|
||||
.resource_table
|
||||
.take::<TcpStreamResource>(tcp_stream_rid)
|
||||
|
@ -30,7 +46,7 @@ fn op_http_start(
|
|||
// process of starting a HTTP server on top of this TCP connection, so we just return a Busy error.
|
||||
// See also: https://github.com/denoland/deno/pull/16242
|
||||
let resource = Rc::try_unwrap(resource_rc)
|
||||
.map_err(|_| custom_error("Busy", "TCP stream is currently in use"))?;
|
||||
.map_err(|_| HttpStartError::TcpStreamInUse)?;
|
||||
let (read_half, write_half) = resource.into_inner();
|
||||
let tcp_stream = read_half.reunite(write_half)?;
|
||||
let addr = tcp_stream.local_addr()?;
|
||||
|
@ -45,7 +61,7 @@ fn op_http_start(
|
|||
// process of starting a HTTP server on top of this TLS connection, so we just return a Busy error.
|
||||
// See also: https://github.com/denoland/deno/pull/16242
|
||||
let resource = Rc::try_unwrap(resource_rc)
|
||||
.map_err(|_| custom_error("Busy", "TLS stream is currently in use"))?;
|
||||
.map_err(|_| HttpStartError::TlsStreamInUse)?;
|
||||
let (read_half, write_half) = resource.into_inner();
|
||||
let tls_stream = read_half.unsplit(write_half);
|
||||
let addr = tls_stream.local_addr()?;
|
||||
|
@ -61,7 +77,7 @@ fn op_http_start(
|
|||
// process of starting a HTTP server on top of this UNIX socket, so we just return a Busy error.
|
||||
// See also: https://github.com/denoland/deno/pull/16242
|
||||
let resource = Rc::try_unwrap(resource_rc)
|
||||
.map_err(|_| custom_error("Busy", "Unix socket is currently in use"))?;
|
||||
.map_err(|_| HttpStartError::UnixSocketInUse)?;
|
||||
let (read_half, write_half) = resource.into_inner();
|
||||
let unix_stream = read_half.reunite(write_half)?;
|
||||
let addr = unix_stream.local_addr()?;
|
||||
|
@ -73,5 +89,5 @@ fn op_http_start(
|
|||
));
|
||||
}
|
||||
|
||||
Err(bad_resource_id())
|
||||
Err(HttpStartError::Other(deno_core::error::bad_resource_id()))
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ pub mod process;
|
|||
pub mod runtime;
|
||||
pub mod signal;
|
||||
pub mod tty;
|
||||
mod utils;
|
||||
pub mod web_worker;
|
||||
pub mod worker_host;
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use super::utils::into_string;
|
||||
use crate::worker::ExitCode;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::OpState;
|
||||
|
@ -73,17 +70,39 @@ deno_core::extension!(
|
|||
},
|
||||
);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum OsError {
|
||||
#[error(transparent)]
|
||||
Permission(deno_core::error::AnyError),
|
||||
#[error("File name or path {0:?} is not valid UTF-8")]
|
||||
InvalidUtf8(std::ffi::OsString),
|
||||
#[error("Key is an empty string.")]
|
||||
EnvEmptyKey,
|
||||
#[error("Key contains invalid characters: {0:?}")]
|
||||
EnvInvalidKey(String),
|
||||
#[error("Value contains invalid characters: {0:?}")]
|
||||
EnvInvalidValue(String),
|
||||
#[error(transparent)]
|
||||
Var(#[from] env::VarError),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[string]
|
||||
fn op_exec_path(state: &mut OpState) -> Result<String, AnyError> {
|
||||
fn op_exec_path(state: &mut OpState) -> Result<String, OsError> {
|
||||
let current_exe = env::current_exe().unwrap();
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_read_blind(¤t_exe, "exec_path", "Deno.execPath()")?;
|
||||
.check_read_blind(¤t_exe, "exec_path", "Deno.execPath()")
|
||||
.map_err(OsError::Permission)?;
|
||||
// normalize path so it doesn't include '.' or '..' components
|
||||
let path = normalize_path(current_exe);
|
||||
|
||||
into_string(path.into_os_string())
|
||||
path
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.map_err(OsError::InvalidUtf8)
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
|
@ -91,20 +110,19 @@ fn op_set_env(
|
|||
state: &mut OpState,
|
||||
#[string] key: &str,
|
||||
#[string] value: &str,
|
||||
) -> Result<(), AnyError> {
|
||||
state.borrow_mut::<PermissionsContainer>().check_env(key)?;
|
||||
) -> Result<(), OsError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_env(key)
|
||||
.map_err(OsError::Permission)?;
|
||||
if key.is_empty() {
|
||||
return Err(type_error("Key is an empty string."));
|
||||
return Err(OsError::EnvEmptyKey);
|
||||
}
|
||||
if key.contains(&['=', '\0'] as &[char]) {
|
||||
return Err(type_error(format!(
|
||||
"Key contains invalid characters: {key:?}"
|
||||
)));
|
||||
return Err(OsError::EnvInvalidKey(key.to_string()));
|
||||
}
|
||||
if value.contains('\0') {
|
||||
return Err(type_error(format!(
|
||||
"Value contains invalid characters: {value:?}"
|
||||
)));
|
||||
return Err(OsError::EnvInvalidValue(value.to_string()));
|
||||
}
|
||||
env::set_var(key, value);
|
||||
Ok(())
|
||||
|
@ -112,7 +130,9 @@ fn op_set_env(
|
|||
|
||||
#[op2]
|
||||
#[serde]
|
||||
fn op_env(state: &mut OpState) -> Result<HashMap<String, String>, AnyError> {
|
||||
fn op_env(
|
||||
state: &mut OpState,
|
||||
) -> Result<HashMap<String, String>, deno_core::error::AnyError> {
|
||||
state.borrow_mut::<PermissionsContainer>().check_env_all()?;
|
||||
Ok(env::vars().collect())
|
||||
}
|
||||
|
@ -122,21 +142,22 @@ fn op_env(state: &mut OpState) -> Result<HashMap<String, String>, AnyError> {
|
|||
fn op_get_env(
|
||||
state: &mut OpState,
|
||||
#[string] key: String,
|
||||
) -> Result<Option<String>, AnyError> {
|
||||
) -> Result<Option<String>, OsError> {
|
||||
let skip_permission_check = NODE_ENV_VAR_ALLOWLIST.contains(&key);
|
||||
|
||||
if !skip_permission_check {
|
||||
state.borrow_mut::<PermissionsContainer>().check_env(&key)?;
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_env(&key)
|
||||
.map_err(OsError::Permission)?;
|
||||
}
|
||||
|
||||
if key.is_empty() {
|
||||
return Err(type_error("Key is an empty string."));
|
||||
return Err(OsError::EnvEmptyKey);
|
||||
}
|
||||
|
||||
if key.contains(&['=', '\0'] as &[char]) {
|
||||
return Err(type_error(format!(
|
||||
"Key contains invalid characters: {key:?}"
|
||||
)));
|
||||
return Err(OsError::EnvInvalidKey(key.to_string()));
|
||||
}
|
||||
|
||||
let r = match env::var(key) {
|
||||
|
@ -150,10 +171,13 @@ fn op_get_env(
|
|||
fn op_delete_env(
|
||||
state: &mut OpState,
|
||||
#[string] key: String,
|
||||
) -> Result<(), AnyError> {
|
||||
state.borrow_mut::<PermissionsContainer>().check_env(&key)?;
|
||||
) -> Result<(), OsError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_env(&key)
|
||||
.map_err(OsError::Permission)?;
|
||||
if key.is_empty() || key.contains(&['=', '\0'] as &[char]) {
|
||||
return Err(type_error("Key contains invalid characters."));
|
||||
return Err(OsError::EnvInvalidKey(key.to_string()));
|
||||
}
|
||||
env::remove_var(key);
|
||||
Ok(())
|
||||
|
@ -178,7 +202,9 @@ fn op_exit(state: &mut OpState) {
|
|||
|
||||
#[op2]
|
||||
#[serde]
|
||||
fn op_loadavg(state: &mut OpState) -> Result<(f64, f64, f64), AnyError> {
|
||||
fn op_loadavg(
|
||||
state: &mut OpState,
|
||||
) -> Result<(f64, f64, f64), deno_core::error::AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("loadavg", "Deno.loadavg()")?;
|
||||
|
@ -187,7 +213,9 @@ fn op_loadavg(state: &mut OpState) -> Result<(f64, f64, f64), AnyError> {
|
|||
|
||||
#[op2]
|
||||
#[string]
|
||||
fn op_hostname(state: &mut OpState) -> Result<String, AnyError> {
|
||||
fn op_hostname(
|
||||
state: &mut OpState,
|
||||
) -> Result<String, deno_core::error::AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("hostname", "Deno.hostname()")?;
|
||||
|
@ -196,7 +224,9 @@ fn op_hostname(state: &mut OpState) -> Result<String, AnyError> {
|
|||
|
||||
#[op2]
|
||||
#[string]
|
||||
fn op_os_release(state: &mut OpState) -> Result<String, AnyError> {
|
||||
fn op_os_release(
|
||||
state: &mut OpState,
|
||||
) -> Result<String, deno_core::error::AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("osRelease", "Deno.osRelease()")?;
|
||||
|
@ -207,10 +237,11 @@ fn op_os_release(state: &mut OpState) -> Result<String, AnyError> {
|
|||
#[serde]
|
||||
fn op_network_interfaces(
|
||||
state: &mut OpState,
|
||||
) -> Result<Vec<NetworkInterface>, AnyError> {
|
||||
) -> Result<Vec<NetworkInterface>, OsError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("networkInterfaces", "Deno.networkInterfaces()")?;
|
||||
.check_sys("networkInterfaces", "Deno.networkInterfaces()")
|
||||
.map_err(OsError::Permission)?;
|
||||
Ok(netif::up()?.map(NetworkInterface::from).collect())
|
||||
}
|
||||
|
||||
|
@ -259,7 +290,7 @@ impl From<netif::Interface> for NetworkInterface {
|
|||
#[serde]
|
||||
fn op_system_memory_info(
|
||||
state: &mut OpState,
|
||||
) -> Result<Option<sys_info::MemInfo>, AnyError> {
|
||||
) -> Result<Option<sys_info::MemInfo>, deno_core::error::AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("systemMemoryInfo", "Deno.systemMemoryInfo()")?;
|
||||
|
@ -269,7 +300,9 @@ fn op_system_memory_info(
|
|||
#[cfg(not(windows))]
|
||||
#[op2]
|
||||
#[smi]
|
||||
fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
|
||||
fn op_gid(
|
||||
state: &mut OpState,
|
||||
) -> Result<Option<u32>, deno_core::error::AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("gid", "Deno.gid()")?;
|
||||
|
@ -283,7 +316,9 @@ fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
|
|||
#[cfg(windows)]
|
||||
#[op2]
|
||||
#[smi]
|
||||
fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
|
||||
fn op_gid(
|
||||
state: &mut OpState,
|
||||
) -> Result<Option<u32>, deno_core::error::AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("gid", "Deno.gid()")?;
|
||||
|
@ -293,7 +328,9 @@ fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
|
|||
#[cfg(not(windows))]
|
||||
#[op2]
|
||||
#[smi]
|
||||
fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
|
||||
fn op_uid(
|
||||
state: &mut OpState,
|
||||
) -> Result<Option<u32>, deno_core::error::AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("uid", "Deno.uid()")?;
|
||||
|
@ -307,7 +344,9 @@ fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
|
|||
#[cfg(windows)]
|
||||
#[op2]
|
||||
#[smi]
|
||||
fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
|
||||
fn op_uid(
|
||||
state: &mut OpState,
|
||||
) -> Result<Option<u32>, deno_core::error::AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("uid", "Deno.uid()")?;
|
||||
|
@ -485,7 +524,7 @@ fn rss() -> usize {
|
|||
}
|
||||
}
|
||||
|
||||
fn os_uptime(state: &mut OpState) -> Result<u64, AnyError> {
|
||||
fn os_uptime(state: &mut OpState) -> Result<u64, deno_core::error::AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_sys("osUptime", "Deno.osUptime()")?;
|
||||
|
@ -494,6 +533,8 @@ fn os_uptime(state: &mut OpState) -> Result<u64, AnyError> {
|
|||
|
||||
#[op2(fast)]
|
||||
#[number]
|
||||
fn op_os_uptime(state: &mut OpState) -> Result<u64, AnyError> {
|
||||
fn op_os_uptime(
|
||||
state: &mut OpState,
|
||||
) -> Result<u64, deno_core::error::AnyError> {
|
||||
os_uptime(state)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::AsyncMutFuture;
|
||||
|
@ -35,6 +32,7 @@ use tokio::process::Command;
|
|||
#[cfg(windows)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
use crate::ops::signal::SignalError;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::ExitStatusExt;
|
||||
#[cfg(unix)]
|
||||
|
@ -105,11 +103,12 @@ impl StdioOrRid {
|
|||
pub fn as_stdio(
|
||||
&self,
|
||||
state: &mut OpState,
|
||||
) -> Result<std::process::Stdio, AnyError> {
|
||||
) -> Result<std::process::Stdio, ProcessError> {
|
||||
match &self {
|
||||
StdioOrRid::Stdio(val) => Ok(val.as_stdio()),
|
||||
StdioOrRid::Rid(rid) => {
|
||||
FileResource::with_file(state, *rid, |file| Ok(file.as_stdio()?))
|
||||
.map_err(ProcessError::Resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,6 +190,39 @@ pub struct SpawnArgs {
|
|||
needs_npm_process_state: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ProcessError {
|
||||
#[error("Failed to spawn '{command}': {error}")]
|
||||
SpawnFailed {
|
||||
command: String,
|
||||
#[source]
|
||||
error: Box<ProcessError>,
|
||||
},
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[cfg(unix)]
|
||||
#[error(transparent)]
|
||||
Nix(nix::Error),
|
||||
#[error("failed resolving cwd: {0}")]
|
||||
FailedResolvingCwd(#[source] std::io::Error),
|
||||
#[error(transparent)]
|
||||
Permission(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
BorrowMut(std::cell::BorrowMutError),
|
||||
#[error(transparent)]
|
||||
Which(which::Error),
|
||||
#[error("Child process has already terminated.")]
|
||||
ChildProcessAlreadyTerminated,
|
||||
#[error("Invalid pid")]
|
||||
InvalidPid,
|
||||
#[error(transparent)]
|
||||
Signal(#[from] SignalError),
|
||||
#[error("Missing cmd")]
|
||||
MissingCmd, // only for Deno.run
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChildStdio {
|
||||
|
@ -208,7 +240,7 @@ pub struct ChildStatus {
|
|||
}
|
||||
|
||||
impl TryFrom<ExitStatus> for ChildStatus {
|
||||
type Error = AnyError;
|
||||
type Error = SignalError;
|
||||
|
||||
fn try_from(status: ExitStatus) -> Result<Self, Self::Error> {
|
||||
let code = status.code();
|
||||
|
@ -259,7 +291,7 @@ type CreateCommand = (
|
|||
|
||||
pub fn npm_process_state_tempfile(
|
||||
contents: &[u8],
|
||||
) -> Result<deno_io::RawIoHandle, AnyError> {
|
||||
) -> Result<deno_io::RawIoHandle, std::io::Error> {
|
||||
let mut temp_file = tempfile::tempfile()?;
|
||||
temp_file.write_all(contents)?;
|
||||
let handle = temp_file.into_raw_io_handle();
|
||||
|
@ -301,7 +333,7 @@ fn create_command(
|
|||
state: &mut OpState,
|
||||
mut args: SpawnArgs,
|
||||
api_name: &str,
|
||||
) -> Result<CreateCommand, AnyError> {
|
||||
) -> Result<CreateCommand, ProcessError> {
|
||||
let maybe_npm_process_state = if args.needs_npm_process_state {
|
||||
let provider = state.borrow::<NpmProcessStateProviderRc>();
|
||||
let process_state = provider.get_npm_process_state();
|
||||
|
@ -505,7 +537,7 @@ fn spawn_child(
|
|||
ipc_pipe_rid: Option<ResourceId>,
|
||||
extra_pipe_rids: Vec<Option<ResourceId>>,
|
||||
detached: bool,
|
||||
) -> Result<Child, AnyError> {
|
||||
) -> Result<Child, ProcessError> {
|
||||
let mut command = tokio::process::Command::from(command);
|
||||
// TODO(@crowlkats): allow detaching processes.
|
||||
// currently deno will orphan a process when exiting with an error or Deno.exit()
|
||||
|
@ -554,10 +586,10 @@ fn spawn_child(
|
|||
}
|
||||
}
|
||||
|
||||
return Err(AnyError::from(err).context(format!(
|
||||
"Failed to spawn '{}'",
|
||||
command.get_program().to_string_lossy()
|
||||
)));
|
||||
return Err(ProcessError::SpawnFailed {
|
||||
command: command.get_program().to_string_lossy().to_string(),
|
||||
error: Box::new(err.into()),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -600,11 +632,19 @@ fn compute_run_cmd_and_check_permissions(
|
|||
arg_clear_env: bool,
|
||||
state: &mut OpState,
|
||||
api_name: &str,
|
||||
) -> Result<(PathBuf, RunEnv), AnyError> {
|
||||
let run_env = compute_run_env(arg_cwd, arg_envs, arg_clear_env)
|
||||
.with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
|
||||
let cmd = resolve_cmd(arg_cmd, &run_env)
|
||||
.with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
|
||||
) -> Result<(PathBuf, RunEnv), ProcessError> {
|
||||
let run_env =
|
||||
compute_run_env(arg_cwd, arg_envs, arg_clear_env).map_err(|e| {
|
||||
ProcessError::SpawnFailed {
|
||||
command: arg_cmd.to_string(),
|
||||
error: Box::new(e),
|
||||
}
|
||||
})?;
|
||||
let cmd =
|
||||
resolve_cmd(arg_cmd, &run_env).map_err(|e| ProcessError::SpawnFailed {
|
||||
command: arg_cmd.to_string(),
|
||||
error: Box::new(e),
|
||||
})?;
|
||||
check_run_permission(
|
||||
state,
|
||||
&RunQueryDescriptor::Path {
|
||||
|
@ -613,7 +653,8 @@ fn compute_run_cmd_and_check_permissions(
|
|||
},
|
||||
&run_env,
|
||||
api_name,
|
||||
)?;
|
||||
)
|
||||
.map_err(ProcessError::Permission)?;
|
||||
Ok((cmd, run_env))
|
||||
}
|
||||
|
||||
|
@ -631,9 +672,10 @@ fn compute_run_env(
|
|||
arg_cwd: Option<&str>,
|
||||
arg_envs: &[(String, String)],
|
||||
arg_clear_env: bool,
|
||||
) -> Result<RunEnv, AnyError> {
|
||||
) -> Result<RunEnv, ProcessError> {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let cwd = std::env::current_dir().context("failed resolving cwd")?;
|
||||
let cwd =
|
||||
std::env::current_dir().map_err(ProcessError::FailedResolvingCwd)?;
|
||||
let cwd = arg_cwd
|
||||
.map(|cwd_arg| resolve_path(cwd_arg, &cwd))
|
||||
.unwrap_or(cwd);
|
||||
|
@ -670,7 +712,7 @@ fn compute_run_env(
|
|||
Ok(RunEnv { envs, cwd })
|
||||
}
|
||||
|
||||
fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, AnyError> {
|
||||
fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, ProcessError> {
|
||||
let is_path = cmd.contains('/');
|
||||
#[cfg(windows)]
|
||||
let is_path = is_path || cmd.contains('\\') || Path::new(&cmd).is_absolute();
|
||||
|
@ -683,7 +725,7 @@ fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, AnyError> {
|
|||
Err(which::Error::CannotFindBinaryPath) => {
|
||||
Err(std::io::Error::from(std::io::ErrorKind::NotFound).into())
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
Err(err) => Err(ProcessError::Which(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -697,7 +739,7 @@ fn check_run_permission(
|
|||
cmd: &RunQueryDescriptor,
|
||||
run_env: &RunEnv,
|
||||
api_name: &str,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
let permissions = state.borrow_mut::<PermissionsContainer>();
|
||||
if !permissions.query_run_all(api_name) {
|
||||
// error the same on all platforms
|
||||
|
@ -754,7 +796,7 @@ fn op_spawn_child(
|
|||
state: &mut OpState,
|
||||
#[serde] args: SpawnArgs,
|
||||
#[string] api_name: String,
|
||||
) -> Result<Child, AnyError> {
|
||||
) -> Result<Child, ProcessError> {
|
||||
let detached = args.detached;
|
||||
let (command, pipe_rid, extra_pipe_rids, handles_to_close) =
|
||||
create_command(state, args, &api_name)?;
|
||||
|
@ -771,16 +813,23 @@ fn op_spawn_child(
|
|||
async fn op_spawn_wait(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<ChildStatus, AnyError> {
|
||||
) -> Result<ChildStatus, ProcessError> {
|
||||
let resource = state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.get::<ChildResource>(rid)?;
|
||||
let result = resource.0.try_borrow_mut()?.wait().await?.try_into();
|
||||
.get::<ChildResource>(rid)
|
||||
.map_err(ProcessError::Resource)?;
|
||||
let result = resource
|
||||
.0
|
||||
.try_borrow_mut()
|
||||
.map_err(ProcessError::BorrowMut)?
|
||||
.wait()
|
||||
.await?
|
||||
.try_into()?;
|
||||
if let Ok(resource) = state.borrow_mut().resource_table.take_any(rid) {
|
||||
resource.close();
|
||||
}
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[op2]
|
||||
|
@ -788,16 +837,14 @@ async fn op_spawn_wait(
|
|||
fn op_spawn_sync(
|
||||
state: &mut OpState,
|
||||
#[serde] args: SpawnArgs,
|
||||
) -> Result<SpawnOutput, AnyError> {
|
||||
) -> Result<SpawnOutput, ProcessError> {
|
||||
let stdout = matches!(args.stdio.stdout, StdioOrRid::Stdio(Stdio::Piped));
|
||||
let stderr = matches!(args.stdio.stderr, StdioOrRid::Stdio(Stdio::Piped));
|
||||
let (mut command, _, _, _) =
|
||||
create_command(state, args, "Deno.Command().outputSync()")?;
|
||||
let output = command.output().with_context(|| {
|
||||
format!(
|
||||
"Failed to spawn '{}'",
|
||||
command.get_program().to_string_lossy()
|
||||
)
|
||||
let output = command.output().map_err(|e| ProcessError::SpawnFailed {
|
||||
command: command.get_program().to_string_lossy().to_string(),
|
||||
error: Box::new(e.into()),
|
||||
})?;
|
||||
|
||||
Ok(SpawnOutput {
|
||||
|
@ -820,17 +867,15 @@ fn op_spawn_kill(
|
|||
state: &mut OpState,
|
||||
#[smi] rid: ResourceId,
|
||||
#[string] signal: String,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), ProcessError> {
|
||||
if let Ok(child_resource) = state.resource_table.get::<ChildResource>(rid) {
|
||||
deprecated::kill(child_resource.1 as i32, &signal)?;
|
||||
return Ok(());
|
||||
}
|
||||
Err(type_error("Child process has already terminated."))
|
||||
Err(ProcessError::ChildProcessAlreadyTerminated)
|
||||
}
|
||||
|
||||
mod deprecated {
|
||||
use deno_core::anyhow;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -876,9 +921,9 @@ mod deprecated {
|
|||
pub fn op_run(
|
||||
state: &mut OpState,
|
||||
#[serde] run_args: RunArgs,
|
||||
) -> Result<RunInfo, AnyError> {
|
||||
) -> Result<RunInfo, ProcessError> {
|
||||
let args = run_args.cmd;
|
||||
let cmd = args.first().ok_or_else(|| anyhow::anyhow!("Missing cmd"))?;
|
||||
let cmd = args.first().ok_or(ProcessError::MissingCmd)?;
|
||||
let (cmd, run_env) = compute_run_cmd_and_check_permissions(
|
||||
cmd,
|
||||
run_args.cwd.as_deref(),
|
||||
|
@ -990,11 +1035,12 @@ mod deprecated {
|
|||
pub async fn op_run_status(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<ProcessStatus, AnyError> {
|
||||
) -> Result<ProcessStatus, ProcessError> {
|
||||
let resource = state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.get::<ChildResource>(rid)?;
|
||||
.get::<ChildResource>(rid)
|
||||
.map_err(ProcessError::Resource)?;
|
||||
let mut child = resource.borrow_mut().await;
|
||||
let run_status = child.wait().await?;
|
||||
let code = run_status.code();
|
||||
|
@ -1017,17 +1063,17 @@ mod deprecated {
|
|||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
|
||||
pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> {
|
||||
let signo = super::super::signal::signal_str_to_int(signal)?;
|
||||
use nix::sys::signal::kill as unix_kill;
|
||||
use nix::sys::signal::Signal;
|
||||
use nix::unistd::Pid;
|
||||
let sig = Signal::try_from(signo)?;
|
||||
unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(AnyError::from)
|
||||
let sig = Signal::try_from(signo).map_err(ProcessError::Nix)?;
|
||||
unix_kill(Pid::from_raw(pid), Some(sig)).map_err(ProcessError::Nix)
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
|
||||
pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> {
|
||||
use std::io::Error;
|
||||
use std::io::ErrorKind::NotFound;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
|
@ -1041,9 +1087,9 @@ mod deprecated {
|
|||
use winapi::um::winnt::PROCESS_TERMINATE;
|
||||
|
||||
if !matches!(signal, "SIGKILL" | "SIGTERM") {
|
||||
Err(type_error(format!("Invalid signal: {signal}")))
|
||||
Err(SignalError::InvalidSignalStr(signal.to_string()).into())
|
||||
} else if pid <= 0 {
|
||||
Err(type_error("Invalid pid"))
|
||||
Err(ProcessError::InvalidPid)
|
||||
} else {
|
||||
let handle =
|
||||
// SAFETY: winapi call
|
||||
|
@ -1077,11 +1123,11 @@ mod deprecated {
|
|||
#[smi] pid: i32,
|
||||
#[string] signal: String,
|
||||
#[string] api_name: String,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), ProcessError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_run_all(&api_name)?;
|
||||
kill(pid, &signal)?;
|
||||
Ok(())
|
||||
.check_run_all(&api_name)
|
||||
.map_err(ProcessError::Permission)?;
|
||||
kill(pid, &signal)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_core::OpState;
|
||||
|
@ -16,10 +15,9 @@ deno_core::extension!(
|
|||
|
||||
#[op2]
|
||||
#[string]
|
||||
fn op_main_module(state: &mut OpState) -> Result<String, AnyError> {
|
||||
fn op_main_module(state: &mut OpState) -> String {
|
||||
let main_url = state.borrow::<ModuleSpecifier>();
|
||||
let main_path = main_url.to_string();
|
||||
Ok(main_path)
|
||||
main_url.to_string()
|
||||
}
|
||||
|
||||
/// This is an op instead of being done at initialization time because
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::AsyncRefCell;
|
||||
use deno_core::CancelFuture;
|
||||
|
@ -46,6 +44,42 @@ deno_core::extension!(
|
|||
}
|
||||
);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SignalError {
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
target_os = "openbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "macos",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos"
|
||||
))]
|
||||
#[error("Invalid signal: {0}")]
|
||||
InvalidSignalStr(String),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
target_os = "openbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "macos",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos"
|
||||
))]
|
||||
#[error("Invalid signal: {0}")]
|
||||
InvalidSignalInt(libc::c_int),
|
||||
#[cfg(target_os = "windows")]
|
||||
#[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
|
||||
InvalidSignalStr(String),
|
||||
#[cfg(target_os = "windows")]
|
||||
#[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
|
||||
InvalidSignalInt(libc::c_int),
|
||||
#[error("Binding to signal '{0}' is not allowed")]
|
||||
SignalNotAllowed(String),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[derive(Default)]
|
||||
struct SignalState {
|
||||
|
@ -153,18 +187,18 @@ macro_rules! first_literal {
|
|||
};
|
||||
}
|
||||
macro_rules! signal_dict {
|
||||
($error_msg:expr, $(($number:literal, $($name:literal)|+)),*) => {
|
||||
pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
|
||||
($(($number:literal, $($name:literal)|+)),*) => {
|
||||
pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, SignalError> {
|
||||
match s {
|
||||
$($($name)|* => Ok($number),)*
|
||||
_ => Err(type_error($error_msg(s))),
|
||||
_ => Err(SignalError::InvalidSignalStr(s.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
|
||||
pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, SignalError> {
|
||||
match s {
|
||||
$($number => Ok(first_literal!($($name),+)),)*
|
||||
_ => Err(type_error($error_msg(s))),
|
||||
_ => Err(SignalError::InvalidSignalInt(s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +206,6 @@ macro_rules! signal_dict {
|
|||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
signal_dict!(
|
||||
|s| { format!("Invalid signal : {}", s) },
|
||||
(1, "SIGHUP"),
|
||||
(2, "SIGINT"),
|
||||
(3, "SIGQUIT"),
|
||||
|
@ -210,7 +243,6 @@ signal_dict!(
|
|||
|
||||
#[cfg(target_os = "openbsd")]
|
||||
signal_dict!(
|
||||
|s| { format!("Invalid signal : {}", s) },
|
||||
(1, "SIGHUP"),
|
||||
(2, "SIGINT"),
|
||||
(3, "SIGQUIT"),
|
||||
|
@ -246,7 +278,6 @@ signal_dict!(
|
|||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
signal_dict!(
|
||||
|s| { format!("Invalid signal : {s}") },
|
||||
(1, "SIGHUP"),
|
||||
(2, "SIGINT"),
|
||||
(3, "SIGQUIT"),
|
||||
|
@ -282,7 +313,6 @@ signal_dict!(
|
|||
|
||||
#[cfg(target_os = "macos")]
|
||||
signal_dict!(
|
||||
|s| { format!("Invalid signal : {s}") },
|
||||
(1, "SIGHUP"),
|
||||
(2, "SIGINT"),
|
||||
(3, "SIGQUIT"),
|
||||
|
@ -318,7 +348,6 @@ signal_dict!(
|
|||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
signal_dict!(
|
||||
|s| { format!("Invalid signal : {s}") },
|
||||
(1, "SIGHUP"),
|
||||
(2, "SIGINT"),
|
||||
(3, "SIGQUIT"),
|
||||
|
@ -362,11 +391,7 @@ signal_dict!(
|
|||
);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
signal_dict!(
|
||||
|_| { "Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK)." },
|
||||
(2, "SIGINT"),
|
||||
(21, "SIGBREAK")
|
||||
);
|
||||
signal_dict!((2, "SIGINT"), (21, "SIGBREAK"));
|
||||
|
||||
#[cfg(unix)]
|
||||
#[op2(fast)]
|
||||
|
@ -374,12 +399,10 @@ signal_dict!(
|
|||
fn op_signal_bind(
|
||||
state: &mut OpState,
|
||||
#[string] sig: &str,
|
||||
) -> Result<ResourceId, AnyError> {
|
||||
) -> Result<ResourceId, SignalError> {
|
||||
let signo = signal_str_to_int(sig)?;
|
||||
if signal_hook_registry::FORBIDDEN.contains(&signo) {
|
||||
return Err(type_error(format!(
|
||||
"Binding to signal '{sig}' is not allowed",
|
||||
)));
|
||||
return Err(SignalError::SignalNotAllowed(sig.to_string()));
|
||||
}
|
||||
|
||||
let signal = AsyncRefCell::new(signal(SignalKind::from_raw(signo))?);
|
||||
|
@ -413,7 +436,7 @@ fn op_signal_bind(
|
|||
fn op_signal_bind(
|
||||
state: &mut OpState,
|
||||
#[string] sig: &str,
|
||||
) -> Result<ResourceId, AnyError> {
|
||||
) -> Result<ResourceId, SignalError> {
|
||||
let signo = signal_str_to_int(sig)?;
|
||||
let resource = SignalStreamResource {
|
||||
signal: AsyncRefCell::new(match signo {
|
||||
|
@ -437,7 +460,7 @@ fn op_signal_bind(
|
|||
async fn op_signal_poll(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<bool, AnyError> {
|
||||
) -> Result<bool, deno_core::error::AnyError> {
|
||||
let resource = state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
|
@ -456,7 +479,7 @@ async fn op_signal_poll(
|
|||
pub fn op_signal_unbind(
|
||||
state: &mut OpState,
|
||||
#[smi] rid: ResourceId,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
let resource = state.resource_table.take::<SignalStreamResource>(rid)?;
|
||||
|
||||
#[cfg(unix)]
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use std::io::Error;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use rustyline::config::Configurer;
|
||||
|
@ -64,6 +63,19 @@ deno_core::extension!(
|
|||
},
|
||||
);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TtyError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[cfg(unix)]
|
||||
#[error(transparent)]
|
||||
Nix(nix::Error),
|
||||
#[error(transparent)]
|
||||
Other(deno_core::error::AnyError),
|
||||
}
|
||||
|
||||
// ref: <https://learn.microsoft.com/en-us/windows/console/setconsolemode>
|
||||
#[cfg(windows)]
|
||||
const COOKED_MODE: DWORD =
|
||||
|
@ -90,8 +102,11 @@ fn op_set_raw(
|
|||
rid: u32,
|
||||
is_raw: bool,
|
||||
cbreak: bool,
|
||||
) -> Result<(), AnyError> {
|
||||
let handle_or_fd = state.resource_table.get_fd(rid)?;
|
||||
) -> Result<(), TtyError> {
|
||||
let handle_or_fd = state
|
||||
.resource_table
|
||||
.get_fd(rid)
|
||||
.map_err(TtyError::Resource)?;
|
||||
|
||||
// From https://github.com/kkawakam/rustyline/blob/master/src/tty/windows.rs
|
||||
// and https://github.com/kkawakam/rustyline/blob/master/src/tty/unix.rs
|
||||
|
@ -107,7 +122,7 @@ fn op_set_raw(
|
|||
let handle = handle_or_fd;
|
||||
|
||||
if cbreak {
|
||||
return Err(deno_core::error::not_supported());
|
||||
return Err(TtyError::Other(deno_core::error::not_supported()));
|
||||
}
|
||||
|
||||
let mut original_mode: DWORD = 0;
|
||||
|
@ -115,7 +130,7 @@ fn op_set_raw(
|
|||
if unsafe { consoleapi::GetConsoleMode(handle, &mut original_mode) }
|
||||
== FALSE
|
||||
{
|
||||
return Err(Error::last_os_error().into());
|
||||
return Err(TtyError::Io(Error::last_os_error()));
|
||||
}
|
||||
|
||||
let new_mode = if is_raw {
|
||||
|
@ -185,7 +200,7 @@ fn op_set_raw(
|
|||
winapi::um::wincon::WriteConsoleInputW(handle, &record, 1, &mut 0)
|
||||
} == FALSE
|
||||
{
|
||||
return Err(Error::last_os_error().into());
|
||||
return Err(TtyError::Io(Error::last_os_error()));
|
||||
}
|
||||
|
||||
/* Wait for read thread to acknowledge the cancellation to ensure that nothing
|
||||
|
@ -199,7 +214,7 @@ fn op_set_raw(
|
|||
|
||||
// SAFETY: winapi call
|
||||
if unsafe { consoleapi::SetConsoleMode(handle, new_mode) } == FALSE {
|
||||
return Err(Error::last_os_error().into());
|
||||
return Err(TtyError::Io(Error::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -252,7 +267,8 @@ fn op_set_raw(
|
|||
Some(mode) => mode,
|
||||
None => {
|
||||
// Save original mode.
|
||||
let original_mode = termios::tcgetattr(raw_fd)?;
|
||||
let original_mode =
|
||||
termios::tcgetattr(raw_fd).map_err(TtyError::Nix)?;
|
||||
tty_mode_store.set(rid, original_mode.clone());
|
||||
original_mode
|
||||
}
|
||||
|
@ -274,11 +290,13 @@ fn op_set_raw(
|
|||
}
|
||||
raw.control_chars[termios::SpecialCharacterIndices::VMIN as usize] = 1;
|
||||
raw.control_chars[termios::SpecialCharacterIndices::VTIME as usize] = 0;
|
||||
termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &raw)?;
|
||||
termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &raw)
|
||||
.map_err(TtyError::Nix)?;
|
||||
} else {
|
||||
// Try restore saved mode.
|
||||
if let Some(mode) = tty_mode_store.take(rid) {
|
||||
termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &mode)?;
|
||||
termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &mode)
|
||||
.map_err(TtyError::Nix)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,13 +308,16 @@ fn op_set_raw(
|
|||
fn op_console_size(
|
||||
state: &mut OpState,
|
||||
#[buffer] result: &mut [u32],
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), TtyError> {
|
||||
fn check_console_size(
|
||||
state: &mut OpState,
|
||||
result: &mut [u32],
|
||||
rid: u32,
|
||||
) -> Result<(), AnyError> {
|
||||
let fd = state.resource_table.get_fd(rid)?;
|
||||
) -> Result<(), TtyError> {
|
||||
let fd = state
|
||||
.resource_table
|
||||
.get_fd(rid)
|
||||
.map_err(TtyError::Resource)?;
|
||||
let size = console_size_from_fd(fd)?;
|
||||
result[0] = size.cols;
|
||||
result[1] = size.rows;
|
||||
|
@ -419,7 +440,7 @@ mod tests {
|
|||
pub fn op_read_line_prompt(
|
||||
#[string] prompt_text: &str,
|
||||
#[string] default_value: &str,
|
||||
) -> Result<Option<String>, AnyError> {
|
||||
) -> Result<Option<String>, ReadlineError> {
|
||||
let mut editor = Editor::<(), rustyline::history::DefaultHistory>::new()
|
||||
.expect("Failed to create editor.");
|
||||
|
||||
|
@ -439,6 +460,6 @@ pub fn op_read_line_prompt(
|
|||
Ok(None)
|
||||
}
|
||||
Err(ReadlineError::Eof) => Ok(None),
|
||||
Err(err) => Err(err.into()),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
|
||||
/// A utility function to map OsStrings to Strings
|
||||
pub fn into_string(s: std::ffi::OsString) -> Result<String, AnyError> {
|
||||
s.into_string().map_err(|s| {
|
||||
let message = format!("File name or path {s:?} is not valid UTF-8");
|
||||
custom_error("InvalidData", message)
|
||||
})
|
||||
}
|
|
@ -4,15 +4,16 @@ mod sync_fetch;
|
|||
|
||||
use crate::web_worker::WebWorkerInternalHandle;
|
||||
use crate::web_worker::WebWorkerType;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::CancelFuture;
|
||||
use deno_core::OpState;
|
||||
use deno_web::JsMessageData;
|
||||
use deno_web::MessagePortError;
|
||||
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,
|
||||
|
@ -30,17 +31,16 @@ deno_core::extension!(
|
|||
fn op_worker_post_message(
|
||||
state: &mut OpState,
|
||||
#[serde] data: JsMessageData,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), MessagePortError> {
|
||||
let handle = state.borrow::<WebWorkerInternalHandle>().clone();
|
||||
handle.port.send(state, data)?;
|
||||
Ok(())
|
||||
handle.port.send(state, data)
|
||||
}
|
||||
|
||||
#[op2(async(lazy), fast)]
|
||||
#[serde]
|
||||
async fn op_worker_recv_message(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
) -> Result<Option<JsMessageData>, AnyError> {
|
||||
) -> Result<Option<JsMessageData>, MessagePortError> {
|
||||
let handle = {
|
||||
let state = state.borrow();
|
||||
state.borrow::<WebWorkerInternalHandle>().clone()
|
||||
|
@ -50,7 +50,6 @@ async fn op_worker_recv_message(
|
|||
.recv(state.clone())
|
||||
.or_cancel(handle.cancel)
|
||||
.await?
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
|
|
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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".
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,6 @@ use crate::web_worker::WorkerControlEvent;
|
|||
use crate::web_worker::WorkerId;
|
||||
use crate::web_worker::WorkerMetadata;
|
||||
use crate::worker::FormatJsErrorFn;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::serde::Deserialize;
|
||||
use deno_core::CancelFuture;
|
||||
|
@ -22,6 +20,7 @@ use deno_permissions::ChildPermissionsArg;
|
|||
use deno_permissions::PermissionsContainer;
|
||||
use deno_web::deserialize_js_transferables;
|
||||
use deno_web::JsMessageData;
|
||||
use deno_web::MessagePortError;
|
||||
use log::debug;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
@ -119,6 +118,20 @@ pub struct CreateWorkerArgs {
|
|||
close_on_idle: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CreateWorkerError {
|
||||
#[error("Classic workers are not supported.")]
|
||||
ClassicWorkers,
|
||||
#[error(transparent)]
|
||||
Permission(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
ModuleResolution(#[from] deno_core::ModuleResolutionError),
|
||||
#[error(transparent)]
|
||||
MessagePort(#[from] MessagePortError),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// Create worker as the host
|
||||
#[op2]
|
||||
#[serde]
|
||||
|
@ -126,7 +139,7 @@ fn op_create_worker(
|
|||
state: &mut OpState,
|
||||
#[serde] args: CreateWorkerArgs,
|
||||
#[serde] maybe_worker_metadata: Option<JsMessageData>,
|
||||
) -> Result<WorkerId, AnyError> {
|
||||
) -> Result<WorkerId, CreateWorkerError> {
|
||||
let specifier = args.specifier.clone();
|
||||
let maybe_source_code = if args.has_source_code {
|
||||
Some(args.source_code.clone())
|
||||
|
@ -137,10 +150,7 @@ fn op_create_worker(
|
|||
let worker_type = args.worker_type;
|
||||
if let WebWorkerType::Classic = worker_type {
|
||||
if let TestingFeaturesEnabled(false) = state.borrow() {
|
||||
return Err(custom_error(
|
||||
"DOMExceptionNotSupportedError",
|
||||
"Classic workers are not supported.",
|
||||
));
|
||||
return Err(CreateWorkerError::ClassicWorkers);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,7 +164,9 @@ fn op_create_worker(
|
|||
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
|
||||
let worker_permissions = if let Some(child_permissions_arg) = args.permissions
|
||||
{
|
||||
parent_permissions.create_child_permissions(child_permissions_arg)?
|
||||
parent_permissions
|
||||
.create_child_permissions(child_permissions_arg)
|
||||
.map_err(CreateWorkerError::Permission)?
|
||||
} else {
|
||||
parent_permissions.clone()
|
||||
};
|
||||
|
@ -166,9 +178,8 @@ fn op_create_worker(
|
|||
let module_specifier = deno_core::resolve_url(&specifier)?;
|
||||
let worker_name = args_name.unwrap_or_default();
|
||||
|
||||
let (handle_sender, handle_receiver) = std::sync::mpsc::sync_channel::<
|
||||
Result<SendableWebWorkerHandle, AnyError>,
|
||||
>(1);
|
||||
let (handle_sender, handle_receiver) =
|
||||
std::sync::mpsc::sync_channel::<SendableWebWorkerHandle>(1);
|
||||
|
||||
// Setup new thread
|
||||
let thread_builder = std::thread::Builder::new().name(format!("{worker_id}"));
|
||||
|
@ -202,7 +213,7 @@ fn op_create_worker(
|
|||
});
|
||||
|
||||
// Send thread safe handle from newly created worker to host thread
|
||||
handle_sender.send(Ok(external_handle)).unwrap();
|
||||
handle_sender.send(external_handle).unwrap();
|
||||
drop(handle_sender);
|
||||
|
||||
// At this point the only method of communication with host
|
||||
|
@ -218,7 +229,7 @@ fn op_create_worker(
|
|||
})?;
|
||||
|
||||
// Receive WebWorkerHandle from newly created worker
|
||||
let worker_handle = handle_receiver.recv().unwrap()?;
|
||||
let worker_handle = handle_receiver.recv().unwrap();
|
||||
|
||||
let worker_thread = WorkerThread {
|
||||
worker_handle: worker_handle.into(),
|
||||
|
@ -291,7 +302,7 @@ fn close_channel(
|
|||
async fn op_host_recv_ctrl(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[serde] id: WorkerId,
|
||||
) -> Result<WorkerControlEvent, AnyError> {
|
||||
) -> WorkerControlEvent {
|
||||
let (worker_handle, cancel_handle) = {
|
||||
let state = state.borrow();
|
||||
let workers_table = state.borrow::<WorkersTable>();
|
||||
|
@ -300,7 +311,7 @@ async fn op_host_recv_ctrl(
|
|||
(handle.worker_handle.clone(), handle.cancel_handle.clone())
|
||||
} else {
|
||||
// If handle was not found it means worker has already shutdown
|
||||
return Ok(WorkerControlEvent::Close);
|
||||
return WorkerControlEvent::Close;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -309,22 +320,21 @@ async fn op_host_recv_ctrl(
|
|||
.or_cancel(cancel_handle)
|
||||
.await;
|
||||
match maybe_event {
|
||||
Ok(Ok(Some(event))) => {
|
||||
Ok(Some(event)) => {
|
||||
// Terminal error means that worker should be removed from worker table.
|
||||
if let WorkerControlEvent::TerminalError(_) = &event {
|
||||
close_channel(state, id, WorkerChannel::Ctrl);
|
||||
}
|
||||
Ok(event)
|
||||
event
|
||||
}
|
||||
Ok(Ok(None)) => {
|
||||
Ok(None) => {
|
||||
// If there was no event from worker it means it has already been closed.
|
||||
close_channel(state, id, WorkerChannel::Ctrl);
|
||||
Ok(WorkerControlEvent::Close)
|
||||
WorkerControlEvent::Close
|
||||
}
|
||||
Ok(Err(err)) => Err(err),
|
||||
Err(_) => {
|
||||
// The worker was terminated.
|
||||
Ok(WorkerControlEvent::Close)
|
||||
WorkerControlEvent::Close
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -334,7 +344,7 @@ async fn op_host_recv_ctrl(
|
|||
async fn op_host_recv_message(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[serde] id: WorkerId,
|
||||
) -> Result<Option<JsMessageData>, AnyError> {
|
||||
) -> Result<Option<JsMessageData>, MessagePortError> {
|
||||
let (worker_handle, cancel_handle) = {
|
||||
let s = state.borrow();
|
||||
let workers_table = s.borrow::<WorkersTable>();
|
||||
|
@ -359,7 +369,7 @@ async fn op_host_recv_message(
|
|||
}
|
||||
Ok(ret)
|
||||
}
|
||||
Ok(Err(err)) => Err(err.into()),
|
||||
Ok(Err(err)) => Err(err),
|
||||
Err(_) => {
|
||||
// The worker was terminated.
|
||||
Ok(None)
|
||||
|
@ -373,7 +383,7 @@ fn op_host_post_message(
|
|||
state: &mut OpState,
|
||||
#[serde] id: WorkerId,
|
||||
#[serde] data: JsMessageData,
|
||||
) -> Result<(), AnyError> {
|
||||
) -> Result<(), MessagePortError> {
|
||||
if let Some(worker_thread) = state.borrow::<WorkersTable>().get(&id) {
|
||||
debug!("post message to worker {}", id);
|
||||
let worker_handle = worker_thread.worker_handle.clone();
|
||||
|
|
|
@ -166,7 +166,10 @@ pub struct WebWorkerInternalHandle {
|
|||
|
||||
impl WebWorkerInternalHandle {
|
||||
/// Post WorkerEvent to parent as a worker
|
||||
pub fn post_event(&self, event: WorkerControlEvent) -> Result<(), AnyError> {
|
||||
pub fn post_event(
|
||||
&self,
|
||||
event: WorkerControlEvent,
|
||||
) -> Result<(), mpsc::TrySendError<WorkerControlEvent>> {
|
||||
let mut sender = self.sender.clone();
|
||||
// If the channel is closed,
|
||||
// the worker must have terminated but the termination message has not yet been received.
|
||||
|
@ -176,8 +179,7 @@ impl WebWorkerInternalHandle {
|
|||
self.has_terminated.store(true, Ordering::SeqCst);
|
||||
return Ok(());
|
||||
}
|
||||
sender.try_send(event)?;
|
||||
Ok(())
|
||||
sender.try_send(event)
|
||||
}
|
||||
|
||||
/// Check if this worker is terminated or being terminated
|
||||
|
@ -263,11 +265,9 @@ impl WebWorkerHandle {
|
|||
/// Get the WorkerEvent with lock
|
||||
/// Return error if more than one listener tries to get event
|
||||
#[allow(clippy::await_holding_refcell_ref)] // TODO(ry) remove!
|
||||
pub async fn get_control_event(
|
||||
&self,
|
||||
) -> Result<Option<WorkerControlEvent>, AnyError> {
|
||||
pub async fn get_control_event(&self) -> Option<WorkerControlEvent> {
|
||||
let mut receiver = self.receiver.borrow_mut();
|
||||
Ok(receiver.next().await)
|
||||
receiver.next().await
|
||||
}
|
||||
|
||||
/// Terminate the worker
|
||||
|
|
|
@ -5,101 +5,101 @@ Deno.test(
|
|||
{ ignore: Deno.build.os !== "windows" },
|
||||
function signalsNotImplemented() {
|
||||
const msg =
|
||||
"Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK).";
|
||||
"Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got ";
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGALRM", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGALRM",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGCHLD", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGCHLD",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGHUP", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGHUP",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGIO", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGIO",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGPIPE", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGPIPE",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGQUIT", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGQUIT",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGTERM", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGTERM",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGUSR1", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGUSR1",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGUSR2", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGUSR2",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
Deno.addSignalListener("SIGWINCH", () => {});
|
||||
},
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGWINCH",
|
||||
);
|
||||
assertThrows(
|
||||
() => Deno.addSignalListener("SIGKILL", () => {}),
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGKILL",
|
||||
);
|
||||
assertThrows(
|
||||
() => Deno.addSignalListener("SIGSTOP", () => {}),
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGSTOP",
|
||||
);
|
||||
assertThrows(
|
||||
() => Deno.addSignalListener("SIGILL", () => {}),
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGILL",
|
||||
);
|
||||
assertThrows(
|
||||
() => Deno.addSignalListener("SIGFPE", () => {}),
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGFPE",
|
||||
);
|
||||
assertThrows(
|
||||
() => Deno.addSignalListener("SIGSEGV", () => {}),
|
||||
Error,
|
||||
msg,
|
||||
msg + "SIGSEGV",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue