1use log;
31use quiche::PathStats;
32use quiche::Stats;
33use std::future::Future;
34use std::pin::Pin;
35use std::task::Context;
36use std::task::Poll;
37use std::time::Duration;
38use tokio::select;
39use tokio::sync::mpsc;
40use tokio::sync::oneshot;
41use tokio::time::sleep;
42use tokio::time::sleep_until;
43use tokio::time::Instant;
44use tokio_quiche::metrics::Metrics;
45use tokio_quiche::quic::HandshakeInfo;
46use tokio_quiche::quic::QuicheConnection;
47use tokio_quiche::settings::Hooks;
48use tokio_quiche::settings::QuicSettings;
49use tokio_quiche::socket::Socket;
50use tokio_quiche::ApplicationOverQuic;
51use tokio_quiche::ConnectionParams;
52use tokio_quiche::QuicResult;
53
54use crate::actions::h3::Action;
55use crate::actions::h3::WaitType;
56use crate::actions::h3::WaitingFor;
57use crate::client::execute_action;
58use crate::client::parse_args;
59use crate::client::parse_streams;
60use crate::client::ClientError;
61use crate::client::CloseTriggerFrames;
62use crate::client::ConnectionSummary;
63use crate::client::ParsedArgs;
64use crate::client::StreamMap;
65use crate::client::MAX_DATAGRAM_SIZE;
66use crate::config::Config as H3iConfig;
67use crate::frame::H3iFrame;
68use crate::quiche;
69
70use super::Client;
71use super::ConnectionCloseDetails;
72use super::StreamParserMap;
73
74pub async fn connect(
76 args: &H3iConfig, frame_actions: Vec<Action>,
77 close_trigger_frames: Option<CloseTriggerFrames>,
78) -> std::result::Result<BuildingConnectionSummary, ClientError> {
79 let quic_settings = create_config(args);
80 let mut connection_params =
81 ConnectionParams::new_client(quic_settings, None, Hooks::default());
82
83 connection_params.session = args.session.clone();
84
85 let ParsedArgs {
86 connect_url,
87 bind_addr,
88 peer_addr,
89 } = parse_args(args);
90
91 let socket = tokio::net::UdpSocket::bind(bind_addr).await.unwrap();
92 socket.connect(peer_addr).await.unwrap();
93
94 log::info!(
95 "connecting to {:} from {:}",
96 peer_addr,
97 socket.local_addr().unwrap()
98 );
99
100 let (h3i, conn_summary_fut) =
101 H3iDriver::new(frame_actions, close_trigger_frames);
102 match tokio_quiche::quic::connect_with_config(
103 Socket::try_from(socket).unwrap(),
104 connect_url,
105 &connection_params,
106 h3i,
107 )
108 .await
109 {
110 Ok(_) => Ok(conn_summary_fut),
111 Err(_) => Err(ClientError::HandshakeFail),
112 }
113}
114
115fn create_config(args: &H3iConfig) -> QuicSettings {
116 let mut quic_settings = QuicSettings::default();
117
118 quic_settings.verify_peer = args.verify_peer;
119 quic_settings.max_idle_timeout =
120 Some(Duration::from_millis(args.idle_timeout));
121 quic_settings.max_recv_udp_payload_size = MAX_DATAGRAM_SIZE;
122 quic_settings.max_send_udp_payload_size = MAX_DATAGRAM_SIZE;
123 quic_settings.initial_max_data = 10_000_000;
124 quic_settings.initial_max_stream_data_bidi_local =
125 args.max_stream_data_bidi_local;
126 quic_settings.initial_max_stream_data_bidi_remote =
127 args.max_stream_data_bidi_remote;
128 quic_settings.initial_max_stream_data_uni = args.max_stream_data_uni;
129 quic_settings.initial_max_streams_bidi = args.max_streams_bidi;
130 quic_settings.initial_max_streams_uni = args.max_streams_uni;
131 quic_settings.disable_active_migration = true;
132 quic_settings.active_connection_id_limit = 0;
133 quic_settings.max_connection_window = args.max_window;
134 quic_settings.max_stream_window = args.max_stream_window;
135 quic_settings.enable_send_streams_blocked = true;
136 quic_settings.grease = false;
137
138 quic_settings.capture_quiche_logs = true;
139 quic_settings.keylog_file = std::env::var_os("SSLKEYLOGFILE")
140 .and_then(|os_str| os_str.into_string().ok());
141
142 quic_settings.enable_dgram = args.enable_dgram;
143 quic_settings.dgram_recv_max_queue_len = args.dgram_recv_queue_len;
144 quic_settings.dgram_send_max_queue_len = args.dgram_send_queue_len;
145
146 quic_settings
147}
148
149#[must_use = "must await to get a ConnectionSummary"]
156pub struct BuildingConnectionSummary {
157 rx: mpsc::UnboundedReceiver<ConnectionRecord>,
158 summary: Option<ConnectionSummary>,
159 seen_all_close_trigger_frames: Option<oneshot::Sender<()>>,
160}
161
162impl BuildingConnectionSummary {
163 fn new(
164 rx: mpsc::UnboundedReceiver<ConnectionRecord>,
165 close_trigger_frames: Option<CloseTriggerFrames>,
166 trigger_frame_tx: oneshot::Sender<()>,
167 ) -> Self {
168 let summary = ConnectionSummary {
169 stream_map: StreamMap::new(close_trigger_frames),
170 ..Default::default()
171 };
172
173 Self {
174 rx,
175 summary: Some(summary),
176 seen_all_close_trigger_frames: Some(trigger_frame_tx),
177 }
178 }
179}
180
181impl Future for BuildingConnectionSummary {
182 type Output = ConnectionSummary;
183
184 fn poll(
185 mut self: Pin<&mut Self>, cx: &mut Context<'_>,
186 ) -> Poll<Self::Output> {
187 while let Poll::Ready(Some(record)) = self.rx.poll_recv(cx) {
188 let summary = self.summary.as_mut().expect("summary already taken");
191
192 match record {
193 ConnectionRecord::StreamedFrame { stream_id, frame } => {
194 let stream_map = &mut summary.stream_map;
195 stream_map.insert(stream_id, frame);
196
197 if stream_map.all_close_trigger_frames_seen() {
198 if let Some(expected_tx) =
200 self.seen_all_close_trigger_frames.take()
201 {
202 let _ = expected_tx.send(());
203 }
204 }
205 },
206 ConnectionRecord::ConnectionStats(s) => summary.stats = Some(s),
207 ConnectionRecord::PathStats(ps) => summary.path_stats = ps,
208 ConnectionRecord::Close(d) => summary.conn_close_details = d,
209 };
210 }
211
212 if self.rx.is_closed() {
213 let summary = self.summary.take().expect("summary already taken");
216 Poll::Ready(summary)
217 } else {
218 Poll::Pending
219 }
220 }
221}
222
223pub struct H3iDriver {
224 actions: Vec<Action>,
225 actions_executed: usize,
226 next_fire_time: Instant,
227 waiting_for_responses: WaitingFor,
228 record_tx: mpsc::UnboundedSender<ConnectionRecord>,
229 stream_parsers: StreamParserMap,
230 close_trigger_seen_rx: oneshot::Receiver<()>,
231}
232
233impl H3iDriver {
234 fn new(
235 actions: Vec<Action>, close_trigger_frames: Option<CloseTriggerFrames>,
236 ) -> (Self, BuildingConnectionSummary) {
237 let (record_tx, record_rx) = mpsc::unbounded_channel();
238 let (close_trigger_seen_tx, close_trigger_seen_rx) = oneshot::channel();
239 let fut = BuildingConnectionSummary::new(
240 record_rx,
241 close_trigger_frames,
242 close_trigger_seen_tx,
243 );
244
245 (
246 Self {
247 actions,
248 actions_executed: 0,
249 next_fire_time: Instant::now(),
250 waiting_for_responses: WaitingFor::default(),
251 record_tx,
252 stream_parsers: StreamParserMap::default(),
253 close_trigger_seen_rx,
254 },
255 fut,
256 )
257 }
258
259 fn should_fire(&self) -> bool {
261 Instant::now() >= self.next_fire_time
262 }
263
264 fn register_waits(&mut self) {
266 while self.actions_executed < self.actions.len() {
267 if let Action::Wait { wait_type } =
268 &self.actions[self.actions_executed]
269 {
270 self.actions_executed += 1;
271
272 match wait_type {
273 WaitType::WaitDuration(duration) => {
274 self.next_fire_time = Instant::now() + *duration;
275
276 log::debug!(
277 "h3i: waiting for responses: {:?}",
278 self.waiting_for_responses
279 );
280 },
281 WaitType::StreamEvent(event) => {
282 self.waiting_for_responses.add_wait(event);
283 },
284 WaitType::CanOpenNumStreams(required_streams) => {
285 log::info!(
286 "h3i: waiting for peer_streams_left_bidi >= {required_streams:?}"
287 );
288 self.waiting_for_responses
289 .set_required_stream_quota(*required_streams);
290 },
291 }
292 } else {
293 break;
294 }
295 }
296 }
297}
298
299impl Client for H3iDriver {
300 fn stream_parsers_mut(&mut self) -> &mut StreamParserMap {
301 &mut self.stream_parsers
302 }
303
304 fn handle_response_frame(
305 &mut self, stream_id: u64, frame: crate::frame::H3iFrame,
306 ) {
307 self.record_tx
308 .send(ConnectionRecord::StreamedFrame { stream_id, frame })
309 .expect("H3iDriver task dropped")
310 }
311}
312
313impl ApplicationOverQuic for H3iDriver {
314 fn on_conn_established(
315 &mut self, _qconn: &mut QuicheConnection, _handshake_info: &HandshakeInfo,
316 ) -> QuicResult<()> {
317 log::info!("h3i: HTTP/3 connection established");
318 Ok(())
319 }
320
321 fn should_act(&self) -> bool {
322 true
325 }
326
327 fn process_reads(&mut self, qconn: &mut QuicheConnection) -> QuicResult<()> {
328 log::trace!("h3i: process_reads");
329
330 self.register_waits();
334
335 let stream_events = parse_streams(qconn, self);
336 for event in stream_events {
337 self.waiting_for_responses.remove_wait(event);
338 }
339
340 self.waiting_for_responses.check_can_open_num_streams(qconn);
341
342 Ok(())
343 }
344
345 fn process_writes(&mut self, qconn: &mut QuicheConnection) -> QuicResult<()> {
346 log::trace!("h3i: process_writes");
347
348 if !self.waiting_for_responses.is_empty() {
349 log::debug!(
350 "awaiting responses on streams {:?}, skipping further action",
351 self.waiting_for_responses
352 );
353
354 return Ok(());
355 }
356
357 let iter = self.actions.clone().into_iter().skip(self.actions_executed);
359
360 for action in iter {
361 match action {
362 Action::SendFrame { .. } |
363 Action::StreamBytes { .. } |
364 Action::SendDatagram { .. } |
365 Action::ResetStream { .. } |
366 Action::StopSending { .. } |
367 Action::OpenUniStream { .. } |
368 Action::ConnectionClose { .. } |
369 Action::SendHeadersFrame { .. } => {
370 if self.should_fire() {
371 self.next_fire_time = Instant::now();
374
375 execute_action(&action, qconn, self.stream_parsers_mut());
376 self.actions_executed += 1;
377 } else {
378 break;
379 }
380 },
381 Action::Wait { .. } => {
382 break;
387 },
388 Action::FlushPackets => {
389 self.actions_executed += 1;
390 break;
391 },
392 }
393 }
394
395 Ok(())
396 }
397
398 async fn wait_for_data(
399 &mut self, qconn: &mut QuicheConnection,
400 ) -> QuicResult<()> {
401 log::trace!("h3i: wait_for_data");
402
403 let sleep_fut = if !self.should_fire() {
404 sleep_until(self.next_fire_time)
405 } else {
406 sleep(Duration::MAX)
409 };
410
411 select! {
412 rx = &mut self.close_trigger_seen_rx, if !self.close_trigger_seen_rx.is_terminated() => {
413 if rx.is_ok() {
417 let _ = qconn.close(true, quiche::h3::WireErrorCode::NoError as u64, b"saw all expected frames");
419 }
420 }
421 _ = sleep_fut => {}
422 }
423
424 Ok(())
425 }
426
427 fn on_conn_close<M: Metrics>(
428 &mut self, qconn: &mut QuicheConnection, _metrics: &M,
429 _work_loop_result: &QuicResult<()>,
430 ) {
431 let _ = self
432 .record_tx
433 .send(ConnectionRecord::Close(ConnectionCloseDetails::new(qconn)));
434
435 let _ = self
436 .record_tx
437 .send(ConnectionRecord::ConnectionStats(qconn.stats()));
438
439 let conn_path_stats = qconn.path_stats().collect::<Vec<PathStats>>();
440 let _ = self
441 .record_tx
442 .send(ConnectionRecord::PathStats(conn_path_stats));
443 }
444}
445
446pub enum ConnectionRecord {
447 StreamedFrame { stream_id: u64, frame: H3iFrame },
448 Close(ConnectionCloseDetails),
449 PathStats(Vec<PathStats>),
450 ConnectionStats(Stats),
451}