11#![ cfg( feature = "test" ) ]
22use 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 ;
66use 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 ) ]
4585pub 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
5192impl 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