1#[cfg(feature = "async")]
34pub mod async_client;
35pub mod connection_summary;
36pub mod sync_client;
37
38use connection_summary::*;
39use qlog::events::http3::FrameParsed;
40use qlog::events::http3::HttpHeader;
41use quiche::ConnectionError;
42
43use std::collections::HashMap;
44use std::net::SocketAddr;
45use std::time::Instant;
46
47use crate::actions::h3::Action;
48use crate::actions::h3::ExpectedStreamSendResult;
49use crate::actions::h3::StreamEvent;
50use crate::actions::h3::StreamEventType;
51use crate::config::Config;
52use crate::frame::H3iFrame;
53use crate::frame::ResetStream;
54use crate::frame_parser::FrameParseResult;
55use crate::frame_parser::FrameParser;
56use crate::frame_parser::InterruptCause;
57use crate::recordreplay::qlog::QlogEvent;
58use crate::recordreplay::qlog::*;
59use qlog::events::http3::Http3Frame;
60use qlog::events::EventData;
61use qlog::streamer::QlogStreamer;
62use serde::Serialize;
63
64use crate::quiche;
65use quiche::h3::frame::Frame as QFrame;
66use quiche::h3::Error;
67use quiche::h3::NameValue;
68
69const MAX_DATAGRAM_SIZE: usize = 1350;
70const QUIC_VERSION: u32 = 1;
71
72fn handle_qlog(
73 qlog_streamer: Option<&mut QlogStreamer>, qlog_frame: Http3Frame,
74 stream_id: u64,
75) {
76 if let Some(s) = qlog_streamer {
77 let ev_data = EventData::Http3FrameParsed(FrameParsed {
78 stream_id,
79 frame: qlog_frame,
80 ..Default::default()
81 });
82
83 s.add_event_data_now(ev_data).ok();
84 }
85}
86
87#[derive(Debug, Serialize)]
88pub enum ClientError {
90 HandshakeFail,
92 HttpFail,
94 Other(String),
96}
97
98pub(crate) trait Client {
99 fn stream_parsers_mut(&mut self) -> &mut StreamParserMap;
101
102 fn handle_response_frame(&mut self, stream_id: u64, frame: H3iFrame);
105}
106
107pub(crate) type StreamParserMap = HashMap<u64, FrameParser>;
108
109fn validate_stream_send_result(
110 result: quiche::Result<usize>, expected: &ExpectedStreamSendResult,
111 stream_id: u64,
112) {
113 match expected {
114 ExpectedStreamSendResult::Ok => {
115 result.unwrap_or_else(|err| {
116 panic!(
117 "Expected stream_send on stream {} to succeed, but got: {:?}",
118 stream_id, err
119 )
120 });
121 },
122 ExpectedStreamSendResult::OkExact(expected_bytes) => {
123 match result {
124 Ok(actual_bytes) if actual_bytes == *expected_bytes => {},
125 Ok(actual_bytes) => panic!(
126 "Expected stream_send on stream {} to write {} bytes, got {}",
127 stream_id, expected_bytes, actual_bytes
128 ),
129 Err(err) => panic!(
130 "Expected stream_send on stream {} to write {} bytes, got error: {:?}",
131 stream_id, expected_bytes, err
132 ),
133 }
134 },
135 ExpectedStreamSendResult::Error(expected_err) => {
136 match result {
137 Ok(written) => panic!(
138 "Expected stream_send on stream {} to fail with {:?}, but wrote {} bytes",
139 stream_id, expected_err, written
140 ),
141 Err(actual_err) if &actual_err == expected_err => {},
142 Err(actual_err) => panic!(
143 "Expected stream_send on stream {} to fail with {:?}, got {:?}",
144 stream_id, expected_err, actual_err
145 ),
146 }
147 },
148 }
149}
150
151pub(crate) fn execute_action<F: quiche::BufFactory>(
152 action: &Action, conn: &mut quiche::Connection<F>,
153 stream_parsers: &mut StreamParserMap,
154) {
155 match action {
156 Action::SendFrame {
157 stream_id,
158 fin_stream,
159 frame,
160 expected_result,
161 } => {
162 log::info!("frame tx id={stream_id} frame={frame:?}");
163
164 let mut d = [42; 9999];
166 let mut b = octets::OctetsMut::with_slice(&mut d);
167
168 if let Some(s) = conn.qlog_streamer() {
169 let events: QlogEvents = action.into();
170 for event in events {
171 match event {
172 QlogEvent::Event { data, ex_data } => {
173 if matches!(
175 data.as_ref(),
176 EventData::QuicPacketSent(..)
177 ) {
178 continue;
179 }
180
181 s.add_event_data_ex_now(*data, ex_data).ok();
182 },
183
184 QlogEvent::JsonEvent(mut ev) => {
185 ev.time = Instant::now()
187 .duration_since(s.start_time())
188 .as_secs_f64() *
189 1000.0;
190 s.add_event(ev).ok();
191 },
192 }
193 }
194 }
195 let len = frame.to_bytes(&mut b).unwrap();
196
197 let result = conn.stream_send(*stream_id, &d[..len], *fin_stream);
199 validate_stream_send_result(result, expected_result, *stream_id);
200
201 stream_parsers
202 .entry(*stream_id)
203 .or_insert_with(|| FrameParser::new(*stream_id));
204 },
205
206 Action::SendHeadersFrame {
207 stream_id,
208 fin_stream,
209 headers,
210 frame,
211 expected_result,
212 ..
213 } => {
214 log::info!("headers frame tx stream={stream_id} hdrs={headers:?}");
215
216 let mut d = [42; 9999];
218 let mut b = octets::OctetsMut::with_slice(&mut d);
219
220 if let Some(s) = conn.qlog_streamer() {
221 let events: QlogEvents = action.into();
222 for event in events {
223 match event {
224 QlogEvent::Event { data, ex_data } => {
225 if matches!(
227 data.as_ref(),
228 EventData::QuicPacketSent(..)
229 ) {
230 continue;
231 }
232
233 s.add_event_data_ex_now(*data, ex_data).ok();
234 },
235
236 QlogEvent::JsonEvent(mut ev) => {
237 ev.time = Instant::now()
239 .duration_since(s.start_time())
240 .as_secs_f64() *
241 1000.0;
242 s.add_event(ev).ok();
243 },
244 }
245 }
246 }
247 let len = frame.to_bytes(&mut b).unwrap();
248 let result = conn.stream_send(*stream_id, &d[..len], *fin_stream);
249 validate_stream_send_result(result, expected_result, *stream_id);
250
251 stream_parsers
252 .entry(*stream_id)
253 .or_insert_with(|| FrameParser::new(*stream_id));
254 },
255
256 Action::OpenUniStream {
257 stream_id,
258 fin_stream,
259 stream_type,
260 expected_result,
261 } => {
262 log::info!(
263 "open uni stream_id={stream_id} ty={stream_type} fin={fin_stream}"
264 );
265
266 let mut d = [42; 8];
267 let mut b = octets::OctetsMut::with_slice(&mut d);
268 b.put_varint(*stream_type).unwrap();
269 let off = b.off();
270
271 let result = conn.stream_send(*stream_id, &d[..off], *fin_stream);
272 validate_stream_send_result(result, expected_result, *stream_id);
273
274 stream_parsers
275 .entry(*stream_id)
276 .or_insert_with(|| FrameParser::new(*stream_id));
277 },
278
279 Action::StreamBytes {
280 stream_id,
281 bytes,
282 fin_stream,
283 expected_result,
284 } => {
285 log::info!(
286 "stream bytes tx id={} len={} fin={}",
287 stream_id,
288 bytes.len(),
289 fin_stream
290 );
291 let result = conn.stream_send(*stream_id, bytes, *fin_stream);
292 validate_stream_send_result(result, expected_result, *stream_id);
293
294 stream_parsers
295 .entry(*stream_id)
296 .or_insert_with(|| FrameParser::new(*stream_id));
297 },
298
299 Action::SendDatagram { payload } => {
300 log::info!("dgram tx len={}", payload.len(),);
301
302 conn.dgram_send(payload)
303 .expect("datagram extension not enabled by peer");
304 },
305
306 Action::ResetStream {
307 stream_id,
308 error_code,
309 } => {
310 log::info!(
311 "reset_stream stream_id={stream_id} error_code={error_code}"
312 );
313 if let Err(e) = conn.stream_shutdown(
314 *stream_id,
315 quiche::Shutdown::Write,
316 *error_code,
317 ) {
318 log::error!("can't send reset_stream: {e}");
319 return;
322 }
323
324 stream_parsers
325 .entry(*stream_id)
326 .or_insert_with(|| FrameParser::new(*stream_id));
327 },
328
329 Action::StopSending {
330 stream_id,
331 error_code,
332 } => {
333 log::info!(
334 "stop_sending stream id={stream_id} error_code={error_code}"
335 );
336
337 if let Err(e) = conn.stream_shutdown(
338 *stream_id,
339 quiche::Shutdown::Read,
340 *error_code,
341 ) {
342 log::error!("can't send stop_sending: {e}");
343 }
344
345 stream_parsers
348 .entry(*stream_id)
349 .or_insert_with(|| FrameParser::new(*stream_id));
350 },
351
352 Action::ConnectionClose { error } => {
353 let ConnectionError {
354 is_app,
355 error_code,
356 reason,
357 } = error;
358
359 log::info!("connection_close={error:?}");
360 let _ = conn.close(*is_app, *error_code, reason);
361 },
362
363 Action::FlushPackets | Action::Wait { .. } => unreachable!(),
365 }
366}
367
368pub(crate) fn parse_streams<F: quiche::BufFactory, C: Client>(
369 conn: &mut quiche::Connection<F>, client: &mut C,
370) -> Vec<StreamEvent> {
371 let mut responded_streams: Vec<StreamEvent> =
372 Vec::with_capacity(conn.readable().len());
373
374 for stream in conn.readable() {
375 if stream % 4 != 0 {
377 continue;
378 }
379
380 loop {
381 let stream_parse_result = client
382 .stream_parsers_mut()
383 .get_mut(&stream)
384 .expect("stream readable with no parser")
385 .try_parse_frame(conn);
386
387 match stream_parse_result {
388 Ok(FrameParseResult::FrameParsed { h3i_frame, fin }) => {
389 if let H3iFrame::Headers(ref headers) = h3i_frame {
390 log::info!("hdrs={headers:?}");
391 }
392
393 handle_response_frame(
394 client,
395 conn.qlog_streamer(),
396 &mut responded_streams,
397 stream,
398 h3i_frame,
399 );
400
401 if fin {
402 handle_fin(
403 &mut responded_streams,
404 client.stream_parsers_mut(),
405 stream,
406 );
407 break;
408 }
409 },
410 Ok(FrameParseResult::Retry) => {},
411 Ok(FrameParseResult::Interrupted(cause)) => {
412 if let InterruptCause::ResetStream(error_code) = cause {
413 let frame = H3iFrame::ResetStream(ResetStream {
414 stream_id: stream,
415 error_code,
416 });
417
418 log::info!("received reset stream: {frame:?}");
419 handle_response_frame(
420 client,
421 None,
422 &mut responded_streams,
423 stream,
424 frame,
425 );
426 }
427
428 handle_fin(
429 &mut responded_streams,
430 client.stream_parsers_mut(),
431 stream,
432 );
433 break;
434 },
435 Err(e) => {
436 match e {
437 Error::TransportError(quiche::Error::Done) => {
438 log::debug!("stream {stream} exhausted");
439 },
440 Error::TransportError(quiche::Error::StreamReset(
441 error_code,
442 )) => {
443 let frame = H3iFrame::ResetStream(ResetStream {
444 stream_id: stream,
445 error_code,
446 });
447
448 log::info!("received reset stream: {frame:?}");
449
450 handle_response_frame(
451 client,
452 None,
453 &mut responded_streams,
454 stream,
455 frame,
456 );
457
458 client.stream_parsers_mut().remove(&stream);
459 },
460 _ => {
461 log::warn!("stream read error: {e}");
462 },
463 };
464
465 break;
466 },
467 }
468 }
469 }
470
471 responded_streams
472}
473
474fn handle_fin(
475 responded_streams: &mut Vec<StreamEvent>,
476 stream_parsers: &mut StreamParserMap, stream_id: u64,
477) {
478 responded_streams.push(StreamEvent {
479 stream_id,
480 event_type: StreamEventType::Finished,
481 });
482
483 stream_parsers.remove(&stream_id);
484}
485
486fn handle_response_frame<C: Client>(
489 client: &mut C, qlog_streamer: Option<&mut QlogStreamer>,
490 responded_streams: &mut Vec<StreamEvent>, stream_id: u64, frame: H3iFrame,
491) {
492 let cloned = frame.clone();
493 client.handle_response_frame(stream_id, cloned);
494
495 let mut to_qlog: Option<Http3Frame> = None;
496 let mut push_to_responses: Option<StreamEvent> = None;
497
498 match frame {
499 H3iFrame::Headers(enriched_headers) => {
500 push_to_responses = Some(StreamEvent {
501 stream_id,
502 event_type: StreamEventType::Headers,
503 });
504
505 let qlog_headers: Vec<HttpHeader> = enriched_headers
506 .headers()
507 .iter()
508 .map(|h| qlog::events::http3::HttpHeader {
509 name: Some(String::from_utf8_lossy(h.name()).into_owned()),
510 name_bytes: None,
511 value: Some(String::from_utf8_lossy(h.value()).into_owned()),
512 value_bytes: None,
513 })
514 .collect();
515
516 to_qlog = Some(Http3Frame::Headers {
517 headers: qlog_headers,
518 raw: None,
519 });
520 },
521 H3iFrame::QuicheH3(quiche_frame) => {
522 if let QFrame::Data { .. } = quiche_frame {
523 push_to_responses = Some(StreamEvent {
524 stream_id,
525 event_type: StreamEventType::Data,
526 });
527 }
528
529 to_qlog = Some(quiche_frame.to_qlog());
530 },
531 H3iFrame::ResetStream(_) => {
532 push_to_responses = Some(StreamEvent {
533 stream_id,
534 event_type: StreamEventType::Finished,
535 });
536 },
537 }
538
539 if let Some(to_qlog) = to_qlog {
540 handle_qlog(qlog_streamer, to_qlog, stream_id);
541 }
542
543 if let Some(to_push) = push_to_responses {
544 responded_streams.push(to_push);
545 }
546}
547
548pub(crate) struct ParsedArgs<'a> {
549 pub(crate) bind_addr: SocketAddr,
550 pub(crate) peer_addr: SocketAddr,
551 pub(crate) connect_url: Option<&'a str>,
552}
553
554pub(crate) fn parse_args(args: &Config) -> ParsedArgs<'_> {
555 let connect_url = if !args.omit_sni {
557 args.host_port.split(':').next()
558 } else {
559 None
560 };
561
562 let (peer_addr, bind_addr) = resolve_socket_addrs(args);
563
564 ParsedArgs {
565 peer_addr,
566 bind_addr,
567 connect_url,
568 }
569}
570
571fn resolve_socket_addrs(args: &Config) -> (SocketAddr, SocketAddr) {
572 let peer_addr = if let Some(addr) = &args.connect_to {
574 addr.parse().expect("--connect-to is expected to be a string containing an IPv4 or IPv6 address with a port. E.g. 192.0.2.0:443")
575 } else {
576 let x = format!("https://{}", args.host_port);
577 *url::Url::parse(&x)
578 .unwrap()
579 .socket_addrs(|| None)
580 .unwrap()
581 .first()
582 .unwrap()
583 };
584
585 let bind_addr = match peer_addr {
589 std::net::SocketAddr::V4(_) => format!("0.0.0.0:{}", args.source_port),
590 std::net::SocketAddr::V6(_) => format!("[::]:{}", args.source_port),
591 };
592
593 (
594 peer_addr,
595 bind_addr.parse().expect("unable to parse bind address"),
596 )
597}