1- use std:: sync:: Arc ;
2- use std:: time:: SystemTime ;
1+ use std:: { path:: PathBuf , sync:: Arc } ;
32
43use futures:: future;
5- use h3_quinn:: quinn;
6- use rustls:: { self , client:: ServerCertVerified } ;
7- use rustls:: { Certificate , ServerName } ;
84use structopt:: StructOpt ;
9- use tokio:: { self , io:: AsyncWriteExt } ;
5+ use tokio:: io:: AsyncWriteExt ;
6+ use tracing:: { error, info} ;
107
11- use h3_quinn:: { self , quinn:: crypto :: rustls :: Error } ;
8+ use h3_quinn:: quinn;
129
1310static ALPN : & [ u8 ] = b"h3" ;
1411
1512#[ derive( StructOpt , Debug ) ]
1613#[ structopt( name = "server" ) ]
1714struct Opt {
18- #[ structopt( long) ]
19- pub insecure : bool ,
15+ #[ structopt(
16+ long,
17+ short,
18+ default_value = "examples/ca.cert" ,
19+ help = "Certificate of CA who issues the server certificate"
20+ ) ]
21+ pub ca : PathBuf ,
2022
2123 #[ structopt( name = "keylogfile" , long) ]
2224 pub key_log_file : bool ,
@@ -31,76 +33,86 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
3133 . with_env_filter ( tracing_subscriber:: EnvFilter :: from_default_env ( ) )
3234 . with_span_events ( tracing_subscriber:: fmt:: format:: FmtSpan :: FULL )
3335 . with_writer ( std:: io:: stderr)
36+ . with_max_level ( tracing:: Level :: INFO )
3437 . init ( ) ;
3538
3639 let opt = Opt :: from_args ( ) ;
3740
38- let dest = opt . uri . parse :: < http :: Uri > ( ) ? ;
41+ // DNS lookup
3942
40- if dest. scheme ( ) != Some ( & http:: uri:: Scheme :: HTTPS ) {
41- Err ( "destination scheme must be 'https'" ) ?;
43+ let uri = opt. uri . parse :: < http:: Uri > ( ) ?;
44+
45+ if uri. scheme ( ) != Some ( & http:: uri:: Scheme :: HTTPS ) {
46+ Err ( "uri scheme must be 'https'" ) ?;
4247 }
4348
44- let auth = dest
45- . authority ( )
46- . ok_or ( "destination must have a host" ) ?
47- . clone ( ) ;
49+ let auth = uri. authority ( ) . ok_or ( "uri must have a host" ) ?. clone ( ) ;
4850
4951 let port = auth. port_u16 ( ) . unwrap_or ( 443 ) ;
5052
51- // dns me!
5253 let addr = tokio:: net:: lookup_host ( ( auth. host ( ) , port) )
5354 . await ?
5455 . next ( )
5556 . ok_or ( "dns found no addresses" ) ?;
5657
57- eprintln ! ( "DNS Lookup for {:?}: {:?}" , dest , addr) ;
58+ info ! ( "DNS lookup for {:?}: {:?}" , uri , addr) ;
5859
59- // quinn setup
60- let tls_config_builder = rustls:: ClientConfig :: builder ( )
61- . with_safe_default_cipher_suites ( )
62- . with_safe_default_kx_groups ( )
63- . with_protocol_versions ( & [ & rustls:: version:: TLS13 ] ) ?;
64- let mut tls_config = if !opt. insecure {
65- let mut roots = rustls:: RootCertStore :: empty ( ) ;
66- match rustls_native_certs:: load_native_certs ( ) {
67- Ok ( certs) => {
68- for cert in certs {
69- if let Err ( e) = roots. add ( & rustls:: Certificate ( cert. 0 ) ) {
70- eprintln ! ( "failed to parse trust anchor: {}" , e) ;
71- }
60+ // create quinn client endpoint
61+
62+ // load CA certificates stored in the system
63+ let mut roots = rustls:: RootCertStore :: empty ( ) ;
64+ match rustls_native_certs:: load_native_certs ( ) {
65+ Ok ( certs) => {
66+ for cert in certs {
67+ if let Err ( e) = roots. add ( & rustls:: Certificate ( cert. 0 ) ) {
68+ error ! ( "failed to parse trust anchor: {}" , e) ;
7269 }
7370 }
74- Err ( e) => {
75- eprintln ! ( "couldn't load any default trust roots: {}" , e) ;
76- }
77- } ;
78- tls_config_builder
79- . with_root_certificates ( roots)
80- . with_no_client_auth ( )
81- } else {
82- tls_config_builder
83- . with_custom_certificate_verifier ( Arc :: new ( YesVerifier ) )
84- . with_no_client_auth ( )
71+ }
72+ Err ( e) => {
73+ error ! ( "couldn't load any default trust roots: {}" , e) ;
74+ }
8575 } ;
76+
77+ // load certificate of CA who issues the server certificate
78+ // NOTE that this should be used for dev only
79+ if let Err ( e) = roots. add ( & rustls:: Certificate ( std:: fs:: read ( opt. ca ) ?) ) {
80+ error ! ( "failed to parse trust anchor: {}" , e) ;
81+ }
82+
83+ let mut tls_config = rustls:: ClientConfig :: builder ( )
84+ . with_safe_default_cipher_suites ( )
85+ . with_safe_default_kx_groups ( )
86+ . with_protocol_versions ( & [ & rustls:: version:: TLS13 ] ) ?
87+ . with_root_certificates ( roots)
88+ . with_no_client_auth ( ) ;
89+
8690 tls_config. enable_early_data = true ;
8791 tls_config. alpn_protocols = vec ! [ ALPN . into( ) ] ;
8892
93+ // optional debugging support
8994 if opt. key_log_file {
9095 // Write all Keys to a file if SSLKEYLOGFILE is set
9196 // WARNING, we enable this for the example, you should think carefully about enabling in your own code
9297 tls_config. key_log = Arc :: new ( rustls:: KeyLogFile :: new ( ) ) ;
9398 }
9499
95- let client_config = quinn:: ClientConfig :: new ( Arc :: new ( tls_config) ) ;
96-
97100 let mut client_endpoint = h3_quinn:: quinn:: Endpoint :: client ( "[::]:0" . parse ( ) . unwrap ( ) ) ?;
101+
102+ let client_config = quinn:: ClientConfig :: new ( Arc :: new ( tls_config) ) ;
98103 client_endpoint. set_default_client_config ( client_config) ;
99- let quinn_conn = h3_quinn:: Connection :: new ( client_endpoint. connect ( addr, auth. host ( ) ) ?. await ?) ;
100104
101- eprintln ! ( "QUIC connected ..." ) ;
105+ let conn = client_endpoint. connect ( addr, auth. host ( ) ) ?. await ?;
106+
107+ info ! ( "QUIC connection established" ) ;
108+
109+ // create h3 client
110+
111+ // h3 is designed to work with different QUIC implementations via
112+ // a generic interface, that is, the [`quic::Connection`] trait.
113+ // h3_quinn implements the trait w/ quinn to make it work with h3.
114+ let quinn_conn = h3_quinn:: Connection :: new ( conn) ;
102115
103- // generic h3
104116 let ( mut driver, mut send_request) = h3:: client:: new ( quinn_conn) . await ?;
105117
106118 let drive = async move {
@@ -115,48 +127,41 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
115127 // So we "move" it.
116128 // vvvv
117129 let request = async move {
118- eprintln ! ( "Sending request ..." ) ;
130+ info ! ( "sending request ..." ) ;
119131
120- let req = http:: Request :: builder ( ) . uri ( dest ) . body ( ( ) ) ?;
132+ let req = http:: Request :: builder ( ) . uri ( uri ) . body ( ( ) ) ?;
121133
134+ // sending request results in a bidirectional stream,
135+ // which is also used for receiving response
122136 let mut stream = send_request. send_request ( req) . await ?;
137+
138+ // finish on the sending side
123139 stream. finish ( ) . await ?;
124140
125- eprintln ! ( "Receiving response ..." ) ;
141+ info ! ( "receiving response ..." ) ;
142+
126143 let resp = stream. recv_response ( ) . await ?;
127144
128- eprintln ! ( "Response : {:?} {}" , resp. version( ) , resp. status( ) ) ;
129- eprintln ! ( "Headers : {:#?}" , resp. headers( ) ) ;
145+ info ! ( "response : {:?} {}" , resp. version( ) , resp. status( ) ) ;
146+ info ! ( "headers : {:#?}" , resp. headers( ) ) ;
130147
148+ // `recv_data()` must be called after `recv_response()` for
149+ // receiving potential response body
131150 while let Some ( mut chunk) = stream. recv_data ( ) . await ? {
132151 let mut out = tokio:: io:: stdout ( ) ;
133152 out. write_all_buf ( & mut chunk) . await ?;
134153 out. flush ( ) . await ?;
135154 }
155+
136156 Ok :: < _ , Box < dyn std:: error:: Error > > ( ( ) )
137157 } ;
138158
139159 let ( req_res, drive_res) = tokio:: join!( request, drive) ;
140160 req_res?;
141161 drive_res?;
142162
163+ // wait for the connection to be closed before exiting
143164 client_endpoint. wait_idle ( ) . await ;
144165
145166 Ok ( ( ) )
146167}
147-
148- struct YesVerifier ;
149-
150- impl rustls:: client:: ServerCertVerifier for YesVerifier {
151- fn verify_server_cert (
152- & self ,
153- _end_entity : & Certificate ,
154- _intermediates : & [ Certificate ] ,
155- _server_name : & ServerName ,
156- _scts : & mut dyn Iterator < Item = & [ u8 ] > ,
157- _ocsp_response : & [ u8 ] ,
158- _now : SystemTime ,
159- ) -> Result < ServerCertVerified , Error > {
160- Ok ( ServerCertVerified :: assertion ( ) )
161- }
162- }
0 commit comments