1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

refactor(ext/net): use concrete error type (#26227)

This commit is contained in:
Leo Kettmeir 2024-10-17 09:43:04 -07:00 committed by GitHub
parent 3b62e05062
commit ed13efc4ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 317 additions and 191 deletions

13
Cargo.lock generated
View file

@ -1423,9 +1423,9 @@ dependencies = [
[[package]]
name = "deno_core"
version = "0.313.0"
version = "0.314.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29f36be738d78e39b6603a6b07f1cf91e28baf3681f87205f07482999e0d0bc2"
checksum = "1fcd11ab87426c611b7170138a768dad7170c8fb66d8095b773d25e58fd254ea"
dependencies = [
"anyhow",
"bincode",
@ -1797,6 +1797,7 @@ dependencies = [
"rustls-tokio-stream",
"serde",
"socket2",
"thiserror",
"tokio",
"trust-dns-proto",
"trust-dns-resolver",
@ -1913,9 +1914,9 @@ dependencies = [
[[package]]
name = "deno_ops"
version = "0.189.0"
version = "0.190.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8f998ad1d5b36064109367ffe67b1088385eb3d8025efc95e445bc013a147a2"
checksum = "a48a3e06cace18a2c49e148da067678c6af80e70757a8c3991301397cf6b9919"
dependencies = [
"proc-macro-rules",
"proc-macro2",
@ -6211,9 +6212,9 @@ dependencies = [
[[package]]
name = "serde_v8"
version = "0.222.0"
version = "0.223.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27130b5cd87f6f06228940a1f3a7ecc988ea13d1bede1398a48d74cb59dabc9a"
checksum = "c127bb9f2024433d06789b242477c808fd7f7dc4c3278576dd5bc99c4e5c75ff"
dependencies = [
"num-bigint",
"serde",

View file

@ -46,7 +46,7 @@ repository = "https://github.com/denoland/deno"
[workspace.dependencies]
deno_ast = { version = "=0.42.2", features = ["transpiling"] }
deno_core = { version = "0.313.0" }
deno_core = { version = "0.314.1" }
deno_bench_util = { version = "0.166.0", path = "./bench_util" }
deno_lockfile = "=0.23.1"

2
ext/cache/lib.rs vendored
View file

@ -28,7 +28,7 @@ pub enum CacheError {
Resource(deno_core::error::AnyError),
#[error(transparent)]
Other(deno_core::error::AnyError),
#[error(transparent)]
#[error("{0}")]
Io(#[from] std::io::Error),
}

View file

@ -21,6 +21,7 @@ pin-project.workspace = true
rustls-tokio-stream.workspace = true
serde.workspace = true
socket2.workspace = true
thiserror.workspace = true
tokio.workspace = true
trust-dns-proto = "0.23"
trust-dns-resolver = { version = "0.23", features = ["tokio-runtime", "serde-config"] }

View file

@ -1,7 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::futures::TryFutureExt;
use deno_core::AsyncMutFuture;
use deno_core::AsyncRefCell;
use deno_core::AsyncResult;
@ -69,25 +68,36 @@ where
pub async fn read(
self: Rc<Self>,
data: &mut [u8],
) -> Result<usize, AnyError> {
) -> Result<usize, std::io::Error> {
let mut rd = self.rd_borrow_mut().await;
let nread = rd.read(data).try_or_cancel(self.cancel_handle()).await?;
Ok(nread)
}
pub async fn write(self: Rc<Self>, data: &[u8]) -> Result<usize, AnyError> {
pub async fn write(
self: Rc<Self>,
data: &[u8],
) -> Result<usize, std::io::Error> {
let mut wr = self.wr_borrow_mut().await;
let nwritten = wr.write(data).await?;
Ok(nwritten)
}
pub async fn shutdown(self: Rc<Self>) -> Result<(), AnyError> {
pub async fn shutdown(self: Rc<Self>) -> Result<(), std::io::Error> {
let mut wr = self.wr_borrow_mut().await;
wr.shutdown().await?;
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum MapError {
#[error("{0}")]
Io(std::io::Error),
#[error("Unable to get resources")]
NoResources,
}
pub type TcpStreamResource =
FullDuplexResource<tcp::OwnedReadHalf, tcp::OwnedWriteHalf>;
@ -100,7 +110,7 @@ impl Resource for TcpStreamResource {
}
fn shutdown(self: Rc<Self>) -> AsyncResult<()> {
Box::pin(self.shutdown())
Box::pin(self.shutdown().map_err(Into::into))
}
fn close(self: Rc<Self>) {
@ -109,31 +119,30 @@ impl Resource for TcpStreamResource {
}
impl TcpStreamResource {
pub fn set_nodelay(self: Rc<Self>, nodelay: bool) -> Result<(), AnyError> {
self.map_socket(Box::new(move |socket| Ok(socket.set_nodelay(nodelay)?)))
pub fn set_nodelay(self: Rc<Self>, nodelay: bool) -> Result<(), MapError> {
self.map_socket(Box::new(move |socket| socket.set_nodelay(nodelay)))
}
pub fn set_keepalive(
self: Rc<Self>,
keepalive: bool,
) -> Result<(), AnyError> {
self
.map_socket(Box::new(move |socket| Ok(socket.set_keepalive(keepalive)?)))
) -> Result<(), MapError> {
self.map_socket(Box::new(move |socket| socket.set_keepalive(keepalive)))
}
#[allow(clippy::type_complexity)]
fn map_socket(
self: Rc<Self>,
map: Box<dyn FnOnce(SockRef) -> Result<(), AnyError>>,
) -> Result<(), AnyError> {
map: Box<dyn FnOnce(SockRef) -> Result<(), std::io::Error>>,
) -> Result<(), MapError> {
if let Some(wr) = RcRef::map(self, |r| &r.wr).try_borrow() {
let stream = wr.as_ref().as_ref();
let socket = socket2::SockRef::from(stream);
return map(socket);
return map(socket).map_err(MapError::Io);
}
Err(generic_error("Unable to get resources"))
Err(MapError::NoResources)
}
}
@ -153,7 +162,9 @@ impl UnixStreamResource {
unreachable!()
}
#[allow(clippy::unused_async)]
pub async fn shutdown(self: Rc<Self>) -> Result<(), AnyError> {
pub async fn shutdown(
self: Rc<Self>,
) -> Result<(), deno_core::error::AnyError> {
unreachable!()
}
pub fn cancel_read_ops(&self) {
@ -170,7 +181,7 @@ impl Resource for UnixStreamResource {
}
fn shutdown(self: Rc<Self>) -> AsyncResult<()> {
Box::pin(self.shutdown())
Box::pin(self.shutdown().map_err(Into::into))
}
fn close(self: Rc<Self>) {

View file

@ -6,10 +6,6 @@ use crate::resolve_addr::resolve_addr;
use crate::resolve_addr::resolve_addr_sync;
use crate::tcp::TcpListener;
use crate::NetPermissions;
use deno_core::error::bad_resource;
use deno_core::error::custom_error;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::CancelFuture;
@ -43,6 +39,7 @@ use trust_dns_proto::rr::record_type::RecordType;
use trust_dns_resolver::config::NameServerConfigGroup;
use trust_dns_resolver::config::ResolverConfig;
use trust_dns_resolver::config::ResolverOpts;
use trust_dns_resolver::error::ResolveError;
use trust_dns_resolver::error::ResolveErrorKind;
use trust_dns_resolver::system_conf;
use trust_dns_resolver::AsyncResolver;
@ -68,11 +65,69 @@ impl From<SocketAddr> for IpAddr {
}
}
pub(crate) fn accept_err(e: std::io::Error) -> AnyError {
#[derive(Debug, thiserror::Error)]
pub enum NetError {
#[error("Listener has been closed")]
ListenerClosed,
#[error("Listener already in use")]
ListenerBusy,
#[error("Socket has been closed")]
SocketClosed,
#[error("Socket has been closed")]
SocketClosedNotConnected,
#[error("Socket already in use")]
SocketBusy,
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("Another accept task is ongoing")]
AcceptTaskOngoing,
#[error("{0}")]
Permission(deno_core::error::AnyError),
#[error("{0}")]
Resource(deno_core::error::AnyError),
#[error("No resolved address found")]
NoResolvedAddress,
#[error("{0}")]
AddrParse(#[from] std::net::AddrParseError),
#[error("{0}")]
Map(crate::io::MapError),
#[error("{0}")]
Canceled(#[from] deno_core::Canceled),
#[error("{0}")]
DnsNotFound(ResolveError),
#[error("{0}")]
DnsNotConnected(ResolveError),
#[error("{0}")]
DnsTimedOut(ResolveError),
#[error("{0}")]
Dns(#[from] ResolveError),
#[error("Provided record type is not supported")]
UnsupportedRecordType,
#[error("File name or path {0:?} is not valid UTF-8")]
InvalidUtf8(std::ffi::OsString),
#[error("unexpected key type")]
UnexpectedKeyType,
#[error("Invalid hostname: '{0}'")]
InvalidHostname(String), // TypeError
#[error("TCP stream is currently in use")]
TcpStreamBusy,
#[error("{0}")]
Rustls(#[from] deno_tls::rustls::Error),
#[error("{0}")]
Tls(#[from] deno_tls::TlsError),
#[error("Error creating TLS certificate: Deno.listenTls requires a key")]
ListenTlsRequiresKey, // InvalidData
#[error("{0}")]
RootCertStore(deno_core::anyhow::Error),
#[error("{0}")]
Reunite(tokio::net::tcp::ReuniteError),
}
pub(crate) fn accept_err(e: std::io::Error) -> NetError {
if let std::io::ErrorKind::Interrupted = e.kind() {
bad_resource("Listener has been closed")
NetError::ListenerClosed
} else {
e.into()
NetError::Io(e)
}
}
@ -81,15 +136,15 @@ pub(crate) fn accept_err(e: std::io::Error) -> AnyError {
pub async fn op_net_accept_tcp(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
) -> Result<(ResourceId, IpAddr, IpAddr), AnyError> {
) -> Result<(ResourceId, IpAddr, IpAddr), NetError> {
let resource = state
.borrow()
.resource_table
.get::<NetworkListenerResource<TcpListener>>(rid)
.map_err(|_| bad_resource("Listener has been closed"))?;
.map_err(|_| NetError::ListenerClosed)?;
let listener = RcRef::map(&resource, |r| &r.listener)
.try_borrow_mut()
.ok_or_else(|| custom_error("Busy", "Another accept task is ongoing"))?;
.ok_or_else(|| NetError::AcceptTaskOngoing)?;
let cancel = RcRef::map(resource, |r| &r.cancel);
let (tcp_stream, _socket_addr) = listener
.accept()
@ -112,12 +167,12 @@ pub async fn op_net_recv_udp(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
#[buffer] mut buf: JsBuffer,
) -> Result<(usize, IpAddr), AnyError> {
) -> Result<(usize, IpAddr), NetError> {
let resource = state
.borrow_mut()
.resource_table
.get::<UdpSocketResource>(rid)
.map_err(|_| bad_resource("Socket has been closed"))?;
.map_err(|_| NetError::SocketClosed)?;
let socket = RcRef::map(&resource, |r| &r.socket).borrow().await;
let cancel_handle = RcRef::map(&resource, |r| &r.cancel);
let (nread, remote_addr) = socket
@ -134,27 +189,29 @@ pub async fn op_net_send_udp<NP>(
#[smi] rid: ResourceId,
#[serde] addr: IpAddr,
#[buffer] zero_copy: JsBuffer,
) -> Result<usize, AnyError>
) -> Result<usize, NetError>
where
NP: NetPermissions + 'static,
{
{
let mut s = state.borrow_mut();
s.borrow_mut::<NP>().check_net(
&(&addr.hostname, Some(addr.port)),
"Deno.DatagramConn.send()",
)?;
s.borrow_mut::<NP>()
.check_net(
&(&addr.hostname, Some(addr.port)),
"Deno.DatagramConn.send()",
)
.map_err(NetError::Permission)?;
}
let addr = resolve_addr(&addr.hostname, addr.port)
.await?
.next()
.ok_or_else(|| generic_error("No resolved address found"))?;
.ok_or(NetError::NoResolvedAddress)?;
let resource = state
.borrow_mut()
.resource_table
.get::<UdpSocketResource>(rid)
.map_err(|_| bad_resource("Socket has been closed"))?;
.map_err(|_| NetError::SocketClosed)?;
let socket = RcRef::map(&resource, |r| &r.socket).borrow().await;
let nwritten = socket.send_to(&zero_copy, &addr).await?;
@ -167,12 +224,12 @@ pub async fn op_net_join_multi_v4_udp(
#[smi] rid: ResourceId,
#[string] address: String,
#[string] multi_interface: String,
) -> Result<(), AnyError> {
) -> Result<(), NetError> {
let resource = state
.borrow_mut()
.resource_table
.get::<UdpSocketResource>(rid)
.map_err(|_| bad_resource("Socket has been closed"))?;
.map_err(|_| NetError::SocketClosed)?;
let socket = RcRef::map(&resource, |r| &r.socket).borrow().await;
let addr = Ipv4Addr::from_str(address.as_str())?;
@ -189,12 +246,12 @@ pub async fn op_net_join_multi_v6_udp(
#[smi] rid: ResourceId,
#[string] address: String,
#[smi] multi_interface: u32,
) -> Result<(), AnyError> {
) -> Result<(), NetError> {
let resource = state
.borrow_mut()
.resource_table
.get::<UdpSocketResource>(rid)
.map_err(|_| bad_resource("Socket has been closed"))?;
.map_err(|_| NetError::SocketClosed)?;
let socket = RcRef::map(&resource, |r| &r.socket).borrow().await;
let addr = Ipv6Addr::from_str(address.as_str())?;
@ -210,12 +267,12 @@ pub async fn op_net_leave_multi_v4_udp(
#[smi] rid: ResourceId,
#[string] address: String,
#[string] multi_interface: String,
) -> Result<(), AnyError> {
) -> Result<(), NetError> {
let resource = state
.borrow_mut()
.resource_table
.get::<UdpSocketResource>(rid)
.map_err(|_| bad_resource("Socket has been closed"))?;
.map_err(|_| NetError::SocketClosed)?;
let socket = RcRef::map(&resource, |r| &r.socket).borrow().await;
let addr = Ipv4Addr::from_str(address.as_str())?;
@ -232,12 +289,12 @@ pub async fn op_net_leave_multi_v6_udp(
#[smi] rid: ResourceId,
#[string] address: String,
#[smi] multi_interface: u32,
) -> Result<(), AnyError> {
) -> Result<(), NetError> {
let resource = state
.borrow_mut()
.resource_table
.get::<UdpSocketResource>(rid)
.map_err(|_| bad_resource("Socket has been closed"))?;
.map_err(|_| NetError::SocketClosed)?;
let socket = RcRef::map(&resource, |r| &r.socket).borrow().await;
let addr = Ipv6Addr::from_str(address.as_str())?;
@ -253,16 +310,16 @@ pub async fn op_net_set_multi_loopback_udp(
#[smi] rid: ResourceId,
is_v4_membership: bool,
loopback: bool,
) -> Result<(), AnyError> {
) -> Result<(), NetError> {
let resource = state
.borrow_mut()
.resource_table
.get::<UdpSocketResource>(rid)
.map_err(|_| bad_resource("Socket has been closed"))?;
.map_err(|_| NetError::SocketClosed)?;
let socket = RcRef::map(&resource, |r| &r.socket).borrow().await;
if is_v4_membership {
socket.set_multicast_loop_v4(loopback)?
socket.set_multicast_loop_v4(loopback)?;
} else {
socket.set_multicast_loop_v6(loopback)?;
}
@ -275,12 +332,12 @@ pub async fn op_net_set_multi_ttl_udp(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
#[smi] ttl: u32,
) -> Result<(), AnyError> {
) -> Result<(), NetError> {
let resource = state
.borrow_mut()
.resource_table
.get::<UdpSocketResource>(rid)
.map_err(|_| bad_resource("Socket has been closed"))?;
.map_err(|_| NetError::SocketClosed)?;
let socket = RcRef::map(&resource, |r| &r.socket).borrow().await;
socket.set_multicast_ttl_v4(ttl)?;
@ -293,7 +350,7 @@ pub async fn op_net_set_multi_ttl_udp(
pub async fn op_net_connect_tcp<NP>(
state: Rc<RefCell<OpState>>,
#[serde] addr: IpAddr,
) -> Result<(ResourceId, IpAddr, IpAddr), AnyError>
) -> Result<(ResourceId, IpAddr, IpAddr), NetError>
where
NP: NetPermissions + 'static,
{
@ -304,7 +361,7 @@ where
pub async fn op_net_connect_tcp_inner<NP>(
state: Rc<RefCell<OpState>>,
addr: IpAddr,
) -> Result<(ResourceId, IpAddr, IpAddr), AnyError>
) -> Result<(ResourceId, IpAddr, IpAddr), NetError>
where
NP: NetPermissions + 'static,
{
@ -312,13 +369,14 @@ where
let mut state_ = state.borrow_mut();
state_
.borrow_mut::<NP>()
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.connect()")?;
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.connect()")
.map_err(NetError::Permission)?;
}
let addr = resolve_addr(&addr.hostname, addr.port)
.await?
.next()
.ok_or_else(|| generic_error("No resolved address found"))?;
.ok_or_else(|| NetError::NoResolvedAddress)?;
let tcp_stream = TcpStream::connect(&addr).await?;
let local_addr = tcp_stream.local_addr()?;
let remote_addr = tcp_stream.peer_addr()?;
@ -353,7 +411,7 @@ pub fn op_net_listen_tcp<NP>(
#[serde] addr: IpAddr,
reuse_port: bool,
load_balanced: bool,
) -> Result<(ResourceId, IpAddr), AnyError>
) -> Result<(ResourceId, IpAddr), NetError>
where
NP: NetPermissions + 'static,
{
@ -362,10 +420,11 @@ where
}
state
.borrow_mut::<NP>()
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.listen()")?;
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.listen()")
.map_err(NetError::Permission)?;
let addr = resolve_addr_sync(&addr.hostname, addr.port)?
.next()
.ok_or_else(|| generic_error("No resolved address found"))?;
.ok_or_else(|| NetError::NoResolvedAddress)?;
let listener = if load_balanced {
TcpListener::bind_load_balanced(addr)
@ -384,16 +443,17 @@ fn net_listen_udp<NP>(
addr: IpAddr,
reuse_address: bool,
loopback: bool,
) -> Result<(ResourceId, IpAddr), AnyError>
) -> Result<(ResourceId, IpAddr), NetError>
where
NP: NetPermissions + 'static,
{
state
.borrow_mut::<NP>()
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenDatagram()")?;
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenDatagram()")
.map_err(NetError::Permission)?;
let addr = resolve_addr_sync(&addr.hostname, addr.port)?
.next()
.ok_or_else(|| generic_error("No resolved address found"))?;
.ok_or_else(|| NetError::NoResolvedAddress)?;
let domain = if addr.is_ipv4() {
Domain::IPV4
@ -453,7 +513,7 @@ pub fn op_net_listen_udp<NP>(
#[serde] addr: IpAddr,
reuse_address: bool,
loopback: bool,
) -> Result<(ResourceId, IpAddr), AnyError>
) -> Result<(ResourceId, IpAddr), NetError>
where
NP: NetPermissions + 'static,
{
@ -468,7 +528,7 @@ pub fn op_node_unstable_net_listen_udp<NP>(
#[serde] addr: IpAddr,
reuse_address: bool,
loopback: bool,
) -> Result<(ResourceId, IpAddr), AnyError>
) -> Result<(ResourceId, IpAddr), NetError>
where
NP: NetPermissions + 'static,
{
@ -551,7 +611,7 @@ pub struct NameServer {
pub async fn op_dns_resolve<NP>(
state: Rc<RefCell<OpState>>,
#[serde] args: ResolveAddrArgs,
) -> Result<Vec<DnsReturnRecord>, AnyError>
) -> Result<Vec<DnsReturnRecord>, NetError>
where
NP: NetPermissions + 'static,
{
@ -587,7 +647,9 @@ where
let socker_addr = &ns.socket_addr;
let ip = socker_addr.ip().to_string();
let port = socker_addr.port();
perm.check_net(&(ip, Some(port)), "Deno.resolveDns()")?;
perm
.check_net(&(ip, Some(port)), "Deno.resolveDns()")
.map_err(NetError::Permission)?;
}
}
@ -618,22 +680,17 @@ where
};
lookup
.map_err(|e| {
let message = format!("{e}");
match e.kind() {
ResolveErrorKind::NoRecordsFound { .. } => {
custom_error("NotFound", message)
}
ResolveErrorKind::Message("No connections available") => {
custom_error("NotConnected", message)
}
ResolveErrorKind::Timeout => custom_error("TimedOut", message),
_ => generic_error(message),
.map_err(|e| match e.kind() {
ResolveErrorKind::NoRecordsFound { .. } => NetError::DnsNotFound(e),
ResolveErrorKind::Message("No connections available") => {
NetError::DnsNotConnected(e)
}
ResolveErrorKind::Timeout => NetError::DnsTimedOut(e),
_ => NetError::Dns(e),
})?
.iter()
.filter_map(|rdata| rdata_to_return_record(record_type)(rdata).transpose())
.collect::<Result<Vec<DnsReturnRecord>, AnyError>>()
.collect::<Result<Vec<DnsReturnRecord>, NetError>>()
}
#[op2(fast)]
@ -641,7 +698,7 @@ pub fn op_set_nodelay(
state: &mut OpState,
#[smi] rid: ResourceId,
nodelay: bool,
) -> Result<(), AnyError> {
) -> Result<(), NetError> {
op_set_nodelay_inner(state, rid, nodelay)
}
@ -650,10 +707,12 @@ pub fn op_set_nodelay_inner(
state: &mut OpState,
rid: ResourceId,
nodelay: bool,
) -> Result<(), AnyError> {
let resource: Rc<TcpStreamResource> =
state.resource_table.get::<TcpStreamResource>(rid)?;
resource.set_nodelay(nodelay)
) -> Result<(), NetError> {
let resource: Rc<TcpStreamResource> = state
.resource_table
.get::<TcpStreamResource>(rid)
.map_err(NetError::Resource)?;
resource.set_nodelay(nodelay).map_err(NetError::Map)
}
#[op2(fast)]
@ -661,7 +720,7 @@ pub fn op_set_keepalive(
state: &mut OpState,
#[smi] rid: ResourceId,
keepalive: bool,
) -> Result<(), AnyError> {
) -> Result<(), NetError> {
op_set_keepalive_inner(state, rid, keepalive)
}
@ -670,17 +729,19 @@ pub fn op_set_keepalive_inner(
state: &mut OpState,
rid: ResourceId,
keepalive: bool,
) -> Result<(), AnyError> {
let resource: Rc<TcpStreamResource> =
state.resource_table.get::<TcpStreamResource>(rid)?;
resource.set_keepalive(keepalive)
) -> Result<(), NetError> {
let resource: Rc<TcpStreamResource> = state
.resource_table
.get::<TcpStreamResource>(rid)
.map_err(NetError::Resource)?;
resource.set_keepalive(keepalive).map_err(NetError::Map)
}
fn rdata_to_return_record(
ty: RecordType,
) -> impl Fn(&RData) -> Result<Option<DnsReturnRecord>, AnyError> {
) -> impl Fn(&RData) -> Result<Option<DnsReturnRecord>, NetError> {
use RecordType::*;
move |r: &RData| -> Result<Option<DnsReturnRecord>, AnyError> {
move |r: &RData| -> Result<Option<DnsReturnRecord>, NetError> {
let record = match ty {
A => r.as_a().map(ToString::to_string).map(DnsReturnRecord::A),
AAAA => r
@ -761,12 +822,7 @@ fn rdata_to_return_record(
.collect();
DnsReturnRecord::Txt(texts)
}),
_ => {
return Err(custom_error(
"NotSupported",
"Provided record type is not supported",
))
}
_ => return Err(NetError::UnsupportedRecordType),
};
Ok(record)
}
@ -985,7 +1041,7 @@ mod tests {
&mut self,
_host: &(T, Option<u16>),
_api_name: &str,
) -> Result<(), AnyError> {
) -> Result<(), deno_core::error::AnyError> {
Ok(())
}
@ -993,7 +1049,7 @@ mod tests {
&mut self,
p: &str,
_api_name: &str,
) -> Result<PathBuf, AnyError> {
) -> Result<PathBuf, deno_core::error::AnyError> {
Ok(PathBuf::from(p))
}
@ -1001,7 +1057,7 @@ mod tests {
&mut self,
p: &str,
_api_name: &str,
) -> Result<PathBuf, AnyError> {
) -> Result<PathBuf, deno_core::error::AnyError> {
Ok(PathBuf::from(p))
}
@ -1009,7 +1065,7 @@ mod tests {
&mut self,
p: &'a Path,
_api_name: &str,
) -> Result<Cow<'a, Path>, AnyError> {
) -> Result<Cow<'a, Path>, deno_core::error::AnyError> {
Ok(Cow::Borrowed(p))
}
}
@ -1091,7 +1147,7 @@ mod tests {
let vals = result.unwrap();
rid = rid.or(Some(vals.0));
}
};
}
let rid = rid.unwrap();
let state = runtime.op_state();

View file

@ -2,6 +2,7 @@
use crate::io::TcpStreamResource;
use crate::ops::IpAddr;
use crate::ops::NetError;
use crate::ops::TlsHandshakeInfo;
use crate::raw::NetworkListenerResource;
use crate::resolve_addr::resolve_addr;
@ -10,13 +11,7 @@ use crate::tcp::TcpListener;
use crate::DefaultTlsOptions;
use crate::NetPermissions;
use crate::UnsafelyIgnoreCertificateErrors;
use deno_core::anyhow::anyhow;
use deno_core::anyhow::bail;
use deno_core::error::bad_resource;
use deno_core::error::custom_error;
use deno_core::error::generic_error;
use deno_core::error::invalid_hostname;
use deno_core::error::AnyError;
use deno_core::futures::TryFutureExt;
use deno_core::op2;
use deno_core::v8;
use deno_core::AsyncRefCell;
@ -118,20 +113,23 @@ impl TlsStreamResource {
pub async fn read(
self: Rc<Self>,
data: &mut [u8],
) -> Result<usize, AnyError> {
) -> Result<usize, std::io::Error> {
let mut rd = RcRef::map(&self, |r| &r.rd).borrow_mut().await;
let cancel_handle = RcRef::map(&self, |r| &r.cancel_handle);
Ok(rd.read(data).try_or_cancel(cancel_handle).await?)
rd.read(data).try_or_cancel(cancel_handle).await
}
pub async fn write(self: Rc<Self>, data: &[u8]) -> Result<usize, AnyError> {
pub async fn write(
self: Rc<Self>,
data: &[u8],
) -> Result<usize, std::io::Error> {
let mut wr = RcRef::map(self, |r| &r.wr).borrow_mut().await;
let nwritten = wr.write(data).await?;
wr.flush().await?;
Ok(nwritten)
}
pub async fn shutdown(self: Rc<Self>) -> Result<(), AnyError> {
pub async fn shutdown(self: Rc<Self>) -> Result<(), std::io::Error> {
let mut wr = RcRef::map(self, |r| &r.wr).borrow_mut().await;
wr.shutdown().await?;
Ok(())
@ -139,7 +137,7 @@ impl TlsStreamResource {
pub async fn handshake(
self: &Rc<Self>,
) -> Result<TlsHandshakeInfo, AnyError> {
) -> Result<TlsHandshakeInfo, std::io::Error> {
if let Some(tls_info) = &*self.handshake_info.borrow() {
return Ok(tls_info.clone());
}
@ -164,7 +162,7 @@ impl Resource for TlsStreamResource {
}
fn shutdown(self: Rc<Self>) -> AsyncResult<()> {
Box::pin(self.shutdown())
Box::pin(self.shutdown().map_err(Into::into))
}
fn close(self: Rc<Self>) {
@ -201,7 +199,7 @@ pub fn op_tls_key_null() -> TlsKeysHolder {
pub fn op_tls_key_static(
#[string] cert: &str,
#[string] key: &str,
) -> Result<TlsKeysHolder, AnyError> {
) -> Result<TlsKeysHolder, deno_tls::TlsError> {
let cert = load_certs(&mut BufReader::new(cert.as_bytes()))?;
let key = load_private_keys(key.as_bytes())?
.into_iter()
@ -236,9 +234,9 @@ pub fn op_tls_cert_resolver_resolve(
#[cppgc] lookup: &TlsKeyLookup,
#[string] sni: String,
#[cppgc] key: &TlsKeysHolder,
) -> Result<(), AnyError> {
) -> Result<(), NetError> {
let TlsKeys::Static(key) = key.take() else {
bail!("unexpected key type");
return Err(NetError::UnexpectedKeyType);
};
lookup.resolve(sni, Ok(key));
Ok(())
@ -258,7 +256,7 @@ pub fn op_tls_cert_resolver_resolve_error(
pub fn op_tls_start<NP>(
state: Rc<RefCell<OpState>>,
#[serde] args: StartTlsArgs,
) -> Result<(ResourceId, IpAddr, IpAddr), AnyError>
) -> Result<(ResourceId, IpAddr, IpAddr), NetError>
where
NP: NetPermissions + 'static,
{
@ -271,7 +269,9 @@ where
{
let mut s = state.borrow_mut();
let permissions = s.borrow_mut::<NP>();
permissions.check_net(&(&hostname, Some(0)), "Deno.startTls()")?;
permissions
.check_net(&(&hostname, Some(0)), "Deno.startTls()")
.map_err(NetError::Permission)?;
}
let ca_certs = args
@ -281,7 +281,7 @@ where
.collect::<Vec<_>>();
let hostname_dns = ServerName::try_from(hostname.to_string())
.map_err(|_| invalid_hostname(&hostname))?;
.map_err(|_| NetError::InvalidHostname(hostname))?;
let unsafely_ignore_certificate_errors = state
.borrow()
@ -291,19 +291,21 @@ where
let root_cert_store = state
.borrow()
.borrow::<DefaultTlsOptions>()
.root_cert_store()?;
.root_cert_store()
.map_err(NetError::RootCertStore)?;
let resource_rc = state
.borrow_mut()
.resource_table
.take::<TcpStreamResource>(rid)?;
.take::<TcpStreamResource>(rid)
.map_err(NetError::Resource)?;
// This TCP connection might be used somewhere else. If it's the case, we cannot proceed with the
// process of starting a TLS connection 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"))?;
let resource =
Rc::try_unwrap(resource_rc).map_err(|_| NetError::TcpStreamBusy)?;
let (read_half, write_half) = resource.into_inner();
let tcp_stream = read_half.reunite(write_half)?;
let tcp_stream = read_half.reunite(write_half).map_err(NetError::Reunite)?;
let local_addr = tcp_stream.local_addr()?;
let remote_addr = tcp_stream.peer_addr()?;
@ -345,7 +347,7 @@ pub async fn op_net_connect_tls<NP>(
#[serde] addr: IpAddr,
#[serde] args: ConnectTlsArgs,
#[cppgc] key_pair: &TlsKeysHolder,
) -> Result<(ResourceId, IpAddr, IpAddr), AnyError>
) -> Result<(ResourceId, IpAddr, IpAddr), NetError>
where
NP: NetPermissions + 'static,
{
@ -359,9 +361,14 @@ where
let mut s = state.borrow_mut();
let permissions = s.borrow_mut::<NP>();
permissions
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectTls()")?;
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectTls()")
.map_err(NetError::Permission)?;
if let Some(path) = cert_file {
Some(permissions.check_read(path, "Deno.connectTls()")?)
Some(
permissions
.check_read(path, "Deno.connectTls()")
.map_err(NetError::Permission)?,
)
} else {
None
}
@ -382,17 +389,18 @@ where
let root_cert_store = state
.borrow()
.borrow::<DefaultTlsOptions>()
.root_cert_store()?;
.root_cert_store()
.map_err(NetError::RootCertStore)?;
let hostname_dns = if let Some(server_name) = args.server_name {
ServerName::try_from(server_name)
} else {
ServerName::try_from(addr.hostname.clone())
}
.map_err(|_| invalid_hostname(&addr.hostname))?;
.map_err(|_| NetError::InvalidHostname(addr.hostname.clone()))?;
let connect_addr = resolve_addr(&addr.hostname, addr.port)
.await?
.next()
.ok_or_else(|| generic_error("No resolved address found"))?;
.ok_or_else(|| NetError::NoResolvedAddress)?;
let tcp_stream = TcpStream::connect(connect_addr).await?;
let local_addr = tcp_stream.local_addr()?;
let remote_addr = tcp_stream.peer_addr()?;
@ -444,7 +452,7 @@ pub fn op_net_listen_tls<NP>(
#[serde] addr: IpAddr,
#[serde] args: ListenTlsArgs,
#[cppgc] keys: &TlsKeysHolder,
) -> Result<(ResourceId, IpAddr), AnyError>
) -> Result<(ResourceId, IpAddr), NetError>
where
NP: NetPermissions + 'static,
{
@ -455,12 +463,13 @@ where
{
let permissions = state.borrow_mut::<NP>();
permissions
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenTls()")?;
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenTls()")
.map_err(NetError::Permission)?;
}
let bind_addr = resolve_addr_sync(&addr.hostname, addr.port)?
.next()
.ok_or_else(|| generic_error("No resolved address found"))?;
.ok_or(NetError::NoResolvedAddress)?;
let tcp_listener = if args.load_balanced {
TcpListener::bind_load_balanced(bind_addr)
@ -475,28 +484,24 @@ where
.map(|s| s.into_bytes())
.collect();
let listener = match keys.take() {
TlsKeys::Null => Err(anyhow!("Deno.listenTls requires a key")),
TlsKeys::Null => return Err(NetError::ListenTlsRequiresKey),
TlsKeys::Static(TlsKey(cert, key)) => {
let mut tls_config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert, key)
.map_err(|e| anyhow!(e))?;
.with_single_cert(cert, key)?;
tls_config.alpn_protocols = alpn;
Ok(TlsListener {
TlsListener {
tcp_listener,
tls_config: Some(tls_config.into()),
server_config_provider: None,
})
}
}
TlsKeys::Resolver(resolver) => Ok(TlsListener {
TlsKeys::Resolver(resolver) => TlsListener {
tcp_listener,
tls_config: None,
server_config_provider: Some(resolver.into_server_config_provider(alpn)),
}),
}
.map_err(|e| {
custom_error("InvalidData", "Error creating TLS certificate").context(e)
})?;
},
};
let tls_listener_resource = NetworkListenerResource::new(listener);
@ -510,23 +515,23 @@ where
pub async fn op_net_accept_tls(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
) -> Result<(ResourceId, IpAddr, IpAddr), AnyError> {
) -> Result<(ResourceId, IpAddr, IpAddr), NetError> {
let resource = state
.borrow()
.resource_table
.get::<NetworkListenerResource<TlsListener>>(rid)
.map_err(|_| bad_resource("Listener has been closed"))?;
.map_err(|_| NetError::ListenerClosed)?;
let cancel_handle = RcRef::map(&resource, |r| &r.cancel);
let listener = RcRef::map(&resource, |r| &r.listener)
.try_borrow_mut()
.ok_or_else(|| custom_error("Busy", "Another accept task is ongoing"))?;
.ok_or_else(|| NetError::AcceptTaskOngoing)?;
let (tls_stream, remote_addr) =
match listener.accept().try_or_cancel(&cancel_handle).await {
Ok(tuple) => tuple,
Err(err) if err.kind() == ErrorKind::Interrupted => {
return Err(bad_resource("Listener has been closed"));
return Err(NetError::ListenerClosed);
}
Err(err) => return Err(err.into()),
};
@ -547,11 +552,11 @@ pub async fn op_net_accept_tls(
pub async fn op_tls_handshake(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
) -> Result<TlsHandshakeInfo, AnyError> {
) -> Result<TlsHandshakeInfo, NetError> {
let resource = state
.borrow()
.resource_table
.get::<TlsStreamResource>(rid)
.map_err(|_| bad_resource("Listener has been closed"))?;
resource.handshake().await
.map_err(|_| NetError::ListenerClosed)?;
resource.handshake().await.map_err(Into::into)
}

View file

@ -1,11 +1,9 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use crate::io::UnixStreamResource;
use crate::ops::NetError;
use crate::raw::NetworkListenerResource;
use crate::NetPermissions;
use deno_core::error::bad_resource;
use deno_core::error::custom_error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::AsyncRefCell;
use deno_core::CancelHandle;
@ -26,11 +24,8 @@ use tokio::net::UnixListener;
pub use tokio::net::UnixStream;
/// 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)
})
pub fn into_string(s: std::ffi::OsString) -> Result<String, NetError> {
s.into_string().map_err(NetError::InvalidUtf8)
}
pub struct UnixDatagramResource {
@ -63,15 +58,15 @@ pub struct UnixListenArgs {
pub async fn op_net_accept_unix(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
) -> Result<(ResourceId, Option<String>, Option<String>), AnyError> {
) -> Result<(ResourceId, Option<String>, Option<String>), NetError> {
let resource = state
.borrow()
.resource_table
.get::<NetworkListenerResource<UnixListener>>(rid)
.map_err(|_| bad_resource("Listener has been closed"))?;
.map_err(|_| NetError::ListenerClosed)?;
let listener = RcRef::map(&resource, |r| &r.listener)
.try_borrow_mut()
.ok_or_else(|| custom_error("Busy", "Listener already in use"))?;
.ok_or(NetError::ListenerBusy)?;
let cancel = RcRef::map(resource, |r| &r.cancel);
let (unix_stream, _socket_addr) = listener
.accept()
@ -95,7 +90,7 @@ pub async fn op_net_accept_unix(
pub async fn op_net_connect_unix<NP>(
state: Rc<RefCell<OpState>>,
#[string] address_path: String,
) -> Result<(ResourceId, Option<String>, Option<String>), AnyError>
) -> Result<(ResourceId, Option<String>, Option<String>), NetError>
where
NP: NetPermissions + 'static,
{
@ -103,10 +98,12 @@ where
let mut state_ = state.borrow_mut();
let address_path = state_
.borrow_mut::<NP>()
.check_read(&address_path, "Deno.connect()")?;
.check_read(&address_path, "Deno.connect()")
.map_err(NetError::Permission)?;
_ = state_
.borrow_mut::<NP>()
.check_write_path(&address_path, "Deno.connect()")?;
.check_write_path(&address_path, "Deno.connect()")
.map_err(NetError::Permission)?;
address_path
};
let unix_stream = UnixStream::connect(&address_path).await?;
@ -127,15 +124,15 @@ pub async fn op_net_recv_unixpacket(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
#[buffer] mut buf: JsBuffer,
) -> Result<(usize, Option<String>), AnyError> {
) -> Result<(usize, Option<String>), NetError> {
let resource = state
.borrow()
.resource_table
.get::<UnixDatagramResource>(rid)
.map_err(|_| bad_resource("Socket has been closed"))?;
.map_err(|_| NetError::SocketClosed)?;
let socket = RcRef::map(&resource, |r| &r.socket)
.try_borrow_mut()
.ok_or_else(|| custom_error("Busy", "Socket already in use"))?;
.ok_or(NetError::SocketBusy)?;
let cancel = RcRef::map(resource, |r| &r.cancel);
let (nread, remote_addr) =
socket.recv_from(&mut buf).try_or_cancel(cancel).await?;
@ -150,24 +147,25 @@ pub async fn op_net_send_unixpacket<NP>(
#[smi] rid: ResourceId,
#[string] address_path: String,
#[buffer] zero_copy: JsBuffer,
) -> Result<usize, AnyError>
) -> Result<usize, NetError>
where
NP: NetPermissions + 'static,
{
let address_path = {
let mut s = state.borrow_mut();
s.borrow_mut::<NP>()
.check_write(&address_path, "Deno.DatagramConn.send()")?
.check_write(&address_path, "Deno.DatagramConn.send()")
.map_err(NetError::Permission)?
};
let resource = state
.borrow()
.resource_table
.get::<UnixDatagramResource>(rid)
.map_err(|_| custom_error("NotConnected", "Socket has been closed"))?;
.map_err(|_| NetError::SocketClosedNotConnected)?;
let socket = RcRef::map(&resource, |r| &r.socket)
.try_borrow_mut()
.ok_or_else(|| custom_error("Busy", "Socket already in use"))?;
.ok_or(NetError::SocketBusy)?;
let nwritten = socket.send_to(&zero_copy, address_path).await?;
Ok(nwritten)
@ -179,14 +177,18 @@ pub fn op_net_listen_unix<NP>(
state: &mut OpState,
#[string] address_path: String,
#[string] api_name: String,
) -> Result<(ResourceId, Option<String>), AnyError>
) -> Result<(ResourceId, Option<String>), NetError>
where
NP: NetPermissions + 'static,
{
let permissions = state.borrow_mut::<NP>();
let api_call_expr = format!("{}()", api_name);
let address_path = permissions.check_read(&address_path, &api_call_expr)?;
_ = permissions.check_write_path(&address_path, &api_call_expr)?;
let address_path = permissions
.check_read(&address_path, &api_call_expr)
.map_err(NetError::Permission)?;
_ = permissions
.check_write_path(&address_path, &api_call_expr)
.map_err(NetError::Permission)?;
let listener = UnixListener::bind(address_path)?;
let local_addr = listener.local_addr()?;
let pathname = local_addr.as_pathname().map(pathstring).transpose()?;
@ -198,14 +200,17 @@ where
pub fn net_listen_unixpacket<NP>(
state: &mut OpState,
address_path: String,
) -> Result<(ResourceId, Option<String>), AnyError>
) -> Result<(ResourceId, Option<String>), NetError>
where
NP: NetPermissions + 'static,
{
let permissions = state.borrow_mut::<NP>();
let address_path =
permissions.check_read(&address_path, "Deno.listenDatagram()")?;
_ = permissions.check_write_path(&address_path, "Deno.listenDatagram()")?;
let address_path = permissions
.check_read(&address_path, "Deno.listenDatagram()")
.map_err(NetError::Permission)?;
_ = permissions
.check_write_path(&address_path, "Deno.listenDatagram()")
.map_err(NetError::Permission)?;
let socket = UnixDatagram::bind(address_path)?;
let local_addr = socket.local_addr()?;
let pathname = local_addr.as_pathname().map(pathstring).transpose()?;
@ -222,7 +227,7 @@ where
pub fn op_net_listen_unixpacket<NP>(
state: &mut OpState,
#[string] path: String,
) -> Result<(ResourceId, Option<String>), AnyError>
) -> Result<(ResourceId, Option<String>), NetError>
where
NP: NetPermissions + 'static,
{
@ -235,13 +240,13 @@ where
pub fn op_node_unstable_net_listen_unixpacket<NP>(
state: &mut OpState,
#[string] path: String,
) -> Result<(ResourceId, Option<String>), AnyError>
) -> Result<(ResourceId, Option<String>), NetError>
where
NP: NetPermissions + 'static,
{
net_listen_unixpacket::<NP>(state, path)
}
pub fn pathstring(pathname: &Path) -> Result<String, AnyError> {
pub fn pathstring(pathname: &Path) -> Result<String, NetError> {
into_string(pathname.into())
}

View file

@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use tokio::net::lookup_host;
@ -9,7 +8,7 @@ use tokio::net::lookup_host;
pub async fn resolve_addr(
hostname: &str,
port: u16,
) -> Result<impl Iterator<Item = SocketAddr> + '_, AnyError> {
) -> Result<impl Iterator<Item = SocketAddr> + '_, std::io::Error> {
let addr_port_pair = make_addr_port_pair(hostname, port);
let result = lookup_host(addr_port_pair).await?;
Ok(result)
@ -19,7 +18,7 @@ pub async fn resolve_addr(
pub fn resolve_addr_sync(
hostname: &str,
port: u16,
) -> Result<impl Iterator<Item = SocketAddr>, AnyError> {
) -> Result<impl Iterator<Item = SocketAddr>, std::io::Error> {
let addr_port_pair = make_addr_port_pair(hostname, port);
let result = addr_port_pair.to_socket_addrs()?;
Ok(result)

View file

@ -23,6 +23,7 @@ use deno_ffi::DlfcnError;
use deno_ffi::IRError;
use deno_ffi::ReprError;
use deno_ffi::StaticError;
use deno_net::ops::NetError;
use deno_tls::TlsError;
use deno_webstorage::WebStorageError;
use std::env;
@ -292,6 +293,48 @@ fn get_broadcast_channel_error(error: &BroadcastChannelError) -> &'static str {
}
}
fn get_net_error(error: &NetError) -> &'static str {
match error {
NetError::ListenerClosed => "BadResource",
NetError::ListenerBusy => "Busy",
NetError::SocketClosed => "BadResource",
NetError::SocketClosedNotConnected => "NotConnected",
NetError::SocketBusy => "Busy",
NetError::Io(e) => get_io_error_class(e),
NetError::AcceptTaskOngoing => "Busy",
NetError::RootCertStore(e)
| NetError::Permission(e)
| NetError::Resource(e) => get_error_class_name(e).unwrap_or("Error"),
NetError::NoResolvedAddress => "Error",
NetError::AddrParse(_) => "Error",
NetError::Map(e) => get_net_map_error(e),
NetError::Canceled(e) => {
let io_err: io::Error = e.to_owned().into();
get_io_error_class(&io_err)
}
NetError::DnsNotFound(_) => "NotFound",
NetError::DnsNotConnected(_) => "NotConnected",
NetError::DnsTimedOut(_) => "TimedOut",
NetError::Dns(_) => "Error",
NetError::UnsupportedRecordType => "NotSupported",
NetError::InvalidUtf8(_) => "InvalidData",
NetError::UnexpectedKeyType => "Error",
NetError::InvalidHostname(_) => "TypeError",
NetError::TcpStreamBusy => "Busy",
NetError::Rustls(_) => "Error",
NetError::Tls(e) => get_tls_error_class(e),
NetError::ListenTlsRequiresKey => "InvalidData",
NetError::Reunite(_) => "Error",
}
}
fn get_net_map_error(error: &deno_net::io::MapError) -> &'static str {
match error {
deno_net::io::MapError::Io(e) => get_io_error_class(e),
deno_net::io::MapError::NoResources => "Error",
}
}
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
deno_core::error::get_custom_error_class(e)
.or_else(|| deno_webgpu::error::get_error_class_name(e))
@ -316,6 +359,11 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
.or_else(|| e.downcast_ref::<CronError>().map(get_cron_error_class))
.or_else(|| e.downcast_ref::<CanvasError>().map(get_canvas_error))
.or_else(|| e.downcast_ref::<CacheError>().map(get_cache_error))
.or_else(|| e.downcast_ref::<NetError>().map(get_net_error))
.or_else(|| {
e.downcast_ref::<deno_net::io::MapError>()
.map(get_net_map_error)
})
.or_else(|| {
e.downcast_ref::<BroadcastChannelError>()
.map(get_broadcast_channel_error)