Skip to content

Commit 3467681

Browse files
committed
Add a very basic test for quinn rpc
1 parent 12e14d7 commit 3467681

File tree

4 files changed

+187
-15
lines changed

4 files changed

+187
-15
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/target
22
iroh.config.toml
3+
.vscode/*

Cargo.lock

Lines changed: 100 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ example-iroh = [
117117
"dep:console",
118118
"iroh/discovery-local-network"
119119
]
120+
test = ["quic-rpc/quinn-transport"]
120121

121122
[package.metadata.docs.rs]
122123
all-features = true
@@ -183,3 +184,4 @@ incremental = false
183184
[patch.crates-io]
184185
iroh-base = { git = "https://github.com/n0-computer/iroh" }
185186
iroh = { git = "https://github.com/n0-computer/iroh" }
187+
quic-rpc = { git = "https://github.com/n0-computer/quic-rpc" }

tests/rpc.rs

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,47 @@
11
#![cfg(feature = "test")]
22
use std::{net::SocketAddr, path::PathBuf, sync::Arc};
33

4-
use iroh_blobs::{net_protocol::Blobs, util::local_pool::{self, LocalPool}};
5-
4+
use iroh_blobs::{net_protocol::Blobs, util::local_pool::LocalPool};
5+
use quic_rpc::transport::quinn::QuinnConnector;
66
use quinn::{
77
crypto::rustls::{QuicClientConfig, QuicServerConfig},
88
rustls, ClientConfig, Endpoint, ServerConfig,
99
};
10+
use rcgen::CertifiedKey;
11+
use tempfile::TempDir;
12+
use testresult::TestResult;
13+
use tokio_util::task::AbortOnDropHandle;
14+
15+
type QC = QuinnConnector<iroh_blobs::rpc::proto::Response, iroh_blobs::rpc::proto::Request>;
16+
type BlobsClient = iroh_blobs::rpc::client::blobs::Client<QC>;
1017

18+
/// Builds default quinn client config and trusts given certificates.
19+
///
20+
/// ## Args
21+
///
22+
/// - server_certs: a list of trusted certificates in DER format.
23+
fn configure_client(server_certs: &[CertifiedKey]) -> anyhow::Result<ClientConfig> {
24+
let mut certs = rustls::RootCertStore::empty();
25+
for cert in server_certs {
26+
let cert = cert.cert.der().clone();
27+
certs.add(cert)?;
28+
}
29+
30+
let crypto_client_config = rustls::ClientConfig::builder_with_provider(Arc::new(
31+
rustls::crypto::ring::default_provider(),
32+
))
33+
.with_protocol_versions(&[&rustls::version::TLS13])
34+
.expect("valid versions")
35+
.with_root_certificates(certs)
36+
.with_no_client_auth();
37+
let quic_client_config = QuicClientConfig::try_from(crypto_client_config)?;
38+
39+
Ok(ClientConfig::new(Arc::new(quic_client_config)))
40+
}
1141

1242
/// Returns default server configuration along with its certificate.
1343
#[allow(clippy::field_reassign_with_default)] // https://github.com/rust-lang/rust-clippy/issues/6527
14-
fn configure_server() -> anyhow::Result<(ServerConfig, Vec<u8>)> {
44+
fn configure_server() -> anyhow::Result<(ServerConfig, CertifiedKey)> {
1545
let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()])?;
1646
let cert_der = cert.cert.der();
1747
let priv_key = rustls::pki_types::PrivatePkcs8KeyDer::from(cert.key_pair.serialize_der());
@@ -31,25 +61,36 @@ fn configure_server() -> anyhow::Result<(ServerConfig, Vec<u8>)> {
3161
.unwrap()
3262
.max_concurrent_uni_streams(0_u8.into());
3363

34-
Ok((server_config, cert_der.to_vec()))
64+
Ok((server_config, cert))
3565
}
3666

37-
pub fn make_server_endpoint(bind_addr: SocketAddr) -> anyhow::Result<(Endpoint, Vec<u8>)> {
67+
pub fn make_server_endpoint(bind_addr: SocketAddr) -> anyhow::Result<(Endpoint, CertifiedKey)> {
3868
let (server_config, server_cert) = configure_server()?;
3969
let endpoint = Endpoint::server(server_config, bind_addr)?;
4070
Ok((endpoint, server_cert))
4171
}
4272

73+
pub fn make_client_endpoint(
74+
bind_addr: SocketAddr,
75+
server_certs: &[CertifiedKey],
76+
) -> anyhow::Result<Endpoint> {
77+
let client_cfg = configure_client(server_certs)?;
78+
let mut endpoint = Endpoint::client(bind_addr)?;
79+
endpoint.set_default_client_config(client_cfg);
80+
Ok(endpoint)
81+
}
82+
4383
/// An iroh node that just has the blobs transport
4484
#[derive(Debug)]
4585
pub struct Node {
4686
pub router: iroh::protocol::Router,
4787
pub blobs: Blobs<iroh_blobs::store::fs::Store>,
48-
pub _local_pool: LocalPool,
88+
pub local_pool: LocalPool,
89+
pub rpc_task: AbortOnDropHandle<()>,
4990
}
5091

5192
impl Node {
52-
pub async fn new(path: PathBuf) -> anyhow::Result<Self> {
93+
pub async fn new(path: PathBuf) -> anyhow::Result<(Self, SocketAddr, CertifiedKey)> {
5394
let store = iroh_blobs::store::fs::Store::load(path).await?;
5495
let local_pool = LocalPool::default();
5596
let endpoint = iroh::Endpoint::builder().bind().await?;
@@ -58,12 +99,42 @@ impl Node {
5899
.accept(iroh_blobs::ALPN, blobs.clone())
59100
.spawn()
60101
.await?;
61-
let endpoint = quinn::Endpoint::server(config, "0.0.0.0:12345".parse().unwrap())?;
62-
let rpc_server = quic_rpc::transport::quinn::QuinnListener::new(endpoint)
63-
Ok(Self {
102+
let (config, key) = configure_server()?;
103+
let endpoint = quinn::Endpoint::server(config, "127.0.0.1:0".parse().unwrap())?;
104+
let local_addr = endpoint.local_addr()?;
105+
let rpc_server = quic_rpc::transport::quinn::QuinnListener::new(endpoint)?;
106+
let rpc_server =
107+
quic_rpc::RpcServer::<iroh_blobs::rpc::proto::RpcService, _>::new(rpc_server);
108+
let blobs2 = blobs.clone();
109+
let rpc_task = rpc_server
110+
.spawn_accept_loop(move |msg, chan| blobs2.clone().handle_rpc_request(msg, chan));
111+
let node = Self {
64112
router,
65113
blobs,
66-
_local_pool: local_pool,
67-
})
114+
local_pool,
115+
rpc_task,
116+
};
117+
Ok((node, local_addr, key))
68118
}
69-
}
119+
}
120+
121+
async fn node_and_client() -> TestResult<(Node, BlobsClient, TempDir)> {
122+
let testdir = tempfile::tempdir()?;
123+
let (node, addr, key) = Node::new(testdir.path().join("blobs")).await?;
124+
let client = make_client_endpoint("127.0.0.1:0".parse().unwrap(), &[key])?;
125+
let client = QuinnConnector::new(client, addr, "localhost".to_string());
126+
let client = quic_rpc::RpcClient::<iroh_blobs::rpc::proto::RpcService, _>::new(client);
127+
let client = iroh_blobs::rpc::client::blobs::Client::new(client);
128+
Ok((node, client, testdir))
129+
}
130+
131+
#[tokio::test]
132+
async fn quinn_rpc_smoke() -> TestResult<()> {
133+
let _ = tracing_subscriber::fmt::try_init();
134+
let (node, client, _testdir) = node_and_client().await?;
135+
println!("Made a client");
136+
let hash = client.add_bytes(b"hello".to_vec()).await?;
137+
println!("Hash: {:?}", hash);
138+
drop(node);
139+
Ok(())
140+
}

0 commit comments

Comments
 (0)