1
1
#[ cfg( feature = "json-output" ) ]
2
- use std:: { fs :: File , path:: PathBuf } ;
2
+ use std:: path:: PathBuf ;
3
3
use std:: {
4
4
net:: { IpAddr , Ipv4Addr , Ipv6Addr , SocketAddr } ,
5
5
sync:: Arc ,
@@ -14,92 +14,60 @@ use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
14
14
use tokio:: sync:: Semaphore ;
15
15
use tracing:: { debug, error, info} ;
16
16
17
- use perf :: {
18
- CongestionAlgorithm , bind_socket ,
17
+ use crate :: {
18
+ CommonOpt , PERF_CIPHER_SUITES ,
19
19
noprotection:: NoProtectionClientConfig ,
20
+ parse_byte_size,
20
21
stats:: { OpenStreamStats , Stats } ,
21
22
} ;
22
23
23
24
/// Connects to a QUIC perf server and maintains a specified pattern of requests until interrupted
24
25
#[ derive( Parser ) ]
25
26
#[ clap( name = "client" ) ]
26
- struct Opt {
27
+ pub struct Opt {
27
28
/// Host to connect to
28
29
#[ clap( default_value = "localhost:4433" ) ]
29
30
host : String ,
30
31
/// Override DNS resolution for host
31
32
#[ clap( long) ]
32
33
ip : Option < IpAddr > ,
34
+ /// Specify the local socket address
35
+ #[ clap( long) ]
36
+ local_addr : Option < SocketAddr > ,
33
37
/// Number of unidirectional requests to maintain concurrently
34
38
#[ clap( long, default_value = "0" ) ]
35
39
uni_requests : u64 ,
36
40
/// Number of bidirectional requests to maintain concurrently
37
41
#[ clap( long, default_value = "1" ) ]
38
42
bi_requests : u64 ,
39
43
/// Number of bytes to request
40
- #[ clap( long, default_value = "1048576" ) ]
44
+ ///
45
+ /// This can use SI suffixes for sizes. For example, 1M will transfer
46
+ /// 1MiB, 10G will transfer 10GiB.
47
+ #[ clap( long, default_value = "1M" , value_parser = parse_byte_size) ]
41
48
download_size : u64 ,
42
49
/// Number of bytes to transmit, in addition to the request header
43
- #[ clap( long, default_value = "1048576" ) ]
50
+ ///
51
+ /// This can use SI suffixes for sizes. For example, 1M will transfer
52
+ /// 1MiB, 10G will transfer 10GiB.
53
+ #[ clap( long, default_value = "1M" , value_parser = parse_byte_size) ]
44
54
upload_size : u64 ,
45
55
/// The time to run in seconds
46
56
#[ clap( long, default_value = "60" ) ]
47
57
duration : u64 ,
48
58
/// The interval in seconds at which stats are reported
49
59
#[ clap( long, default_value = "1" ) ]
50
60
interval : u64 ,
51
- /// Send buffer size in bytes
52
- #[ clap( long, default_value = "2097152" ) ]
53
- send_buffer_size : usize ,
54
- /// Receive buffer size in bytes
55
- #[ clap( long, default_value = "2097152" ) ]
56
- recv_buffer_size : usize ,
57
- /// Specify the local socket address
58
- #[ clap( long) ]
59
- local_addr : Option < SocketAddr > ,
60
- /// Whether to print connection statistics
61
- #[ clap( long) ]
62
- conn_stats : bool ,
63
61
/// File path to output JSON statistics to. If the file is '-', stdout will be used
64
62
#[ cfg( feature = "json-output" ) ]
65
63
#[ clap( long) ]
66
64
json : Option < PathBuf > ,
67
- /// Perform NSS-compatible TLS key logging to the file specified in `SSLKEYLOGFILE`.
68
- #[ clap( long = "keylog" ) ]
69
- keylog : bool ,
70
- /// UDP payload size that the network must be capable of carrying
71
- #[ clap( long, default_value = "1200" ) ]
72
- initial_mtu : u16 ,
73
- /// Disable packet encryption/decryption (for debugging purpose)
74
- #[ clap( long = "no-protection" ) ]
75
- no_protection : bool ,
76
- /// The initial round-trip-time (in msecs)
77
- #[ clap( long) ]
78
- initial_rtt : Option < u64 > ,
79
- /// Ack Frequency mode
80
- #[ clap( long = "ack-frequency" ) ]
81
- ack_frequency : bool ,
82
- /// Congestion algorithm to use
83
- #[ clap( long = "congestion" ) ]
84
- cong_alg : Option < CongestionAlgorithm > ,
85
- /// qlog output file
86
- #[ cfg( feature = "qlog" ) ]
87
- #[ clap( long = "qlog" ) ]
88
- qlog_file : Option < PathBuf > ,
89
- }
90
-
91
- #[ tokio:: main( flavor = "current_thread" ) ]
92
- async fn main ( ) {
93
- let opt = Opt :: parse ( ) ;
94
-
95
- tracing_subscriber:: fmt:: init ( ) ;
96
-
97
- if let Err ( e) = run ( opt) . await {
98
- error ! ( "{:#}" , e) ;
99
- }
65
+ /// Common options
66
+ #[ command( flatten) ]
67
+ common : CommonOpt ,
100
68
}
101
69
102
- async fn run ( opt : Opt ) -> Result < ( ) > {
70
+ pub async fn run ( opt : Opt ) -> Result < ( ) > {
103
71
let mut host_parts = opt. host . split ( ':' ) ;
104
72
let host_name = host_parts. next ( ) . unwrap ( ) ;
105
73
let host_port = host_parts
@@ -128,13 +96,16 @@ async fn run(opt: Opt) -> Result<()> {
128
96
129
97
info ! ( "local addr {:?}" , bind_addr) ;
130
98
131
- let socket = bind_socket ( bind_addr, opt. send_buffer_size , opt. recv_buffer_size ) ?;
99
+ let socket = opt. common . bind_socket ( bind_addr) ?;
100
+
101
+ let mut endpoint_cfg = quinn:: EndpointConfig :: default ( ) ;
102
+ endpoint_cfg. max_udp_payload_size ( opt. common . max_udp_payload_size ) ?;
132
103
133
- let endpoint = quinn:: Endpoint :: new ( Default :: default ( ) , None , socket, Arc :: new ( TokioRuntime ) ) ?;
104
+ let endpoint = quinn:: Endpoint :: new ( endpoint_cfg , None , socket, Arc :: new ( TokioRuntime ) ) ?;
134
105
135
106
let default_provider = rustls:: crypto:: ring:: default_provider ( ) ;
136
107
let provider = Arc :: new ( rustls:: crypto:: CryptoProvider {
137
- cipher_suites : perf :: PERF_CIPHER_SUITES . into ( ) ,
108
+ cipher_suites : PERF_CIPHER_SUITES . into ( ) ,
138
109
..default_provider
139
110
} ) ;
140
111
@@ -146,35 +117,17 @@ async fn run(opt: Opt) -> Result<()> {
146
117
. with_no_client_auth ( ) ;
147
118
crypto. alpn_protocols = vec ! [ b"perf" . to_vec( ) ] ;
148
119
149
- if opt. keylog {
120
+ if opt. common . keylog {
150
121
crypto. key_log = Arc :: new ( rustls:: KeyLogFile :: new ( ) ) ;
151
122
}
152
123
153
- let mut transport = quinn:: TransportConfig :: default ( ) ;
154
- transport. initial_mtu ( opt. initial_mtu ) ;
155
-
156
- if let Some ( initial_rtt) = opt. initial_rtt {
157
- transport. initial_rtt ( Duration :: from_millis ( initial_rtt) ) ;
158
- }
159
-
160
- if opt. ack_frequency {
161
- transport. ack_frequency_config ( Some ( quinn:: AckFrequencyConfig :: default ( ) ) ) ;
162
- }
163
-
164
- if let Some ( cong_alg) = opt. cong_alg {
165
- transport. congestion_controller_factory ( cong_alg. build ( ) ) ;
166
- }
167
-
168
- #[ cfg( feature = "qlog" ) ]
169
- if let Some ( qlog_file) = & opt. qlog_file {
170
- let mut qlog = quinn:: QlogConfig :: default ( ) ;
171
- qlog. writer ( Box :: new ( File :: create ( qlog_file) ?) )
172
- . title ( Some ( "perf-client" . into ( ) ) ) ;
173
- transport. qlog_stream ( qlog. into_stream ( ) ) ;
174
- }
124
+ let transport = opt. common . build_transport_config (
125
+ #[ cfg( feature = "qlog" ) ]
126
+ "perf-client" ,
127
+ ) ?;
175
128
176
129
let crypto = Arc :: new ( QuicClientConfig :: try_from ( crypto) ?) ;
177
- let mut config = quinn:: ClientConfig :: new ( match opt. no_protection {
130
+ let mut config = quinn:: ClientConfig :: new ( match opt. common . no_protection {
178
131
true => Arc :: new ( NoProtectionClientConfig :: new ( crypto) ) ,
179
132
false => crypto,
180
133
} ) ;
@@ -220,7 +173,7 @@ async fn run(opt: Opt) -> Result<()> {
220
173
stats. on_interval ( start, & stream_stats) ;
221
174
222
175
stats. print ( ) ;
223
- if opt. conn_stats {
176
+ if opt. common . conn_stats {
224
177
println ! ( "{:?}\n " , connection. stats( ) ) ;
225
178
}
226
179
}
0 commit comments