1use super::common::alpns;
28use std::time::Duration;
29
30pub trait Args {
31 fn with_docopt(docopt: &docopt::Docopt) -> Self;
32}
33
34pub struct CommonArgs {
36 pub alpns: Vec<&'static [u8]>,
37 pub max_data: u64,
38 pub max_window: u64,
39 pub max_stream_data: u64,
40 pub max_stream_window: u64,
41 pub max_streams_bidi: u64,
42 pub max_streams_uni: u64,
43 pub idle_timeout: u64,
44 pub early_data: bool,
45 pub dump_packet_path: Option<String>,
46 pub no_grease: bool,
47 pub cc_algorithm: String,
48 pub disable_hystart: bool,
49 pub dgrams_enabled: bool,
50 pub dgram_count: u64,
51 pub dgram_data: String,
52 pub max_active_cids: u64,
53 pub enable_active_migration: bool,
54 pub max_field_section_size: Option<u64>,
55 pub qpack_max_table_capacity: Option<u64>,
56 pub qpack_blocked_streams: Option<u64>,
57 pub initial_rtt: Duration,
58 pub initial_cwnd_packets: u64,
59}
60
61impl Args for CommonArgs {
88 fn with_docopt(docopt: &docopt::Docopt) -> Self {
89 let args = docopt.parse().unwrap_or_else(|e| e.exit());
90
91 let http_version = args.get_str("--http-version");
92 let dgram_proto = args.get_str("--dgram-proto");
93 let (alpns, dgrams_enabled) = match (http_version, dgram_proto) {
94 ("HTTP/0.9", "none") => (alpns::HTTP_09.to_vec(), false),
95
96 ("HTTP/0.9", _) => {
97 panic!("Unsupported HTTP version and DATAGRAM protocol.")
98 },
99
100 ("HTTP/3", "none") => (alpns::HTTP_3.to_vec(), false),
101
102 ("HTTP/3", "oneway") => (alpns::HTTP_3.to_vec(), true),
103
104 ("all", "none") => (
105 [alpns::HTTP_3.as_slice(), &alpns::HTTP_09]
106 .concat()
107 .to_vec(),
108 false,
109 ),
110
111 (..) => panic!("Unsupported HTTP version and DATAGRAM protocol."),
112 };
113
114 let dgram_count = args.get_str("--dgram-count");
115 let dgram_count = dgram_count.parse::<u64>().unwrap();
116
117 let dgram_data = args.get_str("--dgram-data").to_string();
118
119 let max_data = args.get_str("--max-data");
120 let max_data = max_data.parse::<u64>().unwrap();
121
122 let max_window = args.get_str("--max-window");
123 let max_window = max_window.parse::<u64>().unwrap();
124
125 let max_stream_data = args.get_str("--max-stream-data");
126 let max_stream_data = max_stream_data.parse::<u64>().unwrap();
127
128 let max_stream_window = args.get_str("--max-stream-window");
129 let max_stream_window = max_stream_window.parse::<u64>().unwrap();
130
131 let max_streams_bidi = args.get_str("--max-streams-bidi");
132 let max_streams_bidi = max_streams_bidi.parse::<u64>().unwrap();
133
134 let max_streams_uni = args.get_str("--max-streams-uni");
135 let max_streams_uni = max_streams_uni.parse::<u64>().unwrap();
136
137 let idle_timeout = args.get_str("--idle-timeout");
138 let idle_timeout = idle_timeout.parse::<u64>().unwrap();
139
140 let early_data = args.get_bool("--early-data");
141
142 let dump_packet_path = if !args.get_str("--dump-packets").is_empty() {
143 Some(args.get_str("--dump-packets").to_string())
144 } else {
145 None
146 };
147
148 let no_grease = args.get_bool("--no-grease");
149
150 let cc_algorithm = args.get_str("--cc-algorithm");
151
152 let disable_hystart = args.get_bool("--disable-hystart");
153
154 let max_active_cids = args.get_str("--max-active-cids");
155 let max_active_cids = max_active_cids.parse::<u64>().unwrap();
156
157 let enable_active_migration = args.get_bool("--enable-active-migration");
158
159 let max_field_section_size =
160 if !args.get_str("--max-field-section-size").is_empty() {
161 Some(
162 args.get_str("--max-field-section-size")
163 .parse::<u64>()
164 .unwrap(),
165 )
166 } else {
167 None
168 };
169
170 let qpack_max_table_capacity =
171 if !args.get_str("--qpack-max-table-capacity").is_empty() {
172 Some(
173 args.get_str("--qpack-max-table-capacity")
174 .parse::<u64>()
175 .unwrap(),
176 )
177 } else {
178 None
179 };
180
181 let qpack_blocked_streams =
182 if !args.get_str("--qpack-blocked-streams").is_empty() {
183 Some(
184 args.get_str("--qpack-blocked-streams")
185 .parse::<u64>()
186 .unwrap(),
187 )
188 } else {
189 None
190 };
191
192 let initial_rtt_millis =
193 args.get_str("--initial-rtt").parse::<u64>().unwrap();
194 let initial_rtt = Duration::from_millis(initial_rtt_millis);
195
196 let initial_cwnd_packets = args
197 .get_str("--initial-cwnd-packets")
198 .parse::<u64>()
199 .unwrap();
200
201 CommonArgs {
202 alpns,
203 max_data,
204 max_window,
205 max_stream_data,
206 max_stream_window,
207 max_streams_bidi,
208 max_streams_uni,
209 idle_timeout,
210 early_data,
211 dump_packet_path,
212 no_grease,
213 cc_algorithm: cc_algorithm.to_string(),
214 disable_hystart,
215 dgrams_enabled,
216 dgram_count,
217 dgram_data,
218 max_active_cids,
219 enable_active_migration,
220 max_field_section_size,
221 qpack_max_table_capacity,
222 qpack_blocked_streams,
223 initial_rtt,
224 initial_cwnd_packets,
225 }
226 }
227}
228
229impl Default for CommonArgs {
230 fn default() -> Self {
231 CommonArgs {
232 alpns: alpns::HTTP_3.to_vec(),
233 max_data: 10000000,
234 max_window: 25165824,
235 max_stream_data: 1000000,
236 max_stream_window: 16777216,
237 max_streams_bidi: 100,
238 max_streams_uni: 100,
239 idle_timeout: 30000,
240 early_data: false,
241 dump_packet_path: None,
242 no_grease: false,
243 cc_algorithm: "cubic".to_string(),
244 disable_hystart: false,
245 dgrams_enabled: false,
246 dgram_count: 0,
247 dgram_data: "quack".to_string(),
248 max_active_cids: 2,
249 enable_active_migration: false,
250 max_field_section_size: None,
251 qpack_max_table_capacity: None,
252 qpack_blocked_streams: None,
253 initial_rtt: Duration::from_millis(333),
254 initial_cwnd_packets: 10,
255 }
256 }
257}
258
259pub const CLIENT_USAGE: &str = "Usage:
260 quiche-client [options] URL...
261 quiche-client -h | --help
262
263Options:
264 --method METHOD Use the given HTTP request method [default: GET].
265 --body FILE Send the given file as request body.
266 --max-data BYTES Connection-wide flow control limit [default: 10000000].
267 --max-window BYTES Connection-wide max receiver window [default: 25165824].
268 --max-stream-data BYTES Per-stream flow control limit [default: 1000000].
269 --max-stream-window BYTES Per-stream max receiver window [default: 16777216].
270 --max-streams-bidi STREAMS Number of allowed concurrent streams [default: 100].
271 --max-streams-uni STREAMS Number of allowed concurrent streams [default: 100].
272 --idle-timeout TIMEOUT Idle timeout in milliseconds [default: 30000].
273 --wire-version VERSION The version number to send to the server [default: babababa].
274 --http-version VERSION HTTP version to use [default: all].
275 --early-data Enable sending early data.
276 --dgram-proto PROTO DATAGRAM application protocol to use [default: none].
277 --dgram-count COUNT Number of DATAGRAMs to send [default: 0].
278 --dgram-data DATA Data to send for certain types of DATAGRAM application protocol [default: quack].
279 --dump-packets PATH Dump the incoming packets as files in the given directory.
280 --dump-responses PATH Dump response payload as files in the given directory.
281 --dump-json Dump response headers and payload to stdout in JSON format.
282 --max-json-payload BYTES Per-response payload limit when dumping JSON [default: 10000].
283 --connect-to ADDRESS Override the server's address.
284 --no-verify Don't verify server's certificate.
285 --trust-origin-ca-pem <file> Path to the pem file of the origin's CA, if not publicly trusted.
286 --no-grease Don't send GREASE.
287 --cc-algorithm NAME Specify which congestion control algorithm to use [default: cubic].
288 --disable-hystart Disable HyStart++.
289 --max-active-cids NUM The maximum number of active Connection IDs we can support [default: 2].
290 --enable-active-migration Enable active connection migration.
291 --perform-migration Perform connection migration on another source port.
292 -H --header HEADER ... Add a request header.
293 -n --requests REQUESTS Send the given number of identical requests [default: 1].
294 --send-priority-update Send HTTP/3 priority updates if the query string params 'u' or 'i' are present in URLs
295 --max-field-section-size BYTES Max size of uncompressed field section. Default is unlimited.
296 --qpack-max-table-capacity BYTES Max capacity of dynamic QPACK decoding.. Any value other that 0 is currently unsupported.
297 --qpack-blocked-streams STREAMS Limit of blocked streams while decoding. Any value other that 0 is currently unsupported.
298 --session-file PATH File used to cache a TLS session for resumption.
299 --source-port PORT Source port to use when connecting to the server [default: 0].
300 --initial-rtt MILLIS The initial RTT in milliseconds [default: 333].
301 --initial-cwnd-packets PACKETS The initial congestion window size in terms of packet count [default: 10].
302 -h --help Show this screen.
303";
304
305pub struct ClientArgs {
307 pub version: u32,
308 pub dump_response_path: Option<String>,
309 pub dump_json: Option<usize>,
310 pub urls: Vec<url::Url>,
311 pub reqs_cardinal: u64,
312 pub req_headers: Vec<String>,
313 pub no_verify: bool,
314 pub trust_origin_ca_pem: Option<String>,
315 pub body: Option<Vec<u8>>,
316 pub method: String,
317 pub connect_to: Option<String>,
318 pub session_file: Option<String>,
319 pub source_port: u16,
320 pub perform_migration: bool,
321 pub send_priority_update: bool,
322}
323
324impl Args for ClientArgs {
325 fn with_docopt(docopt: &docopt::Docopt) -> Self {
326 let args = docopt.parse().unwrap_or_else(|e| e.exit());
327
328 let version = args.get_str("--wire-version");
329 let version = u32::from_str_radix(version, 16).unwrap();
330
331 let dump_response_path = if !args.get_str("--dump-responses").is_empty() {
332 Some(args.get_str("--dump-responses").to_string())
333 } else {
334 None
335 };
336
337 let dump_json = args.get_bool("--dump-json");
338 let dump_json = if dump_json {
339 let max_payload = args.get_str("--max-json-payload");
340 let max_payload = max_payload.parse::<usize>().unwrap();
341 Some(max_payload)
342 } else {
343 None
344 };
345
346 let urls: Vec<url::Url> = args
348 .get_vec("URL")
349 .into_iter()
350 .map(|x| url::Url::parse(x).unwrap())
351 .collect();
352
353 let req_headers = args
355 .get_vec("--header")
356 .into_iter()
357 .map(|x| x.to_string())
358 .collect();
359
360 let reqs_cardinal = args.get_str("--requests");
361 let reqs_cardinal = reqs_cardinal.parse::<u64>().unwrap();
362
363 let no_verify = args.get_bool("--no-verify");
364
365 let trust_origin_ca_pem = args.get_str("--trust-origin-ca-pem");
366 let trust_origin_ca_pem = if !trust_origin_ca_pem.is_empty() {
367 Some(trust_origin_ca_pem.to_string())
368 } else {
369 None
370 };
371
372 let body = if args.get_bool("--body") {
373 std::fs::read(args.get_str("--body")).ok()
374 } else {
375 None
376 };
377
378 let method = args.get_str("--method").to_string();
379
380 let connect_to = if args.get_bool("--connect-to") {
381 Some(args.get_str("--connect-to").to_string())
382 } else {
383 None
384 };
385
386 let session_file = if args.get_bool("--session-file") {
387 Some(args.get_str("--session-file").to_string())
388 } else {
389 None
390 };
391
392 let source_port = args.get_str("--source-port");
393 let source_port = source_port.parse::<u16>().unwrap();
394
395 let perform_migration = args.get_bool("--perform-migration");
396
397 let send_priority_update = args.get_bool("--send-priority-update");
398
399 ClientArgs {
400 version,
401 dump_response_path,
402 dump_json,
403 urls,
404 reqs_cardinal,
405 req_headers,
406 no_verify,
407 trust_origin_ca_pem,
408 body,
409 method,
410 connect_to,
411 session_file,
412 source_port,
413 perform_migration,
414 send_priority_update,
415 }
416 }
417}
418
419impl Default for ClientArgs {
420 fn default() -> Self {
421 ClientArgs {
422 version: 0xbabababa,
423 dump_response_path: None,
424 dump_json: None,
425 urls: vec![],
426 req_headers: vec![],
427 reqs_cardinal: 1,
428 no_verify: false,
429 trust_origin_ca_pem: None,
430 body: None,
431 method: "GET".to_string(),
432 connect_to: None,
433 session_file: None,
434 source_port: 0,
435 perform_migration: false,
436 send_priority_update: false,
437 }
438 }
439}
440
441pub const SERVER_USAGE: &str = "Usage:
442 quiche-server [options]
443 quiche-server -h | --help
444
445Options:
446 --listen <addr> Listen on the given IP:port [default: 127.0.0.1:4433]
447 --cert <file> TLS certificate path [default: src/bin/cert.crt]
448 --key <file> TLS certificate key path [default: src/bin/cert.key]
449 --root <dir> Root directory [default: src/bin/root/]
450 --index <name> The file that will be used as index [default: index.html].
451 --name <str> Name of the server [default: quic.tech]
452 --max-data BYTES Connection-wide flow control limit [default: 10000000].
453 --max-window BYTES Connection-wide max receiver window [default: 25165824].
454 --max-stream-data BYTES Per-stream flow control limit [default: 1000000].
455 --max-stream-window BYTES Per-stream max receiver window [default: 16777216].
456 --max-streams-bidi STREAMS Number of allowed concurrent streams [default: 100].
457 --max-streams-uni STREAMS Number of allowed concurrent streams [default: 100].
458 --idle-timeout TIMEOUT Idle timeout in milliseconds [default: 30000].
459 --dump-packets PATH Dump the incoming packets as files in the given directory.
460 --early-data Enable receiving early data.
461 --no-retry Disable stateless retry.
462 --no-grease Don't send GREASE.
463 --http-version VERSION HTTP version to use [default: all].
464 --dgram-proto PROTO DATAGRAM application protocol to use [default: none].
465 --dgram-count COUNT Number of DATAGRAMs to send [default: 0].
466 --dgram-data DATA Data to send for certain types of DATAGRAM application protocol [default: brrr].
467 --cc-algorithm NAME Specify which congestion control algorithm to use [default: cubic].
468 --disable-hystart Disable HyStart++.
469 --max-active-cids NUM The maximum number of active Connection IDs we can support [default: 2].
470 --enable-active-migration Enable active connection migration.
471 --max-field-section-size BYTES Max size of uncompressed HTTP/3 field section. Default is unlimited.
472 --qpack-max-table-capacity BYTES Max capacity of QPACK dynamic table decoding. Any value other that 0 is currently unsupported.
473 --qpack-blocked-streams STREAMS Limit of streams that can be blocked while decoding. Any value other that 0 is currently unsupported.
474 --disable-gso Disable GSO (linux only).
475 --disable-pacing Disable pacing (linux only).
476 --initial-rtt MILLIS The initial RTT in milliseconds [default: 333].
477 --initial-cwnd-packets PACKETS The initial congestion window size in terms of packet count [default: 10].
478 -h --help Show this screen.
479";
480
481pub struct ServerArgs {
483 pub listen: String,
484 pub no_retry: bool,
485 pub root: String,
486 pub index: String,
487 pub cert: String,
488 pub key: String,
489 pub disable_gso: bool,
490 pub disable_pacing: bool,
491 pub enable_pmtud: bool,
492}
493
494impl Args for ServerArgs {
495 fn with_docopt(docopt: &docopt::Docopt) -> Self {
496 let args = docopt.parse().unwrap_or_else(|e| e.exit());
497
498 let listen = args.get_str("--listen").to_string();
499 let no_retry = args.get_bool("--no-retry");
500 let root = args.get_str("--root").to_string();
501 let index = args.get_str("--index").to_string();
502 let cert = args.get_str("--cert").to_string();
503 let key = args.get_str("--key").to_string();
504 let disable_gso = args.get_bool("--disable-gso");
505 let disable_pacing = args.get_bool("--disable-pacing");
506 let enable_pmtud = args.get_bool("--enable-pmtud");
507
508 ServerArgs {
509 listen,
510 no_retry,
511 root,
512 index,
513 cert,
514 key,
515 disable_gso,
516 disable_pacing,
517 enable_pmtud,
518 }
519 }
520}