1
- use std:: sync:: Arc ;
2
- use std:: time:: SystemTime ;
1
+ use std:: { path:: PathBuf , sync:: Arc } ;
3
2
4
3
use futures:: future;
5
- use h3_quinn:: quinn;
6
- use rustls:: { self , client:: ServerCertVerified } ;
7
- use rustls:: { Certificate , ServerName } ;
8
4
use structopt:: StructOpt ;
9
- use tokio:: { self , io:: AsyncWriteExt } ;
5
+ use tokio:: io:: AsyncWriteExt ;
6
+ use tracing:: { error, info} ;
10
7
11
- use h3_quinn:: { self , quinn:: crypto :: rustls :: Error } ;
8
+ use h3_quinn:: quinn;
12
9
13
10
static ALPN : & [ u8 ] = b"h3" ;
14
11
15
12
#[ derive( StructOpt , Debug ) ]
16
13
#[ structopt( name = "server" ) ]
17
14
struct 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 ,
20
22
21
23
#[ structopt( name = "keylogfile" , long) ]
22
24
pub key_log_file : bool ,
@@ -31,76 +33,86 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
31
33
. with_env_filter ( tracing_subscriber:: EnvFilter :: from_default_env ( ) )
32
34
. with_span_events ( tracing_subscriber:: fmt:: format:: FmtSpan :: FULL )
33
35
. with_writer ( std:: io:: stderr)
36
+ . with_max_level ( tracing:: Level :: INFO )
34
37
. init ( ) ;
35
38
36
39
let opt = Opt :: from_args ( ) ;
37
40
38
- let dest = opt . uri . parse :: < http :: Uri > ( ) ? ;
41
+ // DNS lookup
39
42
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'" ) ?;
42
47
}
43
48
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 ( ) ;
48
50
49
51
let port = auth. port_u16 ( ) . unwrap_or ( 443 ) ;
50
52
51
- // dns me!
52
53
let addr = tokio:: net:: lookup_host ( ( auth. host ( ) , port) )
53
54
. await ?
54
55
. next ( )
55
56
. ok_or ( "dns found no addresses" ) ?;
56
57
57
- eprintln ! ( "DNS Lookup for {:?}: {:?}" , dest , addr) ;
58
+ info ! ( "DNS lookup for {:?}: {:?}" , uri , addr) ;
58
59
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) ;
72
69
}
73
70
}
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
+ }
85
75
} ;
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
+
86
90
tls_config. enable_early_data = true ;
87
91
tls_config. alpn_protocols = vec ! [ ALPN . into( ) ] ;
88
92
93
+ // optional debugging support
89
94
if opt. key_log_file {
90
95
// Write all Keys to a file if SSLKEYLOGFILE is set
91
96
// WARNING, we enable this for the example, you should think carefully about enabling in your own code
92
97
tls_config. key_log = Arc :: new ( rustls:: KeyLogFile :: new ( ) ) ;
93
98
}
94
99
95
- let client_config = quinn:: ClientConfig :: new ( Arc :: new ( tls_config) ) ;
96
-
97
100
let mut client_endpoint = h3_quinn:: quinn:: Endpoint :: client ( "[::]:0" . parse ( ) . unwrap ( ) ) ?;
101
+
102
+ let client_config = quinn:: ClientConfig :: new ( Arc :: new ( tls_config) ) ;
98
103
client_endpoint. set_default_client_config ( client_config) ;
99
- let quinn_conn = h3_quinn:: Connection :: new ( client_endpoint. connect ( addr, auth. host ( ) ) ?. await ?) ;
100
104
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) ;
102
115
103
- // generic h3
104
116
let ( mut driver, mut send_request) = h3:: client:: new ( quinn_conn) . await ?;
105
117
106
118
let drive = async move {
@@ -115,48 +127,41 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
115
127
// So we "move" it.
116
128
// vvvv
117
129
let request = async move {
118
- eprintln ! ( "Sending request ..." ) ;
130
+ info ! ( "sending request ..." ) ;
119
131
120
- let req = http:: Request :: builder ( ) . uri ( dest ) . body ( ( ) ) ?;
132
+ let req = http:: Request :: builder ( ) . uri ( uri ) . body ( ( ) ) ?;
121
133
134
+ // sending request results in a bidirectional stream,
135
+ // which is also used for receiving response
122
136
let mut stream = send_request. send_request ( req) . await ?;
137
+
138
+ // finish on the sending side
123
139
stream. finish ( ) . await ?;
124
140
125
- eprintln ! ( "Receiving response ..." ) ;
141
+ info ! ( "receiving response ..." ) ;
142
+
126
143
let resp = stream. recv_response ( ) . await ?;
127
144
128
- eprintln ! ( "Response : {:?} {}" , resp. version( ) , resp. status( ) ) ;
129
- eprintln ! ( "Headers : {:#?}" , resp. headers( ) ) ;
145
+ info ! ( "response : {:?} {}" , resp. version( ) , resp. status( ) ) ;
146
+ info ! ( "headers : {:#?}" , resp. headers( ) ) ;
130
147
148
+ // `recv_data()` must be called after `recv_response()` for
149
+ // receiving potential response body
131
150
while let Some ( mut chunk) = stream. recv_data ( ) . await ? {
132
151
let mut out = tokio:: io:: stdout ( ) ;
133
152
out. write_all_buf ( & mut chunk) . await ?;
134
153
out. flush ( ) . await ?;
135
154
}
155
+
136
156
Ok :: < _ , Box < dyn std:: error:: Error > > ( ( ) )
137
157
} ;
138
158
139
159
let ( req_res, drive_res) = tokio:: join!( request, drive) ;
140
160
req_res?;
141
161
drive_res?;
142
162
163
+ // wait for the connection to be closed before exiting
143
164
client_endpoint. wait_idle ( ) . await ;
144
165
145
166
Ok ( ( ) )
146
167
}
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