1
1
#![ cfg( feature = "test" ) ]
2
2
use std:: { net:: SocketAddr , path:: PathBuf , sync:: Arc } ;
3
3
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 ;
6
6
use quinn:: {
7
7
crypto:: rustls:: { QuicClientConfig , QuicServerConfig } ,
8
8
rustls, ClientConfig , Endpoint , ServerConfig ,
9
9
} ;
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 > ;
10
17
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
+ }
11
41
12
42
/// Returns default server configuration along with its certificate.
13
43
#[ 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 ) > {
15
45
let cert = rcgen:: generate_simple_self_signed ( vec ! [ "localhost" . into( ) ] ) ?;
16
46
let cert_der = cert. cert . der ( ) ;
17
47
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>)> {
31
61
. unwrap ( )
32
62
. max_concurrent_uni_streams ( 0_u8 . into ( ) ) ;
33
63
34
- Ok ( ( server_config, cert_der . to_vec ( ) ) )
64
+ Ok ( ( server_config, cert ) )
35
65
}
36
66
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 ) > {
38
68
let ( server_config, server_cert) = configure_server ( ) ?;
39
69
let endpoint = Endpoint :: server ( server_config, bind_addr) ?;
40
70
Ok ( ( endpoint, server_cert) )
41
71
}
42
72
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
+
43
83
/// An iroh node that just has the blobs transport
44
84
#[ derive( Debug ) ]
45
85
pub struct Node {
46
86
pub router : iroh:: protocol:: Router ,
47
87
pub blobs : Blobs < iroh_blobs:: store:: fs:: Store > ,
48
- pub _local_pool : LocalPool ,
88
+ pub local_pool : LocalPool ,
89
+ pub rpc_task : AbortOnDropHandle < ( ) > ,
49
90
}
50
91
51
92
impl Node {
52
- pub async fn new ( path : PathBuf ) -> anyhow:: Result < Self > {
93
+ pub async fn new ( path : PathBuf ) -> anyhow:: Result < ( Self , SocketAddr , CertifiedKey ) > {
53
94
let store = iroh_blobs:: store:: fs:: Store :: load ( path) . await ?;
54
95
let local_pool = LocalPool :: default ( ) ;
55
96
let endpoint = iroh:: Endpoint :: builder ( ) . bind ( ) . await ?;
@@ -58,12 +99,42 @@ impl Node {
58
99
. accept ( iroh_blobs:: ALPN , blobs. clone ( ) )
59
100
. spawn ( )
60
101
. 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 {
64
112
router,
65
113
blobs,
66
- _local_pool : local_pool,
67
- } )
114
+ local_pool,
115
+ rpc_task,
116
+ } ;
117
+ Ok ( ( node, local_addr, key) )
68
118
}
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