From 5f00367419b4cb4634d7fda5d41be0e6d50fb559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Tue, 4 Nov 2025 11:11:34 +0100 Subject: [PATCH 1/9] Switch `irpc` crate from thiserror to n0-error --- Cargo.lock | 36 ++++++- Cargo.toml | 3 +- src/lib.rs | 198 +++++++++++++++++++++++---------------- tests/common.rs | 15 ++- tests/mpsc_channel.rs | 46 ++++++--- tests/oneshot_channel.rs | 33 ++++--- 6 files changed, 218 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a90569..2bf8714 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1656,6 +1656,7 @@ dependencies = [ "futures-util", "iroh-quinn", "irpc-derive", + "n0-error", "n0-future", "postcard", "rcgen", @@ -1663,7 +1664,6 @@ dependencies = [ "serde", "smallvec", "testresult", - "thiserror 2.0.17", "thousands", "tokio", "tokio-util", @@ -1873,6 +1873,29 @@ dependencies = [ "uuid", ] +[[package]] +name = "n0-error" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a4839a11b62f1fdd75be912ee20634053c734c2240e867ded41c7f50822c549" +dependencies = [ + "derive_more 2.0.1", + "n0-error-macros", + "spez", +] + +[[package]] +name = "n0-error-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed2a7e5ca3cb5729d4a162d7bcab5b338bed299a2fee8457568d7e0a747ed89" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.107", +] + [[package]] name = "n0-future" version = "0.3.0" @@ -3008,6 +3031,17 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spez" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + [[package]] name = "spin" version = "0.9.8" diff --git a/Cargo.toml b/Cargo.toml index b26b7d8..be3e8d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ tokio = { workspace = true, features = ["sync", "macros"] } # for PollSender (which for some reason is not available in the main tokio api) tokio-util = { version = "0.7.14", default-features = false } # errors -thiserror = "2.0.12" +n0-error = { workspace = true } # used in the endpoint handler code when using rpc tracing = { workspace = true, optional = true } @@ -107,6 +107,7 @@ postcard = { version = "1.1.1", default-features = false } serde = { version = "1", default-features = false, features = ["derive"] } tracing = { version = "0.1.41", default-features = false } n0-future = { version = "0.3", default-features = false } +n0-error = { version = "0.1.0" } tracing-subscriber = { version = "0.3.20" } iroh = { version = "0.94" } iroh-base = { version = "0.94" } diff --git a/src/lib.rs b/src/lib.rs index 1da5f2c..6e7cdfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -295,6 +295,9 @@ use std::{fmt::Debug, future::Future, io, marker::PhantomData, ops::Deref, resul #[cfg(feature = "derive")] #[cfg_attr(quicrpc_docsrs, doc(cfg(feature = "derive")))] pub use irpc_derive::rpc_requests; +use n0_error::stack_error; +#[cfg(feature = "rpc")] +use n0_error::AnyError; use serde::{de::DeserializeOwned, Serialize}; use self::{ @@ -365,10 +368,13 @@ pub trait Channels: Send + 'static { pub mod channel { use std::io; + use n0_error::stack_error; + /// Oneshot channel, similar to tokio's oneshot channel pub mod oneshot { use std::{fmt::Debug, future::Future, io, pin::Pin, task}; + use n0_error::{e, stack_error}; use n0_future::future::Boxed as BoxFuture; use super::SendError; @@ -379,29 +385,32 @@ pub mod channel { /// /// For rpc communication, there can be any number of errors, so this is a /// generic io error. - #[derive(Debug, thiserror::Error)] + #[stack_error(derive, add_meta, from_sources)] pub enum RecvError { /// The sender has been closed. This is the only error that can occur /// for local communication. - #[error("sender closed")] + #[error("Sender closed")] SenderClosed, /// The message exceeded the maximum allowed message size (see [`MAX_MESSAGE_SIZE`]). /// /// [`MAX_MESSAGE_SIZE`]: crate::rpc::MAX_MESSAGE_SIZE - #[error("maximum message size exceeded")] + #[error("Maximum message size exceeded")] MaxMessageSizeExceeded, /// An io error occurred. This can occur for remote communication, /// due to a network error or deserialization error. - #[error("io error: {0}")] - Io(#[from] io::Error), + #[error("Io error")] + Io { + #[error(std_err)] + source: io::Error, + }, } impl From for io::Error { fn from(e: RecvError) -> Self { match e { - RecvError::Io(e) => e, - RecvError::SenderClosed => io::Error::new(io::ErrorKind::BrokenPipe, e), - RecvError::MaxMessageSizeExceeded => { + RecvError::Io { source, .. } => source, + RecvError::SenderClosed { .. } => io::Error::new(io::ErrorKind::BrokenPipe, e), + RecvError::MaxMessageSizeExceeded { .. } => { io::Error::new(io::ErrorKind::InvalidData, e) } } @@ -491,7 +500,7 @@ pub mod channel { /// Local senders will never yield, but can fail if the receiver has been closed. pub async fn send(self, value: T) -> std::result::Result<(), SendError> { match self { - Sender::Tokio(tx) => tx.send(value).map_err(|_| SendError::ReceiverClosed), + Sender::Tokio(tx) => tx.send(value).map_err(|_| e!(SendError::ReceiverClosed)), Sender::Boxed(f) => f(value).await, } } @@ -564,7 +573,9 @@ pub mod channel { fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> task::Poll { match self.get_mut() { - Self::Tokio(rx) => Pin::new(rx).poll(cx).map_err(|_| RecvError::SenderClosed), + Self::Tokio(rx) => Pin::new(rx) + .poll(cx) + .map_err(|_| e!(RecvError::SenderClosed)), Self::Boxed(rx) => Pin::new(rx).poll(cx), } } @@ -618,6 +629,8 @@ pub mod channel { pub mod mpsc { use std::{fmt::Debug, future::Future, io, marker::PhantomData, pin::Pin, sync::Arc}; + use n0_error::{e, stack_error}; + use super::SendError; /// Error when receiving a oneshot or mpsc message. For local communication, @@ -625,24 +638,27 @@ pub mod channel { /// /// For rpc communication, there can be any number of errors, so this is a /// generic io error. - #[derive(Debug, thiserror::Error)] + #[stack_error(derive, add_meta, from_sources)] pub enum RecvError { /// The message exceeded the maximum allowed message size (see [`MAX_MESSAGE_SIZE`]). /// /// [`MAX_MESSAGE_SIZE`]: crate::rpc::MAX_MESSAGE_SIZE - #[error("maximum message size exceeded")] + #[error("Maximum message size exceeded")] MaxMessageSizeExceeded, /// An io error occurred. This can occur for remote communication, /// due to a network error or deserialization error. - #[error("io error: {0}")] - Io(#[from] io::Error), + #[error("Io error")] + Io { + #[error(std_err)] + source: io::Error, + }, } impl From for io::Error { fn from(e: RecvError) -> Self { match e { - RecvError::Io(e) => e, - RecvError::MaxMessageSizeExceeded => { + RecvError::Io { source, .. } => source, + RecvError::MaxMessageSizeExceeded { .. } => { io::Error::new(io::ErrorKind::InvalidData, e) } } @@ -832,9 +848,10 @@ pub mod channel { /// future until completion if you want to reuse the sender or any clone afterwards. pub async fn send(&self, value: T) -> std::result::Result<(), SendError> { match self { - Sender::Tokio(tx) => { - tx.send(value).await.map_err(|_| SendError::ReceiverClosed) - } + Sender::Tokio(tx) => tx + .send(value) + .await + .map_err(|_| e!(SendError::ReceiverClosed)), Sender::Boxed(sink) => sink.send(value).await, } } @@ -865,7 +882,7 @@ pub mod channel { Sender::Tokio(tx) => match tx.try_send(value) { Ok(()) => Ok(true), Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => { - Err(SendError::ReceiverClosed) + Err(e!(SendError::ReceiverClosed)) } Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => Ok(false), }, @@ -1100,29 +1117,34 @@ pub mod channel { /// /// For rpc communication, there can be any number of errors, so this is a /// generic io error. - #[derive(Debug, thiserror::Error)] + #[stack_error(derive, add_meta, from_sources)] pub enum SendError { /// The receiver has been closed. This is the only error that can occur /// for local communication. - #[error("receiver closed")] + #[error("Receiver closed")] ReceiverClosed, /// The message exceeded the maximum allowed message size (see [`MAX_MESSAGE_SIZE`]). /// /// [`MAX_MESSAGE_SIZE`]: crate::rpc::MAX_MESSAGE_SIZE - #[error("maximum message size exceeded")] + #[error("Maximum message size exceeded")] MaxMessageSizeExceeded, /// The underlying io error. This can occur for remote communication, /// due to a network error or serialization error. - #[error("io error: {0}")] - Io(#[from] io::Error), + #[error("Io error")] + Io { + #[error(std_err)] + source: io::Error, + }, } impl From for io::Error { fn from(e: SendError) -> Self { match e { - SendError::ReceiverClosed => io::Error::new(io::ErrorKind::BrokenPipe, e), - SendError::MaxMessageSizeExceeded => io::Error::new(io::ErrorKind::InvalidData, e), - SendError::Io(e) => e, + SendError::ReceiverClosed { .. } => io::Error::new(io::ErrorKind::BrokenPipe, e), + SendError::MaxMessageSizeExceeded { .. } => { + io::Error::new(io::ErrorKind::InvalidData, e) + } + SendError::Io { source, .. } => source, } } } @@ -1683,23 +1705,29 @@ impl Clone for ClientInner { /// Error when opening a request. When cross-process rpc is disabled, this is /// an empty enum since local requests can not fail. -#[derive(Debug, thiserror::Error)] +#[stack_error(derive, add_meta, from_sources)] pub enum RequestError { /// Error in quinn during connect #[cfg(feature = "rpc")] #[cfg_attr(quicrpc_docsrs, doc(cfg(feature = "rpc")))] - #[error("error establishing connection: {0}")] - Connect(#[from] quinn::ConnectError), + #[error("Error establishing connection")] + Connect { + #[error(std_err)] + source: quinn::ConnectError, + }, /// Error in quinn when the connection already exists, when opening a stream pair #[cfg(feature = "rpc")] #[cfg_attr(quicrpc_docsrs, doc(cfg(feature = "rpc")))] - #[error("error opening stream: {0}")] - Connection(#[from] quinn::ConnectionError), + #[error("Error opening stream")] + Connection { + #[error(std_err)] + source: quinn::ConnectionError, + }, /// Generic error for non-quinn transports #[cfg(feature = "rpc")] #[cfg_attr(quicrpc_docsrs, doc(cfg(feature = "rpc")))] - #[error("error opening stream: {0}")] - Other(#[from] anyhow::Error), + #[error("Error opening stream")] + Other { source: AnyError }, #[cfg(not(feature = "rpc"))] #[error("(Without the rpc feature, requests cannot fail")] @@ -1707,19 +1735,19 @@ pub enum RequestError { } /// Error type that subsumes all possible errors in this crate, for convenience. -#[derive(Debug, thiserror::Error)] +#[stack_error(derive, add_meta, from_sources)] pub enum Error { - #[error("request error: {0}")] - Request(#[from] RequestError), - #[error("send error: {0}")] - Send(#[from] channel::SendError), - #[error("mpsc recv error: {0}")] - MpscRecv(#[from] channel::mpsc::RecvError), - #[error("oneshot recv error: {0}")] - OneshotRecv(#[from] channel::oneshot::RecvError), + #[error("Request error")] + Request { source: RequestError }, + #[error("Send error")] + Send { source: channel::SendError }, + #[error("Mpsc recv error")] + MpscRecv { source: channel::mpsc::RecvError }, + #[error("Oneshot recv error")] + OneshotRecv { source: channel::oneshot::RecvError }, #[cfg(feature = "rpc")] - #[error("recv error: {0}")] - Write(#[from] rpc::WriteError), + #[error("Recv error")] + Write { source: rpc::WriteError }, } /// Type alias for a result with an irpc error type. @@ -1728,12 +1756,12 @@ pub type Result = std::result::Result; impl From for io::Error { fn from(e: Error) -> Self { match e { - Error::Request(e) => e.into(), - Error::Send(e) => e.into(), - Error::MpscRecv(e) => e.into(), - Error::OneshotRecv(e) => e.into(), + Error::Request { source, .. } => source.into(), + Error::Send { source, .. } => source.into(), + Error::MpscRecv { source, .. } => source.into(), + Error::OneshotRecv { source, .. } => source.into(), #[cfg(feature = "rpc")] - Error::Write(e) => e.into(), + Error::Write { source, .. } => source.into(), } } } @@ -1742,11 +1770,11 @@ impl From for io::Error { fn from(e: RequestError) -> Self { match e { #[cfg(feature = "rpc")] - RequestError::Connect(e) => io::Error::other(e), + RequestError::Connect { source, .. } => io::Error::other(source), #[cfg(feature = "rpc")] - RequestError::Connection(e) => e.into(), + RequestError::Connection { source, .. } => source.into(), #[cfg(feature = "rpc")] - RequestError::Other(e) => io::Error::other(e), + RequestError::Other { source, .. } => io::Error::other(source), #[cfg(not(feature = "rpc"))] RequestError::Unreachable => unreachable!(), } @@ -1793,6 +1821,7 @@ pub mod rpc { fmt::Debug, future::Future, io, marker::PhantomData, ops::DerefMut, pin::Pin, sync::Arc, }; + use n0_error::{e, stack_error}; use n0_future::{future::Boxed as BoxFuture, task::JoinSet}; /// This is used by irpc-derive to refer to quinn types (SendStream and RecvStream) /// to make generated code work for users without having to depend on quinn directly @@ -1825,38 +1854,46 @@ pub mod rpc { /// Error that can occur when writing the initial message when doing a /// cross-process RPC. - #[derive(Debug, thiserror::Error)] + #[stack_error(derive, add_meta, from_sources)] pub enum WriteError { /// Error writing to the stream with quinn - #[error("error writing to stream: {0}")] - Quinn(#[from] quinn::WriteError), + #[error("Error writing to stream")] + Quinn { + #[error(std_err)] + source: quinn::WriteError, + }, /// The message exceeded the maximum allowed message size (see [`MAX_MESSAGE_SIZE`]). - #[error("maximum message size exceeded")] + #[error("Maximum message size exceeded")] MaxMessageSizeExceeded, /// Generic IO error, e.g. when serializing the message or when using /// other transports. - #[error("error serializing: {0}")] - Io(#[from] io::Error), + #[error("Error serializing")] + Io { + #[error(std_err)] + source: io::Error, + }, } impl From for WriteError { fn from(value: postcard::Error) -> Self { - Self::Io(io::Error::new(io::ErrorKind::InvalidData, value)) + e!(Self::Io, io::Error::new(io::ErrorKind::InvalidData, value)) } } impl From for SendError { fn from(value: postcard::Error) -> Self { - Self::Io(io::Error::new(io::ErrorKind::InvalidData, value)) + e!(Self::Io, io::Error::new(io::ErrorKind::InvalidData, value)) } } impl From for io::Error { fn from(e: WriteError) -> Self { match e { - WriteError::Io(e) => e, - WriteError::MaxMessageSizeExceeded => io::Error::new(io::ErrorKind::InvalidData, e), - WriteError::Quinn(e) => e.into(), + WriteError::Io { source, .. } => source, + WriteError::MaxMessageSizeExceeded { .. } => { + io::Error::new(io::ErrorKind::InvalidData, e) + } + WriteError::Quinn { source, .. } => source.into(), } } } @@ -1867,9 +1904,9 @@ pub mod rpc { quinn::WriteError::Stopped(code) if code == ERROR_CODE_MAX_MESSAGE_SIZE_EXCEEDED.into() => { - SendError::MaxMessageSizeExceeded + e!(SendError::MaxMessageSizeExceeded) } - _ => SendError::Io(io::Error::from(err)), + _ => e!(SendError::Io, io::Error::from(err)), } } } @@ -1990,7 +2027,7 @@ pub mod rpc { ) -> std::result::Result, WriteError> { let msg = msg.into(); if postcard::experimental::serialized_size(&msg)? as u64 > MAX_MESSAGE_SIZE { - return Err(WriteError::MaxMessageSizeExceeded); + return Err(e!(WriteError::MaxMessageSizeExceeded)); } let mut buf = SmallVec::<[u8; 128]>::new(); buf.write_length_prefixed(&msg)?; @@ -2029,7 +2066,7 @@ pub mod rpc { ))?; if size > MAX_MESSAGE_SIZE { read.stop(ERROR_CODE_MAX_MESSAGE_SIZE_EXCEEDED.into()).ok(); - return Err(oneshot::RecvError::MaxMessageSizeExceeded); + return Err(e!(oneshot::RecvError::MaxMessageSizeExceeded)); } let rest = read .read_to_end(size as usize) @@ -2074,17 +2111,17 @@ pub mod rpc { Ok(size) => size, Err(e) => { writer.reset(ERROR_CODE_INVALID_POSTCARD.into()).ok(); - return Err(SendError::Io(io::Error::new( - io::ErrorKind::InvalidData, - e, - ))); + return Err(e!( + SendError::Io, + io::Error::new(io::ErrorKind::InvalidData, e,) + )); } }; if size as u64 > MAX_MESSAGE_SIZE { writer .reset(ERROR_CODE_MAX_MESSAGE_SIZE_EXCEEDED.into()) .ok(); - return Err(SendError::MaxMessageSizeExceeded); + return Err(e!(SendError::MaxMessageSizeExceeded)); } // write via a small buffer to avoid allocation for small values let mut buf = SmallVec::<[u8; 128]>::new(); @@ -2142,7 +2179,7 @@ pub mod rpc { self.recv .stop(ERROR_CODE_MAX_MESSAGE_SIZE_EXCEEDED.into()) .ok(); - return Err(mpsc::RecvError::MaxMessageSizeExceeded); + return Err(e!(mpsc::RecvError::MaxMessageSizeExceeded)); } let mut buf = vec![0; size as usize]; read.read_exact(&mut buf) @@ -2175,14 +2212,17 @@ pub mod rpc { Ok(size) => size, Err(e) => { self.send.reset(ERROR_CODE_INVALID_POSTCARD.into()).ok(); - return Err(SendError::Io(io::Error::new(io::ErrorKind::InvalidData, e))); + return Err(e!( + SendError::Io, + io::Error::new(io::ErrorKind::InvalidData, e) + )); } }; if size as u64 > MAX_MESSAGE_SIZE { self.send .reset(ERROR_CODE_MAX_MESSAGE_SIZE_EXCEEDED.into()) .ok(); - return Err(SendError::MaxMessageSizeExceeded); + return Err(e!(SendError::MaxMessageSizeExceeded)); } let value = value; self.buffer.clear(); @@ -2202,7 +2242,7 @@ pub mod rpc { ) -> Pin> + Send + Sync + '_>> { Box::pin(async { if postcard::experimental::serialized_size(&value)? as u64 > MAX_MESSAGE_SIZE { - return Err(SendError::MaxMessageSizeExceeded); + return Err(e!(SendError::MaxMessageSizeExceeded)); } // todo: move the non-async part out of the box. Will require a new return type. let value = value; @@ -2430,7 +2470,7 @@ pub mod rpc { ERROR_CODE_MAX_MESSAGE_SIZE_EXCEEDED.into(), b"request exceeded max message size", ); - return Err(mpsc::RecvError::MaxMessageSizeExceeded.into()); + return Err(e!(mpsc::RecvError::MaxMessageSizeExceeded).into()); } let mut buf = vec![0; size as usize]; recv.read_exact(&mut buf) diff --git a/tests/common.rs b/tests/common.rs index bdc3021..932c786 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -3,6 +3,7 @@ use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use irpc::util::{make_client_endpoint, make_server_endpoint}; +use n0_error::{e, stack_error}; use quinn::Endpoint; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use testresult::TestResult; @@ -19,9 +20,11 @@ pub fn create_connected_endpoints() -> TestResult<(Endpoint, Endpoint, SocketAdd #[derive(Debug)] pub struct NoSer(pub u64); -#[derive(Debug, thiserror::Error)] -#[error("Cannot serialize odd number: {0}")] -pub struct OddNumberError(u64); +#[stack_error(derive, add_meta)] +#[error("Cannot serialize odd number")] +pub struct OddNumberError { + value: u64, +} impl Serialize for NoSer { fn serialize(&self, serializer: S) -> Result @@ -29,7 +32,9 @@ impl Serialize for NoSer { S: Serializer, { if self.0 % 2 == 1 { - Err(serde::ser::Error::custom(OddNumberError(self.0))) + Err(serde::ser::Error::custom(e!(OddNumberError { + value: self.0 + }))) } else { serializer.serialize_u64(self.0) } @@ -43,7 +48,7 @@ impl<'de> Deserialize<'de> for NoSer { { let value = u64::deserialize(deserializer)?; if value % 2 != 0 { - Err(serde::de::Error::custom(OddNumberError(value))) + Err(serde::de::Error::custom(e!(OddNumberError { value }))) } else { Ok(NoSer(value)) } diff --git a/tests/mpsc_channel.rs b/tests/mpsc_channel.rs index 4717fd1..6d1e83f 100644 --- a/tests/mpsc_channel.rs +++ b/tests/mpsc_channel.rs @@ -12,6 +12,7 @@ use irpc::{ }, util::AsyncWriteVarintExt, }; +use n0_error::e; use quinn::Endpoint; use testresult::TestResult; use tokio::time::timeout; @@ -44,7 +45,9 @@ async fn mpsc_sender_clone_closed_error() -> TestResult<()> { // this should fail with an io error, since the stream was stopped loop { match send3.send(vec![1, 2, 3]).await { - Err(SendError::Io(e)) if e.kind() == ErrorKind::BrokenPipe => break, + Err(SendError::Io { source, .. }) if source.kind() == ErrorKind::BrokenPipe => { + break + } _ => {} }; } @@ -52,7 +55,7 @@ async fn mpsc_sender_clone_closed_error() -> TestResult<()> { // send until we get an error because the remote side stopped the stream while send1.send(vec![1, 2, 3]).await.is_ok() {} match send1.send(vec![4, 5, 6]).await { - Err(SendError::Io(e)) if e.kind() == ErrorKind::BrokenPipe => {} + Err(SendError::Io { source, .. }) if source.kind() == ErrorKind::BrokenPipe => {} e => panic!("Expected SendError::Io with kind BrokenPipe, got {e:?}"), }; // check that closed signal was received by the second sender @@ -90,7 +93,9 @@ async fn mpsc_sender_clone_drop_error() -> TestResult<()> { // this should fail with an io error, since the stream was stopped loop { match send3.send(vec![1, 2, 3]).await { - Err(SendError::Io(e)) if e.kind() == ErrorKind::BrokenPipe => break, + Err(SendError::Io { source, .. }) if source.kind() == ErrorKind::BrokenPipe => { + break + } _ => {} }; } @@ -120,14 +125,14 @@ async fn vec_receiver(server: Endpoint) -> Result<(), RecvError> { .await .unwrap() .await - .map_err(|e| RecvError::Io(e.into()))?; + .map_err(|err| e!(RecvError::Io, err.into()))?; let (_, recv) = conn .accept_bi() .await - .map_err(|e| RecvError::Io(e.into()))?; + .map_err(|err| e!(RecvError::Io, err.into()))?; let mut recv = Receiver::>::from(recv); while recv.recv().await?.is_some() {} - Err(RecvError::Io(io::ErrorKind::UnexpectedEof.into())) + Err(e!(RecvError::Io, io::ErrorKind::UnexpectedEof.into())) } /// Checks that the max message size is enforced on the sender side and that errors are propagated to the receiver side. @@ -144,11 +149,13 @@ async fn mpsc_max_message_size_send() -> TestResult<()> { let Err(cause) = send.send(vec![0u8; 1024 * 1024 * 32]).await else { panic!("client should have failed due to max message size"); }; - assert!(matches!(cause, SendError::MaxMessageSizeExceeded)); + assert!(matches!(cause, SendError::MaxMessageSizeExceeded { .. })); let Err(cause) = server.await? else { panic!("server should have failed due to max message size"); }; - assert!(matches!(cause, mpsc::RecvError::Io(e) if e.kind() == ErrorKind::ConnectionReset)); + assert!( + matches!(cause, mpsc::RecvError::Io { source, .. } if source.kind() == ErrorKind::ConnectionReset) + ); Ok(()) } @@ -168,7 +175,10 @@ async fn mpsc_max_message_size_recv() -> TestResult<()> { let Err(cause) = server.await? else { panic!("server should have failed due to max message size"); }; - assert!(matches!(cause, mpsc::RecvError::MaxMessageSizeExceeded)); + assert!(matches!( + cause, + mpsc::RecvError::MaxMessageSizeExceeded { .. } + )); Ok(()) } @@ -178,14 +188,14 @@ async fn noser_receiver(server: Endpoint) -> Result<(), mpsc::RecvError> { .await .unwrap() .await - .map_err(|e| mpsc::RecvError::Io(e.into()))?; + .map_err(|err| e!(mpsc::RecvError::Io, err.into()))?; let (_, recv) = conn .accept_bi() .await - .map_err(|e| mpsc::RecvError::Io(e.into()))?; + .map_err(|err| e!(mpsc::RecvError::Io, err.into()))?; let mut recv = mpsc::Receiver::::from(recv); while recv.recv().await?.is_some() {} - Err(mpsc::RecvError::Io(io::ErrorKind::UnexpectedEof.into())) + Err(e!(mpsc::RecvError::Io, io::ErrorKind::UnexpectedEof.into())) } /// Checks that a serialization error is caught and propagated to the receiver. @@ -202,11 +212,15 @@ async fn mpsc_serialize_error_send() -> TestResult<()> { let Err(cause) = send.send(NoSer(1)).await else { panic!("client should have failed due to serialization error"); }; - assert!(matches!(cause, SendError::Io(e) if e.kind() == ErrorKind::InvalidData)); + assert!( + matches!(cause, SendError::Io { source, .. } if source.kind() == ErrorKind::InvalidData) + ); let Err(cause) = server.await? else { panic!("server should have failed due to serialization error"); }; - assert!(matches!(cause, mpsc::RecvError::Io(e) if e.kind() == ErrorKind::ConnectionReset)); + assert!( + matches!(cause, mpsc::RecvError::Io { source, .. } if source.kind() == ErrorKind::ConnectionReset) + ); Ok(()) } @@ -223,6 +237,8 @@ async fn mpsc_serialize_error_recv() -> TestResult<()> { let Err(cause) = server.await? else { panic!("server should have failed due to serialization error"); }; - assert!(matches!(cause, mpsc::RecvError::Io(e) if e.kind() == ErrorKind::InvalidData)); + assert!( + matches!(cause, mpsc::RecvError::Io { source, .. } if source.kind() == ErrorKind::InvalidData) + ); Ok(()) } diff --git a/tests/oneshot_channel.rs b/tests/oneshot_channel.rs index 72202e9..d63ad0c 100644 --- a/tests/oneshot_channel.rs +++ b/tests/oneshot_channel.rs @@ -9,6 +9,7 @@ use irpc::{ }, util::AsyncWriteVarintExt, }; +use n0_error::e; use quinn::Endpoint; use testresult::TestResult; @@ -21,14 +22,14 @@ async fn vec_receiver(server: Endpoint) -> Result<(), RecvError> { .await .unwrap() .await - .map_err(|e| RecvError::Io(e.into()))?; + .map_err(|err| e!(RecvError::Io, err.into()))?; let (_, recv) = conn .accept_bi() .await - .map_err(|e| RecvError::Io(e.into()))?; + .map_err(|err| e!(RecvError::Io, err.into()))?; let recv = oneshot::Receiver::>::from(recv); recv.await?; - Err(RecvError::Io(io::ErrorKind::UnexpectedEof.into())) + Err(e!(RecvError::Io, io::ErrorKind::UnexpectedEof.into())) } /// Checks that the max message size is enforced on the sender side and that errors are propagated to the receiver side. @@ -43,11 +44,13 @@ async fn oneshot_max_message_size_send() -> TestResult<()> { let Err(cause) = send.send(vec![0u8; 1024 * 1024 * 32]).await else { panic!("client should have failed due to max message size"); }; - assert!(matches!(cause, SendError::MaxMessageSizeExceeded)); + assert!(matches!(cause, SendError::MaxMessageSizeExceeded { .. })); let Err(cause) = server.await? else { panic!("server should have failed due to max message size"); }; - assert!(matches!(cause, RecvError::Io(e) if e.kind() == ErrorKind::ConnectionReset)); + assert!( + matches!(cause, RecvError::Io { source, .. } if source.kind() == ErrorKind::ConnectionReset) + ); Ok(()) } @@ -65,7 +68,7 @@ async fn oneshot_max_message_size_recv() -> TestResult<()> { let Err(cause) = server.await? else { panic!("server should have failed due to max message size"); }; - assert!(matches!(cause, RecvError::MaxMessageSizeExceeded)); + assert!(matches!(cause, RecvError::MaxMessageSizeExceeded { .. })); Ok(()) } @@ -75,14 +78,14 @@ async fn noser_receiver(server: Endpoint) -> Result<(), RecvError> { .await .unwrap() .await - .map_err(|e| RecvError::Io(e.into()))?; + .map_err(|err| e!(RecvError::Io, err.into()))?; let (_, recv) = conn .accept_bi() .await - .map_err(|e| RecvError::Io(e.into()))?; + .map_err(|err| e!(RecvError::Io, err.into()))?; let recv = oneshot::Receiver::::from(recv); recv.await?; - Err(RecvError::Io(io::ErrorKind::UnexpectedEof.into())) + Err(e!(RecvError::Io, io::ErrorKind::UnexpectedEof.into())) } /// Checks that trying to send a message that cannot be serialized results in an error on the sender side and a connection reset on the receiver side. @@ -97,12 +100,16 @@ async fn oneshot_serialize_error_send() -> TestResult<()> { let Err(cause) = send.send(NoSer(1)).await else { panic!("client should have failed due to serialization error"); }; - assert!(matches!(cause, SendError::Io(e) if e.kind() == ErrorKind::InvalidData)); + assert!( + matches!(cause, SendError::Io { source, .. } if source.kind() == ErrorKind::InvalidData) + ); let Err(cause) = server.await? else { panic!("server should have failed due to serialization error"); }; println!("Server error: {cause:?}"); - assert!(matches!(cause, RecvError::Io(e) if e.kind() == ErrorKind::ConnectionReset)); + assert!( + matches!(cause, RecvError::Io { source, .. } if source.kind() == ErrorKind::ConnectionReset) + ); Ok(()) } @@ -119,6 +126,8 @@ async fn oneshot_serialize_error_recv() -> TestResult<()> { panic!("server should have failed due to serialization error"); }; println!("Server error: {cause:?}"); - assert!(matches!(cause, RecvError::Io(e) if e.kind() == ErrorKind::InvalidData)); + assert!( + matches!(cause, RecvError::Io { source, .. } if source.kind() == ErrorKind::InvalidData) + ); Ok(()) } From 647f67466cd183d793b297de7805d0c98cbf1c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Tue, 4 Nov 2025 11:28:50 +0100 Subject: [PATCH 2/9] Convert all examples to use `n0-error` and remove `anyhow` dependency --- Cargo.lock | 70 ++++++++++++++++++------------------ Cargo.toml | 10 +++--- examples/compute.rs | 43 +++++++++++----------- examples/derive.rs | 4 +-- examples/storage.rs | 20 +++++------ irpc-iroh/Cargo.toml | 2 +- irpc-iroh/examples/0rtt.rs | 18 +++++----- irpc-iroh/examples/auth.rs | 8 ++--- irpc-iroh/examples/derive.rs | 6 ++-- irpc-iroh/examples/simple.rs | 8 ++--- irpc-iroh/src/lib.rs | 17 ++++----- src/lib.rs | 4 +-- src/util.rs | 18 ++++++---- 13 files changed, 116 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bf8714..cd1c89c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1442,8 +1442,7 @@ dependencies = [ [[package]] name = "iroh" version = "0.94.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9428cef1eafd2eac584269986d1949e693877ac12065b401dfde69f664b07ac" +source = "git+https://github.com/n0-computer/iroh.git?branch=main#30c23e8dbaa02d17ab57ba41f0aa5271b0a411dc" dependencies = [ "aead", "backon", @@ -1459,16 +1458,15 @@ dependencies = [ "http", "igd-next", "instant", - "iroh-base", + "iroh-base 0.94.1", "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", "iroh-quinn-udp", "iroh-relay", + "n0-error", "n0-future", - "n0-snafu", "n0-watcher", - "nested_enum_utils", "netdev", "netwatch", "pin-project", @@ -1483,7 +1481,6 @@ dependencies = [ "rustls-webpki", "serde", "smallvec", - "snafu", "strum", "time", "tokio", @@ -1501,16 +1498,27 @@ name = "iroh-base" version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db942f6f3d6fa9b475690c6e8e6684d60591dd886bf1bdfef4c60d89d502215c" +dependencies = [ + "derive_more 2.0.1", + "n0-snafu", + "nested_enum_utils", + "serde", + "snafu", + "url", +] + +[[package]] +name = "iroh-base" +version = "0.94.1" +source = "git+https://github.com/n0-computer/iroh.git?branch=main#30c23e8dbaa02d17ab57ba41f0aa5271b0a411dc" dependencies = [ "curve25519-dalek", "data-encoding", "derive_more 2.0.1", "ed25519-dalek", - "n0-snafu", - "nested_enum_utils", + "n0-error", "rand_core 0.9.3", "serde", - "snafu", "url", "zeroize", "zeroize_derive", @@ -1518,24 +1526,24 @@ dependencies = [ [[package]] name = "iroh-metrics" -version = "0.36.2" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84c167b59ae22f940e78eb347ca5f02aa25608e994cb5a7cc016ac2d5eada18" +checksum = "79e3381da7c93c12d353230c74bba26131d1c8bf3a4d8af0fec041546454582e" dependencies = [ "iroh-metrics-derive", "itoa", + "n0-error", "postcard", "ryu", "serde", - "snafu", "tracing", ] [[package]] name = "iroh-metrics-derive" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "748d380f26f7c25307c0a7acd181b84b977ddc2a1b7beece1e5998623c323aa1" +checksum = "d4e12bd0763fd16062f5cc5e8db15dd52d26e75a8af4c7fb57ccee3589b344b8" dependencies = [ "heck", "proc-macro2", @@ -1600,8 +1608,7 @@ dependencies = [ [[package]] name = "iroh-relay" version = "0.94.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360e201ab1803201de9a125dd838f7a4d13e6ba3a79aeb46c7fbf023266c062e" +source = "git+https://github.com/n0-computer/iroh.git?branch=main#30c23e8dbaa02d17ab57ba41f0aa5271b0a411dc" dependencies = [ "blake3", "bytes", @@ -1614,14 +1621,13 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "iroh-base", + "iroh-base 0.94.1", "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", "lru 0.16.2", + "n0-error", "n0-future", - "n0-snafu", - "nested_enum_utils", "num_enum", "pin-project", "pkarr", @@ -1633,7 +1639,6 @@ dependencies = [ "serde", "serde_bytes", "sha1", - "snafu", "strum", "tokio", "tokio-rustls", @@ -1650,7 +1655,6 @@ dependencies = [ name = "irpc" version = "0.10.0" dependencies = [ - "anyhow", "derive_more 2.0.1", "futures-buffered", "futures-util", @@ -1685,15 +1689,15 @@ dependencies = [ name = "irpc-iroh" version = "0.10.0" dependencies = [ - "anyhow", "clap", "futures-util", "getrandom 0.3.4", "hex", "iroh", - "iroh-base", + "iroh-base 0.94.0", "irpc", "irpc-derive", + "n0-error", "n0-future", "postcard", "rand 0.9.2", @@ -1932,13 +1936,13 @@ dependencies = [ [[package]] name = "n0-watcher" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34c65e127e06e5a2781b28df6a33ea474a7bddc0ac0cfea888bd20c79a1b6516" +checksum = "38acf13c1ddafc60eb7316d52213467f8ccb70b6f02b65e7d97f7799b1f50be4" dependencies = [ "derive_more 2.0.1", + "n0-error", "n0-future", - "snafu", ] [[package]] @@ -2020,9 +2024,9 @@ dependencies = [ [[package]] name = "netwatch" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d7ec7abdbfe67ee70af3f2002326491178419caea22254b9070e6ff0c83491" +checksum = "26f2acd376ef48b6c326abf3ba23c449e0cb8aa5c2511d189dd8a8a3bfac889b" dependencies = [ "atomic-waker", "bytes", @@ -2031,9 +2035,9 @@ dependencies = [ "iroh-quinn-udp", "js-sys", "libc", + "n0-error", "n0-future", "n0-watcher", - "nested_enum_utils", "netdev", "netlink-packet-core", "netlink-packet-route", @@ -2041,7 +2045,6 @@ dependencies = [ "netlink-sys", "pin-project-lite", "serde", - "snafu", "socket2 0.6.1", "time", "tokio", @@ -2306,9 +2309,9 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portmapper" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d73aa9bd141e0ff6060fea89a5437883f3b9ceea1cda71c790b90e17d072a3b3" +checksum = "7b575f975dcf03e258b0c7ab3f81497d7124f508884c37da66a7314aa2a8d467" dependencies = [ "base64", "bytes", @@ -2319,13 +2322,12 @@ dependencies = [ "igd-next", "iroh-metrics", "libc", - "nested_enum_utils", + "n0-error", "netwatch", "num_enum", "rand 0.9.2", "serde", "smallvec", - "snafu", "socket2 0.6.1", "time", "tokio", diff --git a/Cargo.toml b/Cargo.toml index be3e8d5..d9d5cb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,8 +37,6 @@ smallvec = { version = "1.14.0", features = ["write"], optional = true } rustls = { version = "0.23.5", default-features = false, features = ["std"], optional = true } # used in the test utils to generate quinn endpoints rcgen = { version = "0.14.5", optional = true } -# used in the test utils to generate quinn endpoints -anyhow = { workspace = true, optional = true } # used in the benches futures-buffered ={ version = "0.2.9", optional = true } # for AbortOnDropHandle @@ -64,9 +62,9 @@ testresult = "0.4.1" [features] # enable the remote transport -rpc = ["dep:quinn", "dep:postcard", "dep:anyhow", "dep:smallvec", "dep:tracing", "tokio/io-util"] +rpc = ["dep:quinn", "dep:postcard", "dep:smallvec", "dep:tracing", "tokio/io-util"] # add test utilities -quinn_endpoint_setup = ["rpc", "dep:rustls", "dep:rcgen", "dep:anyhow", "dep:futures-buffered", "quinn/rustls-ring"] +quinn_endpoint_setup = ["rpc", "dep:rustls", "dep:rcgen", "dep:futures-buffered", "quinn/rustls-ring"] # pick up parent span when creating channel messages spans = ["dep:tracing"] stream = ["dep:futures-util"] @@ -101,7 +99,6 @@ rustdoc-args = ["--cfg", "quicrpc_docsrs"] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(quicrpc_docsrs)"] } [workspace.dependencies] -anyhow = { version = "1.0.98" } tokio = { version = "1.44", default-features = false } postcard = { version = "1.1.1", default-features = false } serde = { version = "1", default-features = false, features = ["derive"] } @@ -113,3 +110,6 @@ iroh = { version = "0.94" } iroh-base = { version = "0.94" } quinn = { package = "iroh-quinn", version = "0.14.0", default-features = false } futures-util = { version = "0.3", features = ["sink"] } + +[patch.crates-io] +iroh = { git = "https://github.com/n0-computer/iroh.git", branch = "main" } diff --git a/examples/compute.rs b/examples/compute.rs index 7908425..b64ab3f 100644 --- a/examples/compute.rs +++ b/examples/compute.rs @@ -3,7 +3,6 @@ use std::{ net::{Ipv4Addr, SocketAddr, SocketAddrV4}, }; -use anyhow::bail; use futures_buffered::BufferedStreamExt; use irpc::{ channel::{mpsc, oneshot}, @@ -12,6 +11,7 @@ use irpc::{ util::{make_client_endpoint, make_server_endpoint}, Client, Request, WithChannels, }; +use n0_error::{bail_any, Result, StdResultExt}; use n0_future::{ stream::StreamExt, task::{self, AbortOnDropHandle}, @@ -144,15 +144,15 @@ struct ComputeApi { } impl ComputeApi { - pub fn connect(endpoint: quinn::Endpoint, addr: SocketAddr) -> anyhow::Result { + pub fn connect(endpoint: quinn::Endpoint, addr: SocketAddr) -> Result { Ok(ComputeApi { inner: Client::quinn(endpoint, addr), }) } - pub fn listen(&self, endpoint: quinn::Endpoint) -> anyhow::Result> { + pub fn listen(&self, endpoint: quinn::Endpoint) -> Result> { let Some(local) = self.inner.as_local() else { - bail!("cannot listen on a remote service"); + bail_any!("cannot listen on a remote service"); }; let handler = ComputeProtocol::remote_handler(local); Ok(AbortOnDropHandle::new(task::spawn(listen( @@ -160,7 +160,7 @@ impl ComputeApi { )))) } - pub async fn sqr(&self, num: u64) -> anyhow::Result> { + pub async fn sqr(&self, num: u64) -> Result> { let msg = Sqr { num }; match self.inner.request().await? { Request::Local(request) => { @@ -175,7 +175,7 @@ impl ComputeApi { } } - pub async fn sum(&self) -> anyhow::Result<(mpsc::Sender, oneshot::Receiver)> { + pub async fn sum(&self) -> Result<(mpsc::Sender, oneshot::Receiver)> { let msg = Sum; match self.inner.request().await? { Request::Local(request) => { @@ -191,7 +191,7 @@ impl ComputeApi { } } - pub async fn fibonacci(&self, max: u64) -> anyhow::Result> { + pub async fn fibonacci(&self, max: u64) -> Result> { let msg = Fibonacci { max }; match self.inner.request().await? { Request::Local(request) => { @@ -206,10 +206,7 @@ impl ComputeApi { } } - pub async fn multiply( - &self, - initial: u64, - ) -> anyhow::Result<(mpsc::Sender, mpsc::Receiver)> { + pub async fn multiply(&self, initial: u64) -> Result<(mpsc::Sender, mpsc::Receiver)> { let msg = Multiply { initial }; match self.inner.request().await? { Request::Local(request) => { @@ -227,7 +224,7 @@ impl ComputeApi { } // Local usage example -async fn local() -> anyhow::Result<()> { +async fn local() -> Result<()> { let api = ComputeActor::local(); // Test Sqr @@ -265,7 +262,7 @@ async fn local() -> anyhow::Result<()> { Ok(()) } -fn remote_api() -> anyhow::Result<(ComputeApi, AbortOnDropHandle<()>)> { +fn remote_api() -> Result<(ComputeApi, AbortOnDropHandle<()>)> { let port = 10114; let (server, cert) = make_server_endpoint(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port).into())?; @@ -278,7 +275,7 @@ fn remote_api() -> anyhow::Result<(ComputeApi, AbortOnDropHandle<()>)> { } // Remote usage example -async fn remote() -> anyhow::Result<()> { +async fn remote() -> Result<()> { let (api, handle) = remote_api()?; // Test Sqr @@ -318,7 +315,7 @@ async fn remote() -> anyhow::Result<()> { } // Benchmark function using the new ComputeApi -async fn bench(api: ComputeApi, n: u64) -> anyhow::Result<()> { +async fn bench(api: ComputeApi, n: u64) -> Result<()> { // Individual RPCs (sequential) { let mut sum = 0; @@ -342,7 +339,7 @@ async fn bench(api: ComputeApi, n: u64) -> anyhow::Result<()> { let api = api.clone(); let reqs = n0_future::stream::iter((0..n).map(move |i| { let api = api.clone(); - async move { anyhow::Ok(api.sqr(i).await?.await?) } + async move { n0_error::Ok(api.sqr(i).await?.await?) } })); let resp: Vec<_> = reqs.buffered_unordered(32).try_collect().await?; let sum = resp.into_iter().sum::(); @@ -376,7 +373,7 @@ async fn bench(api: ComputeApi, n: u64) -> anyhow::Result<()> { assert_eq!(sum, (0..n).map(|x| x * 2).sum::()); clear_line()?; println!("bidi seq {} rps", rps.separate_with_underscores()); - handle.await??; + handle.await.std_context("panic in task")??; } Ok(()) @@ -395,7 +392,7 @@ fn clear_line() -> io::Result<()> { } // Simple benchmark sending oneshot senders via an mpsc channel -pub async fn reference_bench(n: u64) -> anyhow::Result<()> { +pub async fn reference_bench(n: u64) -> Result<()> { // Create an mpsc channel to send oneshot senders let (tx, mut rx) = tokio::sync::mpsc::channel::>(32); @@ -414,8 +411,8 @@ pub async fn reference_bench(n: u64) -> anyhow::Result<()> { let t0 = std::time::Instant::now(); for i in 0..n { let (send, recv) = tokio::sync::oneshot::channel(); - tx.send(send).await?; - sum += recv.await?; + tx.send(send).await.anyerr()?; + sum += recv.await.anyerr()?; if i % 10000 == 0 { print!("."); io::stdout().flush()?; @@ -432,8 +429,8 @@ pub async fn reference_bench(n: u64) -> anyhow::Result<()> { let t0 = std::time::Instant::now(); let reqs = n0_future::stream::iter((0..n).map(|_| async { let (send, recv) = tokio::sync::oneshot::channel(); - tx.send(send).await?; - anyhow::Ok(recv.await?) + tx.send(send).await.anyerr()?; + n0_error::Ok(recv.await.anyerr()?) })); let resp: Vec<_> = reqs.buffered_unordered(32).try_collect().await?; let sum = resp.into_iter().sum::(); @@ -447,7 +444,7 @@ pub async fn reference_bench(n: u64) -> anyhow::Result<()> { } #[tokio::main] -async fn main() -> anyhow::Result<()> { +async fn main() -> Result<()> { tracing_subscriber::fmt::init(); println!("Local use"); local().await?; diff --git a/examples/derive.rs b/examples/derive.rs index 88a579a..7c1ddcf 100644 --- a/examples/derive.rs +++ b/examples/derive.rs @@ -3,7 +3,6 @@ use std::{ net::{Ipv4Addr, SocketAddr, SocketAddrV4}, }; -use anyhow::{Context, Result}; use irpc::{ channel::{mpsc, oneshot}, rpc::RemoteService, @@ -11,6 +10,7 @@ use irpc::{ util::{make_client_endpoint, make_server_endpoint}, Client, WithChannels, }; +use n0_error::{Result, StdResultExt}; // Import the macro use n0_future::task::{self, AbortOnDropHandle}; use serde::{Deserialize, Serialize}; @@ -129,7 +129,7 @@ impl StorageApi { let local = self .inner .as_local() - .context("cannot listen on remote API")?; + .std_context("cannot listen on remote API")?; let join_handle = task::spawn(irpc::rpc::listen( endpoint, StorageProtocol::remote_handler(local), diff --git a/examples/storage.rs b/examples/storage.rs index e4e939f..c93d20d 100644 --- a/examples/storage.rs +++ b/examples/storage.rs @@ -3,13 +3,13 @@ use std::{ net::{Ipv4Addr, SocketAddr, SocketAddrV4}, }; -use anyhow::bail; use irpc::{ channel::{mpsc, none::NoReceiver, oneshot}, rpc::{listen, RemoteService}, util::{make_client_endpoint, make_server_endpoint}, Channels, Client, Request, Service, WithChannels, }; +use n0_error::{bail_any, Result}; use n0_future::task::{self, AbortOnDropHandle}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -125,15 +125,15 @@ struct StorageApi { } impl StorageApi { - pub fn connect(endpoint: quinn::Endpoint, addr: SocketAddr) -> anyhow::Result { + pub fn connect(endpoint: quinn::Endpoint, addr: SocketAddr) -> Result { Ok(StorageApi { inner: Client::quinn(endpoint, addr), }) } - pub fn listen(&self, endpoint: quinn::Endpoint) -> anyhow::Result> { + pub fn listen(&self, endpoint: quinn::Endpoint) -> Result> { let Some(local) = self.inner.as_local() else { - bail!("cannot listen on a remote service"); + bail_any!("cannot listen on a remote service"); }; let handler = StorageProtocol::remote_handler(local); Ok(AbortOnDropHandle::new(task::spawn(listen( @@ -141,7 +141,7 @@ impl StorageApi { )))) } - pub async fn get(&self, key: String) -> anyhow::Result>> { + pub async fn get(&self, key: String) -> Result>> { let msg = Get { key }; match self.inner.request().await? { Request::Local(request) => { @@ -156,7 +156,7 @@ impl StorageApi { } } - pub async fn list(&self) -> anyhow::Result> { + pub async fn list(&self) -> Result> { let msg = List; match self.inner.request().await? { Request::Local(request) => { @@ -171,7 +171,7 @@ impl StorageApi { } } - pub async fn set(&self, key: String, value: String) -> anyhow::Result> { + pub async fn set(&self, key: String, value: String) -> Result> { let msg = Set { key, value }; match self.inner.request().await? { Request::Local(request) => { @@ -187,7 +187,7 @@ impl StorageApi { } } -async fn local() -> anyhow::Result<()> { +async fn local() -> Result<()> { let api = StorageActor::local(); api.set("hello".to_string(), "world".to_string()) .await? @@ -201,7 +201,7 @@ async fn local() -> anyhow::Result<()> { Ok(()) } -async fn remote() -> anyhow::Result<()> { +async fn remote() -> Result<()> { let port = 10113; let (server, cert) = make_server_endpoint(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port).into())?; @@ -227,7 +227,7 @@ async fn remote() -> anyhow::Result<()> { } #[tokio::main] -async fn main() -> anyhow::Result<()> { +async fn main() -> Result<()> { tracing_subscriber::fmt::init(); println!("Local use"); local().await?; diff --git a/irpc-iroh/Cargo.toml b/irpc-iroh/Cargo.toml index 616ce44..5d875bb 100644 --- a/irpc-iroh/Cargo.toml +++ b/irpc-iroh/Cargo.toml @@ -13,12 +13,12 @@ description = "Iroh transport for irpc" crate-type = ["cdylib", "rlib"] [dependencies] -anyhow = { workspace = true } iroh = { workspace = true } tokio = { workspace = true, features = ["sync"] } tracing = { workspace = true } serde = { workspace = true } postcard = { workspace = true, features = ["alloc", "use-std"] } +n0-error = { workspace = true } n0-future = { workspace = true } irpc = { version = "0.10.0", path = ".." } iroh-base.workspace = true diff --git a/irpc-iroh/examples/0rtt.rs b/irpc-iroh/examples/0rtt.rs index 985a885..f5ef25e 100644 --- a/irpc-iroh/examples/0rtt.rs +++ b/irpc-iroh/examples/0rtt.rs @@ -4,9 +4,9 @@ use std::{ time::{Duration, Instant}, }; -use anyhow::{Context, Result}; use clap::Parser; use iroh::{protocol::Router, Endpoint, EndpointAddr, EndpointId, SecretKey}; +use n0_error::{Result, StdResultExt}; use ping::EchoApi; #[tokio::main] @@ -38,7 +38,7 @@ async fn main() -> Result<()> { tokio::signal::ctrl_c() .await .expect("failed to listen for ctrl_c"); - server_router.shutdown().await?; + server_router.shutdown().await.anyerr()?; } cli::Args::Connect { ticket, @@ -148,7 +148,7 @@ async fn ping_one( pub fn get_or_generate_secret_key() -> Result { if let Ok(secret) = env::var("IROH_SECRET") { // Parse the secret key from string - SecretKey::from_str(&secret).context("Invalid secret key format") + SecretKey::from_str(&secret).std_context("Invalid secret key format") } else { // Generate a new random key let secret_key = SecretKey::generate(&mut rand::rng()); @@ -186,11 +186,11 @@ mod cli { } mod ping { - use anyhow::{Context, Result}; use futures_util::FutureExt; use iroh::Endpoint; use irpc::{channel::oneshot, rpc::RemoteService, rpc_requests, Client, WithChannels}; use irpc_iroh::{Iroh0RttProtocol, IrohProtocol, IrohRemoteConnection}; + use n0_error::{Result, StdResultExt}; use n0_future::future; use serde::{Deserialize, Serialize}; use tracing::info; @@ -225,7 +225,7 @@ mod ping { let local = self .inner .as_local() - .context("can not listen on remote service")?; + .std_context("can not listen on remote service")?; Ok(Iroh0RttProtocol::new(EchoProtocol::remote_handler(local))) } @@ -233,7 +233,7 @@ mod ping { let local = self .inner .as_local() - .context("can not listen on remote service")?; + .std_context("can not listen on remote service")?; Ok(IrohProtocol::new(EchoProtocol::remote_handler(local))) } @@ -244,7 +244,7 @@ mod ping { let conn = endpoint .connect(addr, Self::ALPN) .await - .context("failed to connect to remote service")?; + .std_context("failed to connect to remote service")?; let fut: future::Boxed = Box::pin(async { true }); Ok(EchoApi { inner: Client::boxed(IrohRemoteConnection::new(conn)), @@ -259,7 +259,7 @@ mod ping { let connecting = endpoint .connect_with_opts(addr, Self::ALPN, Default::default()) .await - .context("failed to connect to remote service")?; + .std_context("failed to connect to remote service")?; match connecting.into_0rtt() { Ok((conn, zero_rtt_accepted)) => { info!("0-RTT possible from our side"); @@ -272,7 +272,7 @@ mod ping { Err(connecting) => { info!("0-RTT not possible from our side"); let fut: future::Boxed = Box::pin(async { true }); - let conn = connecting.await?; + let conn = connecting.await.anyerr()?; Ok(EchoApi { inner: Client::boxed(IrohRemoteConnection::new(conn)), zero_rtt_accepted: fut.shared(), diff --git a/irpc-iroh/examples/auth.rs b/irpc-iroh/examples/auth.rs index 13f1af1..45fd7d7 100644 --- a/irpc-iroh/examples/auth.rs +++ b/irpc-iroh/examples/auth.rs @@ -3,8 +3,8 @@ //! * Manually implementing the connection loop //! * Authenticating peers -use anyhow::Result; use iroh::{protocol::Router, Endpoint}; +use n0_error::Result; use self::storage::{StorageClient, StorageServer}; @@ -65,7 +65,6 @@ mod storage { sync::{Arc, Mutex}, }; - use anyhow::Result; use iroh::{ endpoint::Connection, protocol::{AcceptError, ProtocolHandler}, @@ -77,6 +76,7 @@ mod storage { }; // Import the macro use irpc_iroh::{read_request, IrohLazyRemoteConnection}; + use n0_error::{AnyError, Result}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -231,13 +231,13 @@ mod storage { } } - pub async fn auth(&self, token: &str) -> Result<(), anyhow::Error> { + pub async fn auth(&self, token: &str) -> Result<()> { self.inner .rpc(Auth { token: token.to_string(), }) .await? - .map_err(|err| anyhow::anyhow!(err)) + .map_err(AnyError::from_string) } pub async fn get(&self, key: String) -> Result, irpc::Error> { diff --git a/irpc-iroh/examples/derive.rs b/irpc-iroh/examples/derive.rs index 9ae06fc..eb8ad56 100644 --- a/irpc-iroh/examples/derive.rs +++ b/irpc-iroh/examples/derive.rs @@ -1,5 +1,5 @@ -use anyhow::Result; use iroh::{protocol::Router, Endpoint}; +use n0_error::Result; use self::storage::StorageApi; @@ -57,7 +57,6 @@ mod storage { use std::collections::BTreeMap; - use anyhow::{Context, Result}; use iroh::{protocol::ProtocolHandler, Endpoint}; use irpc::{ channel::{mpsc, oneshot}, @@ -66,6 +65,7 @@ mod storage { }; // Import the macro use irpc_iroh::{IrohLazyRemoteConnection, IrohProtocol}; + use n0_error::{Result, StdResultExt}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -171,7 +171,7 @@ mod storage { let local = self .inner .as_local() - .context("can not listen on remote service")?; + .std_context("can not listen on remote service")?; Ok(IrohProtocol::new(StorageProtocol::remote_handler(local))) } diff --git a/irpc-iroh/examples/simple.rs b/irpc-iroh/examples/simple.rs index d08cab4..ed55637 100644 --- a/irpc-iroh/examples/simple.rs +++ b/irpc-iroh/examples/simple.rs @@ -1,15 +1,15 @@ #[tokio::main] -async fn main() -> anyhow::Result<()> { +async fn main() -> n0_error::Result<()> { cli::run().await } mod proto { use std::collections::HashMap; - use anyhow::Result; use iroh::{protocol::Router, Endpoint, EndpointId}; use irpc::{channel::oneshot, rpc_requests, Client, WithChannels}; use irpc_iroh::IrohProtocol; + use n0_error::{Result, StdResultExt}; use serde::{Deserialize, Serialize}; const ALPN: &[u8] = b"iroh-irpc/simple/1"; @@ -44,7 +44,7 @@ mod proto { println!("endpoint id: {}", router.endpoint().id()); tokio::signal::ctrl_c().await?; - router.shutdown().await?; + router.shutdown().await.anyerr()?; Ok(()) } @@ -83,9 +83,9 @@ mod proto { } mod cli { - use anyhow::Result; use clap::Parser; use iroh::EndpointId; + use n0_error::Result; use crate::proto::{connect, listen, GetRequest, SetRequest}; diff --git a/irpc-iroh/src/lib.rs b/irpc-iroh/src/lib.rs index 6971a1b..d662109 100644 --- a/irpc-iroh/src/lib.rs +++ b/irpc-iroh/src/lib.rs @@ -16,6 +16,7 @@ use irpc::{ util::AsyncReadVarintExt, LocalSender, RequestError, }; +use n0_error::{e, Result}; use n0_future::{future::Boxed as BoxFuture, TryFutureExt}; use serde::de::DeserializeOwned; use tracing::{debug, error_span, trace, trace_span, warn, Instrument}; @@ -106,14 +107,11 @@ impl RemoteConnection for IrohLazyRemoteConnection { // try with a new connection, just once *guard = None; connect_and_open_bi(&this.endpoint, &this.addr, &this.alpn, guard) - .await - .map_err(RequestError::Other)? + .await? } } } - None => connect_and_open_bi(&this.endpoint, &this.addr, &this.alpn, guard) - .await - .map_err(RequestError::Other)?, + None => connect_and_open_bi(&this.endpoint, &this.addr, &this.alpn, guard).await?, }; Ok(pair) }) @@ -125,8 +123,11 @@ async fn connect_and_open_bi( addr: &iroh::EndpointAddr, alpn: &[u8], mut guard: tokio::sync::MutexGuard<'_, Option>, -) -> anyhow::Result<(SendStream, RecvStream)> { - let conn = endpoint.connect(addr.clone(), alpn).await?; +) -> Result<(SendStream, RecvStream), RequestError> { + let conn = endpoint + .connect(addr.clone(), alpn) + .await + .map_err(|err| e!(RequestError::Other, err.into()))?; let (send, recv) = conn.open_bi().await?; *guard = Some(conn); Ok((send, recv)) @@ -290,7 +291,7 @@ pub async fn read_request_raw( ERROR_CODE_MAX_MESSAGE_SIZE_EXCEEDED.into(), b"request exceeded max message size", ); - return Err(oneshot::RecvError::MaxMessageSizeExceeded.into()); + return Err(e!(oneshot::RecvError::MaxMessageSizeExceeded).into()); } let mut buf = vec![0; size as usize]; recv.read_exact(&mut buf) diff --git a/src/lib.rs b/src/lib.rs index 6e7cdfc..5d0386a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,7 +80,7 @@ //! use serde::{Deserialize, Serialize}; //! //! #[tokio::main] -//! async fn main() -> anyhow::Result<()> { +//! async fn main() -> n0_error::Result<()> { //! let client = spawn_server(); //! let res = client.rpc(Multiply(3, 7)).await?; //! assert_eq!(res, 21); @@ -266,7 +266,7 @@ use std::{fmt::Debug, future::Future, io, marker::PhantomData, ops::Deref, resul /// Set { key: String, value: String }, /// } /// -/// async fn client_usage(client: Client) -> anyhow::Result<()> { +/// async fn client_usage(client: Client) -> n0_error::Result<()> { /// client /// .rpc(SetRequest { /// key: "foo".to_string(), diff --git a/src/util.rs b/src/util.rs index 558a24c..76803b1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,7 +7,7 @@ mod quinn_setup_utils { use std::{sync::Arc, time::Duration}; - use anyhow::Result; + use n0_error::{Result, StdResultExt}; use quinn::{crypto::rustls::QuicClientConfig, ClientConfig, ServerConfig}; /// Create a quinn client config and trusts given certificates. @@ -19,7 +19,7 @@ mod quinn_setup_utils { let mut certs = rustls::RootCertStore::empty(); for cert in server_certs { let cert = rustls::pki_types::CertificateDer::from(cert.to_vec()); - certs.add(cert)?; + certs.add(cert).std_context("Error configuring certs")?; } let provider = rustls::crypto::ring::default_provider(); @@ -29,7 +29,8 @@ mod quinn_setup_utils { .with_root_certificates(certs) .with_no_client_auth(); let quic_client_config = - quinn::crypto::rustls::QuicClientConfig::try_from(crypto_client_config)?; + quinn::crypto::rustls::QuicClientConfig::try_from(crypto_client_config) + .std_context("Error creating QUIC client config")?; let mut transport_config = quinn::TransportConfig::default(); transport_config.keep_alive_interval(Some(Duration::from_secs(1))); @@ -41,14 +42,16 @@ mod quinn_setup_utils { /// Create a quinn server config with a self-signed certificate /// /// Returns the server config and the certificate in DER format - pub fn configure_server() -> anyhow::Result<(ServerConfig, Vec)> { - let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()])?; + pub fn configure_server() -> Result<(ServerConfig, Vec)> { + let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]) + .std_context("Error generating self-signed cert")?; let cert_der = cert.cert.der(); let priv_key = rustls::pki_types::PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der()); let cert_chain = vec![cert_der.clone()]; - let mut server_config = ServerConfig::with_single_cert(cert_chain, priv_key.into())?; + let mut server_config = ServerConfig::with_single_cert(cert_chain, priv_key.into()) + .std_context("Error creating server config")?; Arc::get_mut(&mut server_config.transport) .unwrap() .max_concurrent_uni_streams(0_u8.into()); @@ -65,7 +68,8 @@ mod quinn_setup_utils { .dangerous() .with_custom_certificate_verifier(Arc::new(SkipServerVerification)) .with_no_client_auth(); - let client_cfg = QuicClientConfig::try_from(crypto)?; + let client_cfg = + QuicClientConfig::try_from(crypto).std_context("Error creating QUIC client config")?; let client_cfg = ClientConfig::new(Arc::new(client_cfg)); Ok(client_cfg) } From d3df39620aee249afd7751c32afb635f1861ffaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Tue, 4 Nov 2025 11:35:01 +0100 Subject: [PATCH 3/9] Simplify `OddNumberError` again --- tests/common.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/common.rs b/tests/common.rs index 932c786..1e18203 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -3,7 +3,7 @@ use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use irpc::util::{make_client_endpoint, make_server_endpoint}; -use n0_error::{e, stack_error}; +use n0_error::stack_error; use quinn::Endpoint; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use testresult::TestResult; @@ -20,11 +20,9 @@ pub fn create_connected_endpoints() -> TestResult<(Endpoint, Endpoint, SocketAdd #[derive(Debug)] pub struct NoSer(pub u64); -#[stack_error(derive, add_meta)] +#[stack_error(derive)] #[error("Cannot serialize odd number")] -pub struct OddNumberError { - value: u64, -} +pub struct OddNumberError(u64); impl Serialize for NoSer { fn serialize(&self, serializer: S) -> Result @@ -32,9 +30,7 @@ impl Serialize for NoSer { S: Serializer, { if self.0 % 2 == 1 { - Err(serde::ser::Error::custom(e!(OddNumberError { - value: self.0 - }))) + Err(serde::ser::Error::custom(OddNumberError(self.0))) } else { serializer.serialize_u64(self.0) } @@ -48,7 +44,7 @@ impl<'de> Deserialize<'de> for NoSer { { let value = u64::deserialize(deserializer)?; if value % 2 != 0 { - Err(serde::de::Error::custom(e!(OddNumberError { value }))) + Err(serde::de::Error::custom(OddNumberError(value))) } else { Ok(NoSer(value)) } From d8b213b8d3b0f7f9372d4880ab9585c426be10dc Mon Sep 17 00:00:00 2001 From: Frando Date: Tue, 4 Nov 2025 11:58:59 +0100 Subject: [PATCH 4/9] fix: fields for Unreachable error --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5d0386a..a278331 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1776,7 +1776,7 @@ impl From for io::Error { #[cfg(feature = "rpc")] RequestError::Other { source, .. } => io::Error::other(source), #[cfg(not(feature = "rpc"))] - RequestError::Unreachable => unreachable!(), + RequestError::Unreachable { .. } => unreachable!(), } } } From c7691919c824dcab9d09f4de6332d851e87e5c16 Mon Sep 17 00:00:00 2001 From: Franz Heinzmann Date: Wed, 5 Nov 2025 19:28:46 +0100 Subject: [PATCH 5/9] refactor: use adapted iroh 0rtt api (#81) Depends on https://github.com/n0-computer/iroh/pull/3607 --- Cargo.lock | 248 ++++++------------------------------- Cargo.toml | 7 +- irpc-iroh/examples/0rtt.rs | 21 +++- irpc-iroh/src/lib.rs | 130 +++++++++++++++---- 4 files changed, 163 insertions(+), 243 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd1c89c..f86b3aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "aead" version = "0.6.0-rc.2" @@ -102,15 +87,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" -dependencies = [ - "backtrace", -] - [[package]] name = "arrayref" version = "0.3.9" @@ -144,7 +120,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -202,21 +178,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link 0.2.1", -] - [[package]] name = "base16ct" version = "0.3.0" @@ -270,12 +231,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "btparse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387e80962b798815a2b5c4bcfdb6bf626fa922ffe9f74e373103b858738e9f31" - [[package]] name = "bumpalo" version = "3.19.0" @@ -389,7 +344,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -407,17 +362,6 @@ dependencies = [ "thiserror 2.0.17", ] -[[package]] -name = "color-backtrace" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e49b1973af2a47b5b44f7dd0a344598da95c872e1556b045607888784e973b91" -dependencies = [ - "backtrace", - "btparse", - "termcolor", -] - [[package]] name = "colorchoice" version = "1.0.4" @@ -589,7 +533,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -644,7 +588,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", "unicode-xid", ] @@ -656,7 +600,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", "unicode-xid", ] @@ -685,7 +629,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -762,7 +706,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -892,7 +836,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -966,12 +910,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - [[package]] name = "glob" version = "0.3.3" @@ -1441,8 +1379,8 @@ dependencies = [ [[package]] name = "iroh" -version = "0.94.0" -source = "git+https://github.com/n0-computer/iroh.git?branch=main#30c23e8dbaa02d17ab57ba41f0aa5271b0a411dc" +version = "0.95.0" +source = "git+https://github.com/n0-computer/iroh.git?branch=Frando/0rtt-api#3ceb8a2d18c9f8d1c76b46ade0293136c20e3bba" dependencies = [ "aead", "backon", @@ -1458,7 +1396,7 @@ dependencies = [ "http", "igd-next", "instant", - "iroh-base 0.94.1", + "iroh-base", "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", @@ -1495,22 +1433,8 @@ dependencies = [ [[package]] name = "iroh-base" -version = "0.94.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db942f6f3d6fa9b475690c6e8e6684d60591dd886bf1bdfef4c60d89d502215c" -dependencies = [ - "derive_more 2.0.1", - "n0-snafu", - "nested_enum_utils", - "serde", - "snafu", - "url", -] - -[[package]] -name = "iroh-base" -version = "0.94.1" -source = "git+https://github.com/n0-computer/iroh.git?branch=main#30c23e8dbaa02d17ab57ba41f0aa5271b0a411dc" +version = "0.95.0" +source = "git+https://github.com/n0-computer/iroh.git?branch=Frando/0rtt-api#3ceb8a2d18c9f8d1c76b46ade0293136c20e3bba" dependencies = [ "curve25519-dalek", "data-encoding", @@ -1548,7 +1472,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -1607,8 +1531,8 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "0.94.0" -source = "git+https://github.com/n0-computer/iroh.git?branch=main#30c23e8dbaa02d17ab57ba41f0aa5271b0a411dc" +version = "0.95.0" +source = "git+https://github.com/n0-computer/iroh.git?branch=Frando/0rtt-api#3ceb8a2d18c9f8d1c76b46ade0293136c20e3bba" dependencies = [ "blake3", "bytes", @@ -1621,7 +1545,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "iroh-base 0.94.1", + "iroh-base", "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", @@ -1682,7 +1606,7 @@ version = "0.8.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -1694,7 +1618,7 @@ dependencies = [ "getrandom 0.3.4", "hex", "iroh", - "iroh-base 0.94.0", + "iroh-base", "irpc", "irpc-derive", "n0-error", @@ -1839,15 +1763,6 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - [[package]] name = "mio" version = "1.1.0" @@ -1897,7 +1812,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -1921,19 +1836,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "n0-snafu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1815107e577a95bfccedb4cfabc73d709c0db6d12de3f14e0f284a8c5036dc4f" -dependencies = [ - "anyhow", - "btparse", - "color-backtrace", - "snafu", - "tracing-error", -] - [[package]] name = "n0-watcher" version = "0.5.0" @@ -1945,18 +1847,6 @@ dependencies = [ "n0-future", ] -[[package]] -name = "nested_enum_utils" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa9161ed44d30e9702fe42bd78693bceac0fed02f647da749f36109023d3a3" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "netdev" version = "0.38.2" @@ -2114,16 +2004,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.107", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", + "syn", ] [[package]] @@ -2235,7 +2116,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -2359,7 +2240,7 @@ checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -2633,12 +2514,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -2859,7 +2734,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -2991,28 +2866,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "snafu" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" -dependencies = [ - "backtrace", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.107", -] - [[package]] name = "socket2" version = "0.5.10" @@ -3041,7 +2894,7 @@ checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -3099,7 +2952,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -3108,17 +2961,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.107" @@ -3147,7 +2989,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -3224,7 +3066,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -3235,7 +3077,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -3323,7 +3165,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -3500,7 +3342,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -3513,16 +3355,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-error" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" -dependencies = [ - "tracing", - "tracing-subscriber", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -3705,7 +3537,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.107", + "syn", "wasm-bindgen-shared", ] @@ -3740,7 +3572,7 @@ checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3950,7 +3782,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -3961,7 +3793,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -4456,7 +4288,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", "synstructure", ] @@ -4483,7 +4315,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -4503,7 +4335,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", "synstructure", ] @@ -4524,7 +4356,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] [[package]] @@ -4557,5 +4389,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index d9d5cb0..cd1be96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,10 +106,11 @@ tracing = { version = "0.1.41", default-features = false } n0-future = { version = "0.3", default-features = false } n0-error = { version = "0.1.0" } tracing-subscriber = { version = "0.3.20" } -iroh = { version = "0.94" } -iroh-base = { version = "0.94" } +iroh = { version = "0.95" } +iroh-base = { version = "0.95" } quinn = { package = "iroh-quinn", version = "0.14.0", default-features = false } futures-util = { version = "0.3", features = ["sink"] } [patch.crates-io] -iroh = { git = "https://github.com/n0-computer/iroh.git", branch = "main" } +iroh = { git = "https://github.com/n0-computer/iroh.git", branch = "Frando/0rtt-api" } +iroh-base = { git = "https://github.com/n0-computer/iroh.git", branch = "Frando/0rtt-api" } diff --git a/irpc-iroh/examples/0rtt.rs b/irpc-iroh/examples/0rtt.rs index f5ef25e..0023b01 100644 --- a/irpc-iroh/examples/0rtt.rs +++ b/irpc-iroh/examples/0rtt.rs @@ -187,9 +187,11 @@ mod cli { mod ping { use futures_util::FutureExt; - use iroh::Endpoint; + use iroh::{endpoint::ZeroRttStatus, Endpoint}; use irpc::{channel::oneshot, rpc::RemoteService, rpc_requests, Client, WithChannels}; - use irpc_iroh::{Iroh0RttProtocol, IrohProtocol, IrohRemoteConnection}; + use irpc_iroh::{ + Iroh0RttProtocol, IrohProtocol, IrohRemoteConnection, IrohZrttRemoteConnection, + }; use n0_error::{Result, StdResultExt}; use n0_future::future; use serde::{Deserialize, Serialize}; @@ -261,11 +263,20 @@ mod ping { .await .std_context("failed to connect to remote service")?; match connecting.into_0rtt() { - Ok((conn, zero_rtt_accepted)) => { + Ok(conn) => { info!("0-RTT possible from our side"); - let fut: future::Boxed = Box::pin(zero_rtt_accepted); + let fut: future::Boxed = Box::pin({ + let conn = conn.clone(); + async move { + match conn.handshake_completed().await { + Err(_) => false, + Ok(ZeroRttStatus::Accepted(_)) => true, + Ok(ZeroRttStatus::Rejected(_)) => false, + } + } + }); Ok(EchoApi { - inner: Client::boxed(IrohRemoteConnection::new(conn)), + inner: Client::boxed(IrohZrttRemoteConnection::new(conn)), zero_rtt_accepted: fut.shared(), }) } diff --git a/irpc-iroh/src/lib.rs b/irpc-iroh/src/lib.rs index d662109..8deb25b 100644 --- a/irpc-iroh/src/lib.rs +++ b/irpc-iroh/src/lib.rs @@ -1,11 +1,17 @@ use std::{ - fmt, io, + fmt, + future::Future, + io, sync::{atomic::AtomicU64, Arc}, }; use iroh::{ - endpoint::{Connecting, Connection, ConnectionError, RecvStream, SendStream}, + endpoint::{ + Accepting, ConnectingError, Connection, ConnectionError, IncomingZeroRttConnection, + OutgoingZeroRttConnection, RecvStream, RemoteEndpointIdError, SendStream, VarInt, + }, protocol::{AcceptError, ProtocolHandler}, + EndpointId, }; use irpc::{ channel::oneshot, @@ -63,6 +69,32 @@ impl irpc::rpc::RemoteConnection for IrohRemoteConnection { } } +#[derive(Debug, Clone)] +pub struct IrohZrttRemoteConnection(OutgoingZeroRttConnection); + +impl IrohZrttRemoteConnection { + pub fn new(connection: OutgoingZeroRttConnection) -> Self { + Self(connection) + } +} + +impl irpc::rpc::RemoteConnection for IrohZrttRemoteConnection { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + + fn open_bi( + &self, + ) -> n0_future::future::Boxed> + { + let conn = self.0.clone(); + Box::pin(async move { + let (send, recv) = conn.open_bi().await?; + Ok((send, recv)) + }) + } +} + /// A connection to a remote service. /// /// Initially this does just have the endpoint and the address. Once a @@ -166,17 +198,14 @@ impl IrohProtocol { } impl ProtocolHandler for IrohProtocol { - fn accept( - &self, - connection: Connection, - ) -> impl std::future::Future> + Send { + async fn accept(&self, connection: Connection) -> Result<(), AcceptError> { let handler = self.handler.clone(); let request_id = self .request_id .fetch_add(1, std::sync::atomic::Ordering::AcqRel); - let fut = handle_connection(connection, handler).map_err(AcceptError::from_err); + let fut = handle_connection(&connection, handler).map_err(AcceptError::from_err); let span = trace_span!("rpc", id = request_id); - Box::pin(fut.instrument(span)) + fut.instrument(span).await } } @@ -215,30 +244,32 @@ impl Iroh0RttProtocol { } impl ProtocolHandler for Iroh0RttProtocol { - async fn on_connecting(&self, connecting: Connecting) -> Result { - let (conn, _zero_rtt_accepted) = connecting - .into_0rtt() - .expect("accept into 0.5 RTT always succeeds"); - Ok(conn) - } - - fn accept( - &self, - connection: Connection, - ) -> impl std::future::Future> + Send { + async fn on_accepting(&self, accepting: Accepting) -> Result { + let zrtt_conn = accepting.into_0rtt(); let handler = self.handler.clone(); let request_id = self .request_id .fetch_add(1, std::sync::atomic::Ordering::AcqRel); - let fut = handle_connection(connection, handler).map_err(AcceptError::from_err); - let span = trace_span!("rpc", id = request_id); - Box::pin(fut.instrument(span)) + handle_connection(&zrtt_conn, handler) + .map_err(AcceptError::from_err) + .instrument(trace_span!("rpc", id = request_id)) + .await?; + let conn = zrtt_conn + .handshake_completed() + .await + .map_err(|err| AcceptError::from(ConnectingError::from(err)))?; + Ok(conn) + } + + async fn accept(&self, _connection: Connection) -> Result<(), AcceptError> { + // Noop, handled in [`Self::on_accepting`] + Ok(()) } } /// Handles a single iroh connection with the provided `handler`. pub async fn handle_connection( - connection: Connection, + connection: &impl IncomingRemoteConnection, handler: Handler, ) -> io::Result<()> { if let Ok(remote) = connection.remote_id() { @@ -246,21 +277,66 @@ pub async fn handle_connection( } debug!("connection accepted"); loop { - let Some((msg, rx, tx)) = read_request_raw(&connection).await? else { + let Some((msg, rx, tx)) = read_request_raw(connection).await? else { return Ok(()); }; handler(msg, rx, tx).await?; } } +/// Reads a single request from a connection, and a message with channels. pub async fn read_request( - connection: &Connection, + connection: &impl IncomingRemoteConnection, ) -> std::io::Result> { Ok(read_request_raw::(connection) .await? .map(|(msg, rx, tx)| S::with_remote_channels(msg, rx, tx))) } +/// Abstracts over [`Connection`] and [`IncomingZeroRttConnection`]. +/// +/// You don't need to implement this trait yourself. It is used by [`read_request`] and +/// [`handle_connection`] to work with both fully authenticated connections and with +/// 0-RTT connections. +pub trait IncomingRemoteConnection { + /// Accepts a single bidirectional stream. + fn accept_bi( + &self, + ) -> impl Future> + Send; + /// Close the connection. + fn close(&self, error_code: VarInt, reason: &[u8]); + /// Returns the remote's endpoint id. + /// + /// This may only fail for 0-RTT connections. + fn remote_id(&self) -> Result; +} + +impl IncomingRemoteConnection for IncomingZeroRttConnection { + async fn accept_bi(&self) -> Result<(SendStream, RecvStream), ConnectionError> { + self.accept_bi().await + } + + fn close(&self, error_code: VarInt, reason: &[u8]) { + self.close(error_code, reason) + } + fn remote_id(&self) -> Result { + self.remote_id() + } +} + +impl IncomingRemoteConnection for Connection { + async fn accept_bi(&self) -> Result<(SendStream, RecvStream), ConnectionError> { + self.accept_bi().await + } + + fn close(&self, error_code: VarInt, reason: &[u8]) { + self.close(error_code, reason) + } + fn remote_id(&self) -> Result { + Ok(self.remote_id()) + } +} + /// Reads a single request from the connection. /// /// This accepts a bi-directional stream from the connection and reads and parses the request. @@ -269,7 +345,7 @@ pub async fn read_request( /// Returns None if the remote closed the connection with error code `0`. /// Returns an error for all other failure cases. pub async fn read_request_raw( - connection: &Connection, + connection: &impl IncomingRemoteConnection, ) -> std::io::Result> { let (send, mut recv) = match connection.accept_bi().await { Ok((s, r)) => (s, r), @@ -324,7 +400,7 @@ pub async fn listen(endpoint: iroh::Endpoint, han let handler = handler.clone(); let fut = async move { match incoming.await { - Ok(connection) => match handle_connection(connection, handler).await { + Ok(connection) => match handle_connection(&connection, handler).await { Err(err) => warn!("connection closed with error: {err:?}"), Ok(()) => debug!("connection closed"), }, From 79adfbb65c3e7df45056599accdfbb623fdff546 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Wed, 5 Nov 2025 12:40:02 -0600 Subject: [PATCH 6/9] Revert examples to anyhow anyhow is a dev dependency of both irpc and irpc-iroh because dev deps are included in examples. --- Cargo.lock | 8 +++++++ Cargo.toml | 3 +++ examples/compute.rs | 43 +++++++++++++++++++----------------- examples/derive.rs | 4 ++-- examples/storage.rs | 20 ++++++++--------- irpc-iroh/Cargo.toml | 1 + irpc-iroh/examples/0rtt.rs | 18 +++++++-------- irpc-iroh/examples/auth.rs | 8 +++---- irpc-iroh/examples/derive.rs | 6 ++--- irpc-iroh/examples/simple.rs | 8 +++---- 10 files changed, 67 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f86b3aa..d0a906e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,6 +87,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + [[package]] name = "arrayref" version = "0.3.9" @@ -1579,6 +1585,7 @@ dependencies = [ name = "irpc" version = "0.10.0" dependencies = [ + "anyhow", "derive_more 2.0.1", "futures-buffered", "futures-util", @@ -1613,6 +1620,7 @@ dependencies = [ name = "irpc-iroh" version = "0.10.0" dependencies = [ + "anyhow", "clap", "futures-util", "getrandom 0.3.4", diff --git a/Cargo.toml b/Cargo.toml index cd1be96..554d45a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,8 @@ thousands = "0.2.0" # macro tests trybuild = "1.0.104" testresult = "0.4.1" +# used in examples +anyhow = { workspace = true } [features] # enable the remote transport @@ -99,6 +101,7 @@ rustdoc-args = ["--cfg", "quicrpc_docsrs"] unexpected_cfgs = { level = "warn", check-cfg = ["cfg(quicrpc_docsrs)"] } [workspace.dependencies] +anyhow = { version = "1" } tokio = { version = "1.44", default-features = false } postcard = { version = "1.1.1", default-features = false } serde = { version = "1", default-features = false, features = ["derive"] } diff --git a/examples/compute.rs b/examples/compute.rs index b64ab3f..7908425 100644 --- a/examples/compute.rs +++ b/examples/compute.rs @@ -3,6 +3,7 @@ use std::{ net::{Ipv4Addr, SocketAddr, SocketAddrV4}, }; +use anyhow::bail; use futures_buffered::BufferedStreamExt; use irpc::{ channel::{mpsc, oneshot}, @@ -11,7 +12,6 @@ use irpc::{ util::{make_client_endpoint, make_server_endpoint}, Client, Request, WithChannels, }; -use n0_error::{bail_any, Result, StdResultExt}; use n0_future::{ stream::StreamExt, task::{self, AbortOnDropHandle}, @@ -144,15 +144,15 @@ struct ComputeApi { } impl ComputeApi { - pub fn connect(endpoint: quinn::Endpoint, addr: SocketAddr) -> Result { + pub fn connect(endpoint: quinn::Endpoint, addr: SocketAddr) -> anyhow::Result { Ok(ComputeApi { inner: Client::quinn(endpoint, addr), }) } - pub fn listen(&self, endpoint: quinn::Endpoint) -> Result> { + pub fn listen(&self, endpoint: quinn::Endpoint) -> anyhow::Result> { let Some(local) = self.inner.as_local() else { - bail_any!("cannot listen on a remote service"); + bail!("cannot listen on a remote service"); }; let handler = ComputeProtocol::remote_handler(local); Ok(AbortOnDropHandle::new(task::spawn(listen( @@ -160,7 +160,7 @@ impl ComputeApi { )))) } - pub async fn sqr(&self, num: u64) -> Result> { + pub async fn sqr(&self, num: u64) -> anyhow::Result> { let msg = Sqr { num }; match self.inner.request().await? { Request::Local(request) => { @@ -175,7 +175,7 @@ impl ComputeApi { } } - pub async fn sum(&self) -> Result<(mpsc::Sender, oneshot::Receiver)> { + pub async fn sum(&self) -> anyhow::Result<(mpsc::Sender, oneshot::Receiver)> { let msg = Sum; match self.inner.request().await? { Request::Local(request) => { @@ -191,7 +191,7 @@ impl ComputeApi { } } - pub async fn fibonacci(&self, max: u64) -> Result> { + pub async fn fibonacci(&self, max: u64) -> anyhow::Result> { let msg = Fibonacci { max }; match self.inner.request().await? { Request::Local(request) => { @@ -206,7 +206,10 @@ impl ComputeApi { } } - pub async fn multiply(&self, initial: u64) -> Result<(mpsc::Sender, mpsc::Receiver)> { + pub async fn multiply( + &self, + initial: u64, + ) -> anyhow::Result<(mpsc::Sender, mpsc::Receiver)> { let msg = Multiply { initial }; match self.inner.request().await? { Request::Local(request) => { @@ -224,7 +227,7 @@ impl ComputeApi { } // Local usage example -async fn local() -> Result<()> { +async fn local() -> anyhow::Result<()> { let api = ComputeActor::local(); // Test Sqr @@ -262,7 +265,7 @@ async fn local() -> Result<()> { Ok(()) } -fn remote_api() -> Result<(ComputeApi, AbortOnDropHandle<()>)> { +fn remote_api() -> anyhow::Result<(ComputeApi, AbortOnDropHandle<()>)> { let port = 10114; let (server, cert) = make_server_endpoint(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port).into())?; @@ -275,7 +278,7 @@ fn remote_api() -> Result<(ComputeApi, AbortOnDropHandle<()>)> { } // Remote usage example -async fn remote() -> Result<()> { +async fn remote() -> anyhow::Result<()> { let (api, handle) = remote_api()?; // Test Sqr @@ -315,7 +318,7 @@ async fn remote() -> Result<()> { } // Benchmark function using the new ComputeApi -async fn bench(api: ComputeApi, n: u64) -> Result<()> { +async fn bench(api: ComputeApi, n: u64) -> anyhow::Result<()> { // Individual RPCs (sequential) { let mut sum = 0; @@ -339,7 +342,7 @@ async fn bench(api: ComputeApi, n: u64) -> Result<()> { let api = api.clone(); let reqs = n0_future::stream::iter((0..n).map(move |i| { let api = api.clone(); - async move { n0_error::Ok(api.sqr(i).await?.await?) } + async move { anyhow::Ok(api.sqr(i).await?.await?) } })); let resp: Vec<_> = reqs.buffered_unordered(32).try_collect().await?; let sum = resp.into_iter().sum::(); @@ -373,7 +376,7 @@ async fn bench(api: ComputeApi, n: u64) -> Result<()> { assert_eq!(sum, (0..n).map(|x| x * 2).sum::()); clear_line()?; println!("bidi seq {} rps", rps.separate_with_underscores()); - handle.await.std_context("panic in task")??; + handle.await??; } Ok(()) @@ -392,7 +395,7 @@ fn clear_line() -> io::Result<()> { } // Simple benchmark sending oneshot senders via an mpsc channel -pub async fn reference_bench(n: u64) -> Result<()> { +pub async fn reference_bench(n: u64) -> anyhow::Result<()> { // Create an mpsc channel to send oneshot senders let (tx, mut rx) = tokio::sync::mpsc::channel::>(32); @@ -411,8 +414,8 @@ pub async fn reference_bench(n: u64) -> Result<()> { let t0 = std::time::Instant::now(); for i in 0..n { let (send, recv) = tokio::sync::oneshot::channel(); - tx.send(send).await.anyerr()?; - sum += recv.await.anyerr()?; + tx.send(send).await?; + sum += recv.await?; if i % 10000 == 0 { print!("."); io::stdout().flush()?; @@ -429,8 +432,8 @@ pub async fn reference_bench(n: u64) -> Result<()> { let t0 = std::time::Instant::now(); let reqs = n0_future::stream::iter((0..n).map(|_| async { let (send, recv) = tokio::sync::oneshot::channel(); - tx.send(send).await.anyerr()?; - n0_error::Ok(recv.await.anyerr()?) + tx.send(send).await?; + anyhow::Ok(recv.await?) })); let resp: Vec<_> = reqs.buffered_unordered(32).try_collect().await?; let sum = resp.into_iter().sum::(); @@ -444,7 +447,7 @@ pub async fn reference_bench(n: u64) -> Result<()> { } #[tokio::main] -async fn main() -> Result<()> { +async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); println!("Local use"); local().await?; diff --git a/examples/derive.rs b/examples/derive.rs index 7c1ddcf..88a579a 100644 --- a/examples/derive.rs +++ b/examples/derive.rs @@ -3,6 +3,7 @@ use std::{ net::{Ipv4Addr, SocketAddr, SocketAddrV4}, }; +use anyhow::{Context, Result}; use irpc::{ channel::{mpsc, oneshot}, rpc::RemoteService, @@ -10,7 +11,6 @@ use irpc::{ util::{make_client_endpoint, make_server_endpoint}, Client, WithChannels, }; -use n0_error::{Result, StdResultExt}; // Import the macro use n0_future::task::{self, AbortOnDropHandle}; use serde::{Deserialize, Serialize}; @@ -129,7 +129,7 @@ impl StorageApi { let local = self .inner .as_local() - .std_context("cannot listen on remote API")?; + .context("cannot listen on remote API")?; let join_handle = task::spawn(irpc::rpc::listen( endpoint, StorageProtocol::remote_handler(local), diff --git a/examples/storage.rs b/examples/storage.rs index c93d20d..e4e939f 100644 --- a/examples/storage.rs +++ b/examples/storage.rs @@ -3,13 +3,13 @@ use std::{ net::{Ipv4Addr, SocketAddr, SocketAddrV4}, }; +use anyhow::bail; use irpc::{ channel::{mpsc, none::NoReceiver, oneshot}, rpc::{listen, RemoteService}, util::{make_client_endpoint, make_server_endpoint}, Channels, Client, Request, Service, WithChannels, }; -use n0_error::{bail_any, Result}; use n0_future::task::{self, AbortOnDropHandle}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -125,15 +125,15 @@ struct StorageApi { } impl StorageApi { - pub fn connect(endpoint: quinn::Endpoint, addr: SocketAddr) -> Result { + pub fn connect(endpoint: quinn::Endpoint, addr: SocketAddr) -> anyhow::Result { Ok(StorageApi { inner: Client::quinn(endpoint, addr), }) } - pub fn listen(&self, endpoint: quinn::Endpoint) -> Result> { + pub fn listen(&self, endpoint: quinn::Endpoint) -> anyhow::Result> { let Some(local) = self.inner.as_local() else { - bail_any!("cannot listen on a remote service"); + bail!("cannot listen on a remote service"); }; let handler = StorageProtocol::remote_handler(local); Ok(AbortOnDropHandle::new(task::spawn(listen( @@ -141,7 +141,7 @@ impl StorageApi { )))) } - pub async fn get(&self, key: String) -> Result>> { + pub async fn get(&self, key: String) -> anyhow::Result>> { let msg = Get { key }; match self.inner.request().await? { Request::Local(request) => { @@ -156,7 +156,7 @@ impl StorageApi { } } - pub async fn list(&self) -> Result> { + pub async fn list(&self) -> anyhow::Result> { let msg = List; match self.inner.request().await? { Request::Local(request) => { @@ -171,7 +171,7 @@ impl StorageApi { } } - pub async fn set(&self, key: String, value: String) -> Result> { + pub async fn set(&self, key: String, value: String) -> anyhow::Result> { let msg = Set { key, value }; match self.inner.request().await? { Request::Local(request) => { @@ -187,7 +187,7 @@ impl StorageApi { } } -async fn local() -> Result<()> { +async fn local() -> anyhow::Result<()> { let api = StorageActor::local(); api.set("hello".to_string(), "world".to_string()) .await? @@ -201,7 +201,7 @@ async fn local() -> Result<()> { Ok(()) } -async fn remote() -> Result<()> { +async fn remote() -> anyhow::Result<()> { let port = 10113; let (server, cert) = make_server_endpoint(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port).into())?; @@ -227,7 +227,7 @@ async fn remote() -> Result<()> { } #[tokio::main] -async fn main() -> Result<()> { +async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); println!("Local use"); local().await?; diff --git a/irpc-iroh/Cargo.toml b/irpc-iroh/Cargo.toml index 5d875bb..9bf886d 100644 --- a/irpc-iroh/Cargo.toml +++ b/irpc-iroh/Cargo.toml @@ -34,3 +34,4 @@ clap = { version = "4.5.41", features = ["derive"] } futures-util.workspace = true hex = "0.4.3" rand = "0.9.2" +anyhow = { workspace = true } diff --git a/irpc-iroh/examples/0rtt.rs b/irpc-iroh/examples/0rtt.rs index 0023b01..3dc8342 100644 --- a/irpc-iroh/examples/0rtt.rs +++ b/irpc-iroh/examples/0rtt.rs @@ -4,9 +4,9 @@ use std::{ time::{Duration, Instant}, }; +use anyhow::{Context, Result}; use clap::Parser; use iroh::{protocol::Router, Endpoint, EndpointAddr, EndpointId, SecretKey}; -use n0_error::{Result, StdResultExt}; use ping::EchoApi; #[tokio::main] @@ -38,7 +38,7 @@ async fn main() -> Result<()> { tokio::signal::ctrl_c() .await .expect("failed to listen for ctrl_c"); - server_router.shutdown().await.anyerr()?; + server_router.shutdown().await?; } cli::Args::Connect { ticket, @@ -148,7 +148,7 @@ async fn ping_one( pub fn get_or_generate_secret_key() -> Result { if let Ok(secret) = env::var("IROH_SECRET") { // Parse the secret key from string - SecretKey::from_str(&secret).std_context("Invalid secret key format") + SecretKey::from_str(&secret).context("Invalid secret key format") } else { // Generate a new random key let secret_key = SecretKey::generate(&mut rand::rng()); @@ -186,13 +186,13 @@ mod cli { } mod ping { + use anyhow::{Context, Result}; use futures_util::FutureExt; use iroh::{endpoint::ZeroRttStatus, Endpoint}; use irpc::{channel::oneshot, rpc::RemoteService, rpc_requests, Client, WithChannels}; use irpc_iroh::{ Iroh0RttProtocol, IrohProtocol, IrohRemoteConnection, IrohZrttRemoteConnection, }; - use n0_error::{Result, StdResultExt}; use n0_future::future; use serde::{Deserialize, Serialize}; use tracing::info; @@ -227,7 +227,7 @@ mod ping { let local = self .inner .as_local() - .std_context("can not listen on remote service")?; + .context("can not listen on remote service")?; Ok(Iroh0RttProtocol::new(EchoProtocol::remote_handler(local))) } @@ -235,7 +235,7 @@ mod ping { let local = self .inner .as_local() - .std_context("can not listen on remote service")?; + .context("can not listen on remote service")?; Ok(IrohProtocol::new(EchoProtocol::remote_handler(local))) } @@ -246,7 +246,7 @@ mod ping { let conn = endpoint .connect(addr, Self::ALPN) .await - .std_context("failed to connect to remote service")?; + .context("failed to connect to remote service")?; let fut: future::Boxed = Box::pin(async { true }); Ok(EchoApi { inner: Client::boxed(IrohRemoteConnection::new(conn)), @@ -261,7 +261,7 @@ mod ping { let connecting = endpoint .connect_with_opts(addr, Self::ALPN, Default::default()) .await - .std_context("failed to connect to remote service")?; + .context("failed to connect to remote service")?; match connecting.into_0rtt() { Ok(conn) => { info!("0-RTT possible from our side"); @@ -283,7 +283,7 @@ mod ping { Err(connecting) => { info!("0-RTT not possible from our side"); let fut: future::Boxed = Box::pin(async { true }); - let conn = connecting.await.anyerr()?; + let conn = connecting.await?; Ok(EchoApi { inner: Client::boxed(IrohRemoteConnection::new(conn)), zero_rtt_accepted: fut.shared(), diff --git a/irpc-iroh/examples/auth.rs b/irpc-iroh/examples/auth.rs index 45fd7d7..13f1af1 100644 --- a/irpc-iroh/examples/auth.rs +++ b/irpc-iroh/examples/auth.rs @@ -3,8 +3,8 @@ //! * Manually implementing the connection loop //! * Authenticating peers +use anyhow::Result; use iroh::{protocol::Router, Endpoint}; -use n0_error::Result; use self::storage::{StorageClient, StorageServer}; @@ -65,6 +65,7 @@ mod storage { sync::{Arc, Mutex}, }; + use anyhow::Result; use iroh::{ endpoint::Connection, protocol::{AcceptError, ProtocolHandler}, @@ -76,7 +77,6 @@ mod storage { }; // Import the macro use irpc_iroh::{read_request, IrohLazyRemoteConnection}; - use n0_error::{AnyError, Result}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -231,13 +231,13 @@ mod storage { } } - pub async fn auth(&self, token: &str) -> Result<()> { + pub async fn auth(&self, token: &str) -> Result<(), anyhow::Error> { self.inner .rpc(Auth { token: token.to_string(), }) .await? - .map_err(AnyError::from_string) + .map_err(|err| anyhow::anyhow!(err)) } pub async fn get(&self, key: String) -> Result, irpc::Error> { diff --git a/irpc-iroh/examples/derive.rs b/irpc-iroh/examples/derive.rs index eb8ad56..9ae06fc 100644 --- a/irpc-iroh/examples/derive.rs +++ b/irpc-iroh/examples/derive.rs @@ -1,5 +1,5 @@ +use anyhow::Result; use iroh::{protocol::Router, Endpoint}; -use n0_error::Result; use self::storage::StorageApi; @@ -57,6 +57,7 @@ mod storage { use std::collections::BTreeMap; + use anyhow::{Context, Result}; use iroh::{protocol::ProtocolHandler, Endpoint}; use irpc::{ channel::{mpsc, oneshot}, @@ -65,7 +66,6 @@ mod storage { }; // Import the macro use irpc_iroh::{IrohLazyRemoteConnection, IrohProtocol}; - use n0_error::{Result, StdResultExt}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -171,7 +171,7 @@ mod storage { let local = self .inner .as_local() - .std_context("can not listen on remote service")?; + .context("can not listen on remote service")?; Ok(IrohProtocol::new(StorageProtocol::remote_handler(local))) } diff --git a/irpc-iroh/examples/simple.rs b/irpc-iroh/examples/simple.rs index ed55637..d08cab4 100644 --- a/irpc-iroh/examples/simple.rs +++ b/irpc-iroh/examples/simple.rs @@ -1,15 +1,15 @@ #[tokio::main] -async fn main() -> n0_error::Result<()> { +async fn main() -> anyhow::Result<()> { cli::run().await } mod proto { use std::collections::HashMap; + use anyhow::Result; use iroh::{protocol::Router, Endpoint, EndpointId}; use irpc::{channel::oneshot, rpc_requests, Client, WithChannels}; use irpc_iroh::IrohProtocol; - use n0_error::{Result, StdResultExt}; use serde::{Deserialize, Serialize}; const ALPN: &[u8] = b"iroh-irpc/simple/1"; @@ -44,7 +44,7 @@ mod proto { println!("endpoint id: {}", router.endpoint().id()); tokio::signal::ctrl_c().await?; - router.shutdown().await.anyerr()?; + router.shutdown().await?; Ok(()) } @@ -83,9 +83,9 @@ mod proto { } mod cli { + use anyhow::Result; use clap::Parser; use iroh::EndpointId; - use n0_error::Result; use crate::proto::{connect, listen, GetRequest, SetRequest}; From 5975600b2acb090f1cb0a235ef45227f66deb1c0 Mon Sep 17 00:00:00 2001 From: Franz Heinzmann Date: Thu, 6 Nov 2025 01:32:04 +0100 Subject: [PATCH 7/9] refactor: move 0rtt status into RemoteConnection trait (#82) Based on #81 With the 0rtt status being accessible from the connection directly, we can just move this into the `RemoteConnection` abstraction, making working with 0rtt requests simpler for implementors (see changes to the 0rtt example!). --- Cargo.lock | 227 +++++++++++++++++-------------------- Cargo.toml | 4 - irpc-iroh/examples/0rtt.rs | 26 +---- irpc-iroh/src/lib.rs | 20 ++++ src/lib.rs | 44 ++++--- 5 files changed, 154 insertions(+), 167 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0a906e..25b689b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -257,9 +257,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.41" +version = "1.2.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" dependencies = [ "find-msvc-tools", "shlex", @@ -321,9 +321,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.50" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -331,9 +331,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.50" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -561,9 +561,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] @@ -651,9 +651,9 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -1198,9 +1198,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -1211,9 +1211,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -1224,11 +1224,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -1239,42 +1238,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -1375,9 +1370,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -1385,8 +1380,9 @@ dependencies = [ [[package]] name = "iroh" -version = "0.95.0" -source = "git+https://github.com/n0-computer/iroh.git?branch=Frando/0rtt-api#3ceb8a2d18c9f8d1c76b46ade0293136c20e3bba" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2374ba3cdaac152dc6ada92d971f7328e6408286faab3b7350842b2ebbed4789" dependencies = [ "aead", "backon", @@ -1439,8 +1435,9 @@ dependencies = [ [[package]] name = "iroh-base" -version = "0.95.0" -source = "git+https://github.com/n0-computer/iroh.git?branch=Frando/0rtt-api#3ceb8a2d18c9f8d1c76b46ade0293136c20e3bba" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a8c5fb1cc65589f0d7ab44269a76f615a8c4458356952c9b0ef1c93ea45ff8" dependencies = [ "curve25519-dalek", "data-encoding", @@ -1537,8 +1534,9 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "0.95.0" -source = "git+https://github.com/n0-computer/iroh.git?branch=Frando/0rtt-api#3ceb8a2d18c9f8d1c76b46ade0293136c20e3bba" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43fbdf2aeffa7d6ede1a31f6570866c2199b1cee96a0b563994623795d1bac2c" dependencies = [ "blake3", "bytes", @@ -1641,9 +1639,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" @@ -1675,9 +1673,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", @@ -1697,15 +1695,15 @@ checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" @@ -2027,9 +2025,9 @@ dependencies = [ [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl-probe" @@ -2253,9 +2251,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -2286,9 +2284,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -2539,9 +2537,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.33" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "log", "once_cell", @@ -2566,9 +2564,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "web-time", "zeroize", @@ -2603,9 +2601,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.7" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -2689,9 +2687,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" +checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" [[package]] name = "semver" @@ -2843,9 +2841,9 @@ dependencies = [ [[package]] name = "signature" -version = "3.0.0-rc.4" +version = "3.0.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc280a6ff65c79fbd6622f64d7127f32b85563bca8c53cd2e9141d6744a9056d" +checksum = "2a0251c9d6468f4ba853b6352b190fb7c1e405087779917c238445eb03993826" [[package]] name = "simdutf8" @@ -2971,9 +2969,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.107" +version = "2.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" +checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" dependencies = [ "proc-macro2", "quote", @@ -3029,9 +3027,9 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "target-triple" -version = "0.1.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" +checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" [[package]] name = "termcolor" @@ -3125,9 +3123,9 @@ checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -3200,9 +3198,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -3400,9 +3398,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.112" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d66678374d835fe847e0dc8348fde2ceb5be4a7ec204437d8367f0d8df266a5" +checksum = "559b6a626c0815c942ac98d434746138b4f89ddd6a1b8cbb168c6845fb3376c5" dependencies = [ "glob", "serde", @@ -3421,9 +3419,9 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-xid" @@ -3524,9 +3522,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", @@ -3535,25 +3533,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -3564,9 +3548,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3574,22 +3558,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] @@ -3609,9 +3593,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -3633,23 +3617,23 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.3", + "webpki-root-certs 1.0.4", ] [[package]] name = "webpki-root-certs" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d651ec480de84b762e7be71e6efa7461699c19d9e2c272c8d93455f567786e" +checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] @@ -4229,9 +4213,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "ws_stream_wasm" @@ -4254,9 +4238,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.27" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "xmltree" @@ -4278,11 +4262,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -4290,9 +4273,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", @@ -4369,9 +4352,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -4380,9 +4363,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -4391,9 +4374,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 554d45a..604542e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,7 +113,3 @@ iroh = { version = "0.95" } iroh-base = { version = "0.95" } quinn = { package = "iroh-quinn", version = "0.14.0", default-features = false } futures-util = { version = "0.3", features = ["sink"] } - -[patch.crates-io] -iroh = { git = "https://github.com/n0-computer/iroh.git", branch = "Frando/0rtt-api" } -iroh-base = { git = "https://github.com/n0-computer/iroh.git", branch = "Frando/0rtt-api" } diff --git a/irpc-iroh/examples/0rtt.rs b/irpc-iroh/examples/0rtt.rs index 3dc8342..1bfeef1 100644 --- a/irpc-iroh/examples/0rtt.rs +++ b/irpc-iroh/examples/0rtt.rs @@ -187,13 +187,11 @@ mod cli { mod ping { use anyhow::{Context, Result}; - use futures_util::FutureExt; - use iroh::{endpoint::ZeroRttStatus, Endpoint}; + use iroh::Endpoint; use irpc::{channel::oneshot, rpc::RemoteService, rpc_requests, Client, WithChannels}; use irpc_iroh::{ Iroh0RttProtocol, IrohProtocol, IrohRemoteConnection, IrohZrttRemoteConnection, }; - use n0_future::future; use serde::{Deserialize, Serialize}; use tracing::info; @@ -207,7 +205,6 @@ mod ping { pub struct EchoApi { inner: Client, - zero_rtt_accepted: futures_util::future::Shared>, } impl EchoApi { @@ -218,9 +215,7 @@ mod ping { } pub async fn echo_0rtt(&self, data: Vec) -> irpc::Result> { - self.inner - .rpc_0rtt(Echo { data }, self.zero_rtt_accepted.clone()) - .await + self.inner.rpc_0rtt(Echo { data }).await } pub fn expose_0rtt(self) -> Result> { @@ -247,10 +242,8 @@ mod ping { .connect(addr, Self::ALPN) .await .context("failed to connect to remote service")?; - let fut: future::Boxed = Box::pin(async { true }); Ok(EchoApi { inner: Client::boxed(IrohRemoteConnection::new(conn)), - zero_rtt_accepted: fut.shared(), }) } @@ -265,28 +258,15 @@ mod ping { match connecting.into_0rtt() { Ok(conn) => { info!("0-RTT possible from our side"); - let fut: future::Boxed = Box::pin({ - let conn = conn.clone(); - async move { - match conn.handshake_completed().await { - Err(_) => false, - Ok(ZeroRttStatus::Accepted(_)) => true, - Ok(ZeroRttStatus::Rejected(_)) => false, - } - } - }); Ok(EchoApi { inner: Client::boxed(IrohZrttRemoteConnection::new(conn)), - zero_rtt_accepted: fut.shared(), }) } Err(connecting) => { info!("0-RTT not possible from our side"); - let fut: future::Boxed = Box::pin(async { true }); let conn = connecting.await?; Ok(EchoApi { inner: Client::boxed(IrohRemoteConnection::new(conn)), - zero_rtt_accepted: fut.shared(), }) } } @@ -306,10 +286,8 @@ mod ping { let (tx, rx) = tokio::sync::mpsc::channel(1); let actor = Self { recv: rx }; n0_future::task::spawn(actor.run()); - let fut: future::Boxed = Box::pin(async { true }); EchoApi { inner: Client::local(tx), - zero_rtt_accepted: fut.shared(), } } diff --git a/irpc-iroh/src/lib.rs b/irpc-iroh/src/lib.rs index 8deb25b..a7b695f 100644 --- a/irpc-iroh/src/lib.rs +++ b/irpc-iroh/src/lib.rs @@ -9,6 +9,7 @@ use iroh::{ endpoint::{ Accepting, ConnectingError, Connection, ConnectionError, IncomingZeroRttConnection, OutgoingZeroRttConnection, RecvStream, RemoteEndpointIdError, SendStream, VarInt, + ZeroRttStatus, }, protocol::{AcceptError, ProtocolHandler}, EndpointId, @@ -67,6 +68,10 @@ impl irpc::rpc::RemoteConnection for IrohRemoteConnection { Ok((send, recv)) }) } + + fn zero_rtt_accepted(&self) -> BoxFuture { + Box::pin(async { true }) + } } #[derive(Debug, Clone)] @@ -93,6 +98,17 @@ impl irpc::rpc::RemoteConnection for IrohZrttRemoteConnection { Ok((send, recv)) }) } + + fn zero_rtt_accepted(&self) -> BoxFuture { + let conn = self.0.clone(); + Box::pin(async move { + match conn.handshake_completed().await { + Err(_) => false, + Ok(ZeroRttStatus::Accepted(_)) => true, + Ok(ZeroRttStatus::Rejected(_)) => false, + } + }) + } } /// A connection to a remote service. @@ -148,6 +164,10 @@ impl RemoteConnection for IrohLazyRemoteConnection { Ok(pair) }) } + + fn zero_rtt_accepted(&self) -> BoxFuture { + Box::pin(async { true }) + } } async fn connect_and_open_bi( diff --git a/src/lib.rs b/src/lib.rs index a278331..7f3469f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1541,11 +1541,7 @@ impl Client { /// Compared to [Self::notify], this variant takes a future that returns true /// if 0rtt has been accepted. If not, the data is sent again via the same /// remote channel. For local requests, the future is ignored. - pub fn notify_0rtt( - &self, - msg: Req, - zero_rtt_accepted: impl Future + Send + 'static, - ) -> impl Future> + Send + 'static + pub fn notify_0rtt(&self, msg: Req) -> impl Future> + Send + 'static where S: From, S::Message: From>, @@ -1556,7 +1552,6 @@ impl Client { match this.request().await? { Request::Local(request) => { request.send((msg,)).await?; - zero_rtt_accepted.await; } #[cfg(not(feature = "rpc"))] Request::Remote(_request) => unreachable!(), @@ -1565,7 +1560,7 @@ impl Client { // see https://www.iroh.computer/blog/0rtt-api#connect-side let buf = rpc::prepare_write::(msg)?; let (_tx, _rx) = request.write_raw(&buf).await?; - if !zero_rtt_accepted.await { + if !this.0.zero_rtt_accepted().await { // 0rtt was not accepted, the data is lost, send it again! let Request::Remote(request) = this.request().await? else { unreachable!() @@ -1583,11 +1578,7 @@ impl Client { /// Compared to [Self::rpc], this variant takes a future that returns true /// if 0rtt has been accepted. If not, the data is sent again via the same /// remote channel. For local requests, the future is ignored. - pub fn rpc_0rtt( - &self, - msg: Req, - zero_rtt_accepted: impl Future + Send + 'static, - ) -> impl Future> + Send + 'static + pub fn rpc_0rtt(&self, msg: Req) -> impl Future> + Send + 'static where S: From, S::Message: From>, @@ -1600,7 +1591,6 @@ impl Client { Request::Local(request) => { let (tx, rx) = oneshot::channel(); request.send((msg, tx)).await?; - zero_rtt_accepted.await; rx } #[cfg(not(feature = "rpc"))] @@ -1610,7 +1600,7 @@ impl Client { // see https://www.iroh.computer/blog/0rtt-api#connect-side let buf = rpc::prepare_write::(msg)?; let (_tx, rx) = request.write_raw(&buf).await?; - if zero_rtt_accepted.await { + if this.0.zero_rtt_accepted().await { rx } else { // 0rtt was not accepted, the data is lost, send it again! @@ -1637,7 +1627,6 @@ impl Client { &self, msg: Req, local_response_cap: usize, - zero_rtt_accepted: impl Future + Send + 'static, ) -> impl Future>> + Send + 'static where S: From, @@ -1651,7 +1640,6 @@ impl Client { Request::Local(request) => { let (tx, rx) = mpsc::channel(local_response_cap); request.send((msg, tx)).await?; - zero_rtt_accepted.await; rx } #[cfg(not(feature = "rpc"))] @@ -1661,7 +1649,7 @@ impl Client { // see https://www.iroh.computer/blog/0rtt-api#connect-side let buf = rpc::prepare_write::(msg)?; let (_tx, rx) = request.write_raw(&buf).await?; - if zero_rtt_accepted.await { + if this.0.zero_rtt_accepted().await { rx } else { // 0rtt was not accepted, the data is lost, send it again! @@ -1703,6 +1691,15 @@ impl Clone for ClientInner { } } +impl ClientInner { + async fn zero_rtt_accepted(&self) -> bool { + match self { + ClientInner::Local(_sender) => true, + ClientInner::Remote(remote_connection) => remote_connection.zero_rtt_accepted().await, + } + } +} + /// Error when opening a request. When cross-process rpc is disabled, this is /// an empty enum since local requests can not fail. #[stack_error(derive, add_meta, from_sources)] @@ -1929,6 +1926,11 @@ pub mod rpc { fn open_bi( &self, ) -> BoxFuture>; + + /// Returns whether 0-RTT data was accepted by the server. + /// + /// For connections that were fully authenticated before allowing to send any data, this should return `true`. + fn zero_rtt_accepted(&self) -> BoxFuture; } /// A connection to a remote service. @@ -1960,6 +1962,10 @@ pub mod rpc { Ok(pair) }) } + + fn zero_rtt_accepted(&self) -> BoxFuture { + Box::pin(async { true }) + } } impl QuinnLazyRemoteConnection { @@ -2001,6 +2007,10 @@ pub mod rpc { Ok(pair) }) } + + fn zero_rtt_accepted(&self) -> BoxFuture { + Box::pin(async { true }) + } } async fn connect_and_open_bi( From aa785df6b8d621dd7ef7b4d0ac843597ffbd8c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Wed, 5 Nov 2025 19:49:29 -0500 Subject: [PATCH 8/9] fix: add back cfg that got lost in rebase --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 7f3469f..912ed3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1695,7 +1695,10 @@ impl ClientInner { async fn zero_rtt_accepted(&self) -> bool { match self { ClientInner::Local(_sender) => true, + #[cfg(feature = "rpc")] ClientInner::Remote(remote_connection) => remote_connection.zero_rtt_accepted().await, + #[cfg(not(feature = "rpc"))] + Self::Remote(_) => unreachable!(), } } } From 28ab3fb3345550fc34f13e3d93be35c5f641dc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Wed, 5 Nov 2025 19:52:53 -0500 Subject: [PATCH 9/9] fix: more fixing cfg errors --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 912ed3e..7494c3c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1692,6 +1692,7 @@ impl Clone for ClientInner { } impl ClientInner { + #[allow(dead_code)] async fn zero_rtt_accepted(&self) -> bool { match self { ClientInner::Local(_sender) => true,