diff --git a/iroh-cli/src/commands.rs b/iroh-cli/src/commands.rs index bd1bc0d18af..a9e8d44cdf3 100644 --- a/iroh-cli/src/commands.rs +++ b/iroh-cli/src/commands.rs @@ -130,7 +130,7 @@ impl Cli { .await } else { crate::logging::init_terminal_logging()?; - let iroh = Iroh::connect(data_dir).await.context("rpc connect")?; + let iroh = Iroh::connect_path(data_dir).await.context("rpc connect")?; let env = ConsoleEnv::for_console(data_dir_owned, &iroh).await?; console::run(&iroh, &env).await } @@ -151,7 +151,7 @@ impl Cli { .await } else { crate::logging::init_terminal_logging()?; - let iroh = Iroh::connect(data_dir).await.context("rpc connect")?; + let iroh = Iroh::connect_path(data_dir).await.context("rpc connect")?; let env = ConsoleEnv::for_cli(data_dir_owned, &iroh).await?; command.run(&iroh, &env).await } diff --git a/iroh-cli/src/commands/doc.rs b/iroh-cli/src/commands/doc.rs index d26f7859127..373ad6b1cb9 100644 --- a/iroh-cli/src/commands/doc.rs +++ b/iroh-cli/src/commands/doc.rs @@ -958,7 +958,7 @@ mod tests { let cli = ConsoleEnv::for_console(data_dir.path().to_owned(), &node) .await .context("ConsoleEnv")?; - let iroh = iroh::client::Iroh::connect(data_dir.path()) + let iroh = iroh::client::Iroh::connect_path(data_dir.path()) .await .context("rpc connect")?; diff --git a/iroh-cli/src/commands/node.rs b/iroh-cli/src/commands/node.rs index b85422069e0..17a3a6dee04 100644 --- a/iroh-cli/src/commands/node.rs +++ b/iroh-cli/src/commands/node.rs @@ -75,6 +75,9 @@ impl NodeCommands { println!("Listening addresses: {:#?}", response.listen_addrs); println!("Node public key: {}", response.addr.node_id); println!("Version: {}", response.version); + if let Some(port) = response.rpc_port { + println!("RPC Port: {}", port); + } } } Ok(()) diff --git a/iroh/src/client/node.rs b/iroh/src/client/node.rs index 96ce9d87c22..7091c78a49f 100644 --- a/iroh/src/client/node.rs +++ b/iroh/src/client/node.rs @@ -81,4 +81,6 @@ pub struct NodeStatus { pub listen_addrs: Vec, /// The version of the node pub version: String, + /// RPC port, if currently listening. + pub rpc_port: Option, } diff --git a/iroh/src/client/quic.rs b/iroh/src/client/quic.rs index 6351c9632d0..06ecb08f4af 100644 --- a/iroh/src/client/quic.rs +++ b/iroh/src/client/quic.rs @@ -25,23 +25,29 @@ pub type RpcClient = quic_rpc::RpcClient impl Iroh { /// Connect to an iroh node running on the same computer, but in a different process. - pub async fn connect(root: impl AsRef) -> anyhow::Result { + pub async fn connect_path(root: impl AsRef) -> anyhow::Result { let rpc_status = RpcStatus::load(root).await?; match rpc_status { RpcStatus::Stopped => { bail!("iroh is not running, please start it"); } - RpcStatus::Running { client, .. } => Ok(Iroh::new(client)), + RpcStatus::Running { client, port: _ } => Ok(Iroh::new(client)), } } + + /// Connect to an iroh node at the given RPC address. + pub async fn connect_addr(addr: SocketAddr) -> anyhow::Result { + let client = connect_raw(addr).await?; + Ok(Iroh::new(client)) + } } /// Create a raw RPC client to an iroh node running on the same computer, but in a different /// process. -pub(crate) async fn connect_raw(rpc_port: u16) -> anyhow::Result { +pub(crate) async fn connect_raw(addr: SocketAddr) -> anyhow::Result { let bind_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0).into(); let endpoint = create_quinn_client(bind_addr, vec![RPC_ALPN.to_vec()], false)?; - let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), rpc_port); + let server_name = "localhost".to_string(); let connection = QuinnConnection::::new(endpoint, addr, server_name); let connection = BoxedConnection::new(connection); diff --git a/iroh/src/node.rs b/iroh/src/node.rs index 91e6febc61e..43822fd4e19 100644 --- a/iroh/src/node.rs +++ b/iroh/src/node.rs @@ -58,6 +58,7 @@ pub struct Node { #[derive(derive_more::Debug)] struct NodeInner { db: D, + rpc_port: Option, docs: Option, endpoint: Endpoint, gossip: Gossip, @@ -150,6 +151,11 @@ impl Node { self.inner.endpoint.home_relay() } + /// Returns `Some(port)` if an RPC endpoint is running on this port. + pub fn my_rpc_port(&self) -> Option { + self.inner.rpc_port + } + /// Shutdown the node. /// /// This does not gracefully terminate currently: all connections are closed and diff --git a/iroh/src/node/builder.rs b/iroh/src/node/builder.rs index d2917eb2ca9..f9b3612824c 100644 --- a/iroh/src/node/builder.rs +++ b/iroh/src/node/builder.rs @@ -90,6 +90,7 @@ where bind_port: Option, secret_key: SecretKey, rpc_endpoint: E, + rpc_port: Option, blobs_store: D, keylog: bool, relay_mode: RelayMode, @@ -156,6 +157,7 @@ impl Default for Builder { relay_mode: RelayMode::Default, dns_resolver: None, rpc_endpoint: Default::default(), + rpc_port: None, gc_policy: GcPolicy::Disabled, docs_storage: DocsStorage::Memory, node_discovery: Default::default(), @@ -182,6 +184,7 @@ impl Builder { relay_mode: RelayMode::Default, dns_resolver: None, rpc_endpoint: Default::default(), + rpc_port: None, gc_policy: GcPolicy::Disabled, docs_storage, node_discovery: Default::default(), @@ -244,6 +247,7 @@ where blobs_store, keylog: self.keylog, rpc_endpoint: self.rpc_endpoint, + rpc_port: self.rpc_port, relay_mode: self.relay_mode, dns_resolver: self.dns_resolver, gc_policy: self.gc_policy, @@ -256,7 +260,11 @@ where } /// Configure rpc endpoint, changing the type of the builder to the new endpoint type. - pub fn rpc_endpoint>(self, value: E2) -> Builder { + pub fn rpc_endpoint>( + self, + value: E2, + port: Option, + ) -> Builder { // we can't use ..self here because the return type is different Builder { storage: self.storage, @@ -265,6 +273,7 @@ where blobs_store: self.blobs_store, keylog: self.keylog, rpc_endpoint: value, + rpc_port: port, relay_mode: self.relay_mode, dns_resolver: self.dns_resolver, gc_policy: self.gc_policy, @@ -291,6 +300,7 @@ where blobs_store: self.blobs_store, keylog: self.keylog, rpc_endpoint: ep, + rpc_port: Some(actual_rpc_port), relay_mode: self.relay_mode, dns_resolver: self.dns_resolver, gc_policy: self.gc_policy, @@ -501,6 +511,7 @@ where let client = crate::client::Iroh::new(quic_rpc::RpcClient::new(controller.clone())); let inner = Arc::new(NodeInner { + rpc_port: self.rpc_port, db: self.blobs_store, docs, endpoint, diff --git a/iroh/src/node/rpc.rs b/iroh/src/node/rpc.rs index 2c9b49fe383..b3d69ba2b3f 100644 --- a/iroh/src/node/rpc.rs +++ b/iroh/src/node/rpc.rs @@ -790,6 +790,7 @@ impl Handler { .await .unwrap_or_default(), version: env!("CARGO_PKG_VERSION").to_string(), + rpc_port: self.inner.rpc_port, }) } diff --git a/iroh/src/node/rpc_status.rs b/iroh/src/node/rpc_status.rs index 29f633eb81a..272c2b2f333 100644 --- a/iroh/src/node/rpc_status.rs +++ b/iroh/src/node/rpc_status.rs @@ -1,4 +1,7 @@ -use std::path::Path; +use std::{ + net::{Ipv4Addr, SocketAddr}, + path::Path, +}; use anyhow::{ensure, Context, Result}; use tokio::{fs, io::AsyncReadExt}; @@ -40,7 +43,8 @@ impl RpcStatus { .await .context("read rpc lock file")?; let running_rpc_port = u16::from_le_bytes(buffer); - if let Ok(client) = crate::client::quic_connect_raw(running_rpc_port).await { + let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), running_rpc_port); + if let Ok(client) = crate::client::quic_connect_raw(addr).await { return Ok(RpcStatus::Running { port: running_rpc_port, client,