1use std::net::SocketAddr;
28use std::ops::ControlFlow;
29use std::sync::Arc;
30use std::task::Poll;
31use std::time::Duration;
32use std::time::Instant;
33#[cfg(feature = "perf-quic-listener-metrics")]
34use std::time::SystemTime;
35
36use super::connection_stage::Close;
37use super::connection_stage::ConnectionStage;
38use super::connection_stage::ConnectionStageContext;
39use super::connection_stage::Handshake;
40use super::connection_stage::RunningApplication;
41use super::gso::*;
42use super::utilization_estimator::BandwidthReporter;
43
44use crate::metrics::labels;
45use crate::metrics::Metrics;
46use crate::quic::connection::ApplicationOverQuic;
47use crate::quic::connection::HandshakeError;
48use crate::quic::connection::HandshakeInfo;
49use crate::quic::connection::Incoming;
50use crate::quic::connection::QuicConnectionStats;
51use crate::quic::connection::SharedConnectionIdGenerator;
52use crate::quic::hooks::ConnectionHook;
53use crate::quic::router::ConnectionMapCommand;
54use crate::quic::QuicheConnection;
55use crate::QuicResult;
56
57use boring::ssl::SslRef;
58use datagram_socket::DatagramSocketSend;
59use datagram_socket::DatagramSocketSendExt;
60use datagram_socket::MaybeConnectedSocket;
61use datagram_socket::QuicAuditStats;
62use foundations::telemetry::log;
63use quiche::ConnectionId;
64use quiche::Error as QuicheError;
65use quiche::SendInfo;
66use tokio::select;
67use tokio::sync::mpsc;
68use tokio::time;
69
70pub(crate) const INCOMING_QUEUE_SIZE: usize = 2048;
72
73pub(crate) const CHECK_INCOMING_QUEUE_RATIO: usize = INCOMING_QUEUE_SIZE / 16;
76
77const RELEASE_TIMER_THRESHOLD: Duration = Duration::from_micros(250);
78
79const GSO_THRESHOLD: usize = 1_000;
81
82const SEND_BUFFER_SIZE: usize = crate::buf_factory::BufFactory::MAX_BUF_SIZE;
90
91const TRANSIENT_SEND_BUFFER_SIZE: usize = 1500;
97
98fn alloc_send_buffer() -> Box<[u8]> {
106 vec![0u8; SEND_BUFFER_SIZE].into_boxed_slice()
107}
108
109thread_local! {
110 static SEND_BUF_POOL: std::cell::RefCell<Vec<Box<[u8]>>> =
118 const { std::cell::RefCell::new(Vec::new()) };
119}
120
121const SEND_BUF_POOL_CAP: usize = 16;
132
133struct PooledSendBuf(Box<[u8]>);
140
141impl PooledSendBuf {
142 fn acquire() -> Self {
143 let buf = SEND_BUF_POOL
144 .with(|pool| pool.borrow_mut().pop())
145 .unwrap_or_else(|| {
146 crate::metrics::quic::send_buffer_pool_allocated().inc();
150 alloc_send_buffer()
151 });
152 Self(buf)
156 }
157}
158
159impl Drop for PooledSendBuf {
160 fn drop(&mut self) {
161 let buf = std::mem::take(&mut self.0);
166 SEND_BUF_POOL.with(|pool| {
171 let mut pool = pool.borrow_mut();
172 if pool.len() < SEND_BUF_POOL_CAP {
173 pool.push(buf);
174 } else {
175 crate::metrics::quic::send_buffer_pool_discarded().inc();
180 }
181 });
182 }
183}
184
185impl std::ops::Deref for PooledSendBuf {
186 type Target = [u8];
187
188 fn deref(&self) -> &[u8] {
189 &self.0
190 }
191}
192
193impl std::ops::DerefMut for PooledSendBuf {
194 fn deref_mut(&mut self) -> &mut [u8] {
195 &mut self.0
196 }
197}
198
199enum TransientSendBuf {
205 Pooled(PooledSendBuf),
206 Unpooled(Box<[u8]>),
207}
208
209impl TransientSendBuf {
210 fn acquire(pool_send_buffer: bool) -> Self {
211 if pool_send_buffer {
212 Self::Pooled(PooledSendBuf::acquire())
213 } else {
214 Self::Unpooled(
215 vec![0u8; TRANSIENT_SEND_BUFFER_SIZE].into_boxed_slice(),
216 )
217 }
218 }
219}
220
221impl AsRef<[u8]> for TransientSendBuf {
222 fn as_ref(&self) -> &[u8] {
223 match self {
224 Self::Pooled(buf) => &buf[..],
225 Self::Unpooled(buf) => &buf[..],
226 }
227 }
228}
229
230impl AsMut<[u8]> for TransientSendBuf {
231 fn as_mut(&mut self) -> &mut [u8] {
232 match self {
233 Self::Pooled(buf) => &mut buf[..],
234 Self::Unpooled(buf) => &mut buf[..],
235 }
236 }
237}
238
239pub struct WriterConfig {
240 pub pending_cid: Option<ConnectionId<'static>>,
241 pub peer_addr: SocketAddr,
242 pub local_addr: SocketAddr,
243 pub with_gso: bool,
244 pub pacing_offload: bool,
245 pub with_pktinfo: bool,
246 pub pool_send_buffer: bool,
250}
251
252#[derive(Default)]
253pub(crate) struct WriteState {
254 conn_established: bool,
255 bytes_written: usize,
256 segment_size: usize,
257 num_pkts: usize,
258 tx_time: Option<Instant>,
259 has_pending_data: bool,
260 next_release_time: Option<Instant>,
263 selected_path: Option<(SocketAddr, SocketAddr)>,
266 pending_paths: quiche::SocketAddrIter,
268}
269
270pub(crate) struct IoWorkerParams<Tx, M> {
271 pub(crate) socket: MaybeConnectedSocket<Tx>,
272 pub(crate) shutdown_tx: mpsc::Sender<()>,
273 pub(crate) cfg: WriterConfig,
274 pub(crate) audit_log_stats: Arc<QuicAuditStats>,
275 pub(crate) write_state: WriteState,
276 pub(crate) conn_map_cmd_tx: mpsc::UnboundedSender<ConnectionMapCommand>,
277 pub(crate) cid_generator: Option<SharedConnectionIdGenerator>,
278 #[cfg(feature = "perf-quic-listener-metrics")]
279 pub(crate) init_rx_time: Option<SystemTime>,
280 pub(crate) metrics: M,
281 pub(crate) handshake_info: HandshakeInfo,
282}
283
284fn notify_path_events(
285 connection_hook: Option<&(dyn ConnectionHook + Send + Sync + 'static)>,
286 qconn: &mut QuicheConnection,
287) {
288 while let Some(path_event) = qconn.path_event_next() {
289 if let Some(hook) = connection_hook {
290 hook.on_path_event(qconn, &path_event);
291 }
292 }
293}
294
295pub(crate) struct IoWorker<Tx, M, S> {
296 socket: MaybeConnectedSocket<Tx>,
297 shutdown_tx: mpsc::Sender<()>,
301 cfg: WriterConfig,
302 audit_log_stats: Arc<QuicAuditStats>,
303 write_state: WriteState,
304 conn_map_cmd_tx: mpsc::UnboundedSender<ConnectionMapCommand>,
305 cid_generator: Option<SharedConnectionIdGenerator>,
306 #[cfg(feature = "perf-quic-listener-metrics")]
307 init_rx_time: Option<SystemTime>,
308 metrics: M,
309 handshake_info: HandshakeInfo,
315 conn_stage: S,
316 bw_estimator: BandwidthReporter,
317}
318
319impl<Tx, M, S> IoWorker<Tx, M, S>
320where
321 Tx: DatagramSocketSend + Send,
322 M: Metrics,
323 S: ConnectionStage,
324{
325 pub(crate) fn new(params: IoWorkerParams<Tx, M>, conn_stage: S) -> Self {
326 let bw_estimator =
327 BandwidthReporter::new(params.metrics.utilized_bandwidth());
328
329 log::trace!("Creating IoWorker with stage: {conn_stage:?}");
330
331 Self {
332 socket: params.socket,
333 shutdown_tx: params.shutdown_tx,
334 cfg: params.cfg,
335 audit_log_stats: params.audit_log_stats,
336 write_state: params.write_state,
337 conn_map_cmd_tx: params.conn_map_cmd_tx,
338 cid_generator: params.cid_generator,
339 #[cfg(feature = "perf-quic-listener-metrics")]
340 init_rx_time: params.init_rx_time,
341 metrics: params.metrics,
342 handshake_info: params.handshake_info,
343 conn_stage,
344 bw_estimator,
345 }
346 }
347
348 fn fill_available_scids(&self, qconn: &mut QuicheConnection) {
349 if qconn.scids_left() == 0 {
350 return;
351 }
352 let Some(cid_generator) = self.cid_generator.as_deref() else {
353 return;
354 };
355
356 let current_cid = qconn.source_id().into_owned();
357 for _ in 0..qconn.scids_left() {
358 let reset_token = random_u128();
360 let new_cid = cid_generator.new_connection_id();
361
362 if self
363 .conn_map_cmd_tx
364 .send(ConnectionMapCommand::MapCid {
365 existing_cid: current_cid.clone(),
366 new_cid: new_cid.clone(),
367 })
368 .is_err()
369 {
370 return;
372 }
373
374 if qconn.new_scid(&new_cid, reset_token, false).is_err() {
375 return;
377 }
378 }
379 }
380
381 fn unmap_cid(&self, cid: ConnectionId<'static>) {
382 let _ = self
384 .conn_map_cmd_tx
385 .send(ConnectionMapCommand::UnmapCid(cid));
386 }
387
388 fn refresh_connection_ids(&self, qconn: &mut QuicheConnection) {
389 self.fill_available_scids(qconn);
391
392 while let Some(retired_cid) = qconn.retired_scid_next() {
394 self.unmap_cid(retired_cid);
395 }
396 }
397
398 async fn work_loop<A: ApplicationOverQuic>(
399 &mut self, qconn: &mut QuicheConnection,
400 ctx: &mut ConnectionStageContext<A>,
401 ) -> QuicResult<()> {
402 const DEFAULT_SLEEP: Duration = Duration::from_secs(60);
403 let mut current_deadline: Option<Instant> = None;
404 let sleep = time::sleep(DEFAULT_SLEEP);
405 tokio::pin!(sleep);
406
407 let mut persistent_send_buf: Option<Box<[u8]>> =
411 (!self.cfg.pool_send_buffer).then(alloc_send_buffer);
412
413 loop {
414 let now = Instant::now();
415
416 self.write_state.has_pending_data = true;
417
418 let mut pooled_send_buf: Option<PooledSendBuf> = None;
424
425 while self.write_state.has_pending_data {
426 let mut packets_sent = 0;
427
428 let mut did_recv = false;
431 while let Some(pkt) = ctx
432 .in_pkt
433 .take()
434 .or_else(|| ctx.incoming_pkt_receiver.try_recv().ok())
435 {
436 self.process_incoming(qconn, pkt)?;
437 did_recv = true;
438 }
439
440 notify_path_events(ctx.connection_hook.as_deref(), qconn);
443
444 self.conn_stage.on_read(did_recv, qconn, ctx)?;
445
446 notify_path_events(ctx.connection_hook.as_deref(), qconn);
448 self.refresh_connection_ids(qconn);
449
450 let can_release = match self.write_state.next_release_time {
451 None => true,
452 Some(next_release) =>
453 next_release
454 .checked_duration_since(now)
455 .unwrap_or_default() <
456 RELEASE_TIMER_THRESHOLD,
457 };
458
459 self.write_state.has_pending_data &= can_release;
460
461 while self.write_state.has_pending_data &&
462 packets_sent < CHECK_INCOMING_QUEUE_RATIO
463 {
464 let send_buf: &mut [u8] =
471 if let Some(buf) = persistent_send_buf.as_deref_mut() {
472 buf
473 } else {
474 &mut pooled_send_buf
475 .get_or_insert_with(PooledSendBuf::acquire)[..]
476 };
477
478 self.gather_data_from_quiche_conn(qconn, send_buf, false)?;
479
480 if qconn.is_closed() {
482 return Ok(());
483 }
484
485 let mut flush_operation_token =
486 TrackMidHandshakeFlush::new(self.metrics.clone());
487
488 self.flush_buffer_to_socket(&send_buf[..]).await;
489
490 flush_operation_token.mark_complete();
491
492 packets_sent += self.write_state.num_pkts;
493
494 if let ControlFlow::Break(reason) =
495 self.conn_stage.on_flush(qconn, ctx)
496 {
497 return reason;
498 }
499 }
500 }
501
502 drop(pooled_send_buf);
507
508 self.bw_estimator.update(qconn, now);
509
510 self.audit_log_stats
511 .set_max_bandwidth(self.bw_estimator.max_bandwidth);
512 self.audit_log_stats.set_max_loss_pct(
513 (self.bw_estimator.max_loss_pct * 100_f32).round() as u8,
514 );
515
516 let new_deadline = min_of_some(
517 qconn.timeout_instant(),
518 self.write_state.next_release_time,
519 );
520 let new_deadline =
521 min_of_some(new_deadline, self.handshake_deadline(qconn));
522
523 if new_deadline != current_deadline {
524 current_deadline = new_deadline;
525
526 sleep
527 .as_mut()
528 .reset(new_deadline.unwrap_or(now + DEFAULT_SLEEP).into());
529 }
530
531 let incoming_recv = &mut ctx.incoming_pkt_receiver;
532 let application = &mut ctx.application;
533
534 select! {
535 biased;
536 () = &mut sleep => {
537 qconn.on_timeout();
544
545 self.write_state.next_release_time = None;
546 current_deadline = None;
547 sleep.as_mut().reset((now + DEFAULT_SLEEP).into());
548 }
549 Some(pkt) = incoming_recv.recv() => ctx.in_pkt = Some(pkt),
550 directive = self.wait_for_data_or_handshake(qconn, application) => {
551 match directive? {
552 WaitForDataOrHandshakeDirective::Flush(send_buf) => {
553 self.flush_buffer_to_socket(send_buf.as_ref()).await;
558 }
559 WaitForDataOrHandshakeDirective::Noop => {}
560 }
561 },
562 };
563
564 self.enforce_handshake_deadline(qconn)?;
565 }
566 }
567
568 #[cfg(feature = "perf-quic-listener-metrics")]
569 fn measure_complete_handshake_time(&mut self) {
570 if let Some(init_rx_time) = self.init_rx_time.take() {
571 if let Ok(delta) = init_rx_time.elapsed() {
572 self.metrics
573 .handshake_time_seconds(
574 labels::QuicHandshakeStage::HandshakeResponse,
575 )
576 .observe(delta.as_nanos() as u64);
577 }
578 }
579 }
580
581 fn gather_data_from_quiche_conn(
586 &mut self, qconn: &mut QuicheConnection, send_buf: &mut [u8],
587 single_packet: bool,
588 ) -> QuicResult<usize> {
589 let mut segment_size = None;
590 let mut send_info = None;
591
592 self.write_state.num_pkts = 0;
593 self.write_state.bytes_written = 0;
594
595 self.write_state.selected_path = None;
596
597 let now = Instant::now();
598
599 let send_buf = {
600 let trunc = UDP_MAX_GSO_PACKET_SIZE.min(send_buf.len());
601 &mut send_buf[..trunc]
602 };
603
604 #[cfg(feature = "gcongestion")]
605 let gcongestion_enabled = true;
606
607 #[cfg(not(feature = "gcongestion"))]
608 let gcongestion_enabled = qconn.gcongestion_enabled().unwrap_or(false);
609
610 let initial_release_decision = if gcongestion_enabled {
611 let initial_release_decision = qconn
612 .get_next_release_time()
613 .filter(|_| self.pacing_enabled(qconn));
614
615 if let Some(future_release_time) =
616 initial_release_decision.as_ref().and_then(|v| v.time(now))
617 {
618 let max_into_fut = qconn.max_release_into_future();
619
620 if future_release_time.duration_since(now) >= max_into_fut {
621 self.write_state.next_release_time =
622 Some(now + max_into_fut.mul_f32(0.8));
623 self.write_state.has_pending_data = false;
624 return Ok(0);
625 }
626 }
627
628 initial_release_decision
629 } else {
630 None
631 };
632
633 let buffer_write_outcome = loop {
634 let outcome = self.write_packet_to_buffer(
635 qconn,
636 send_buf,
637 &mut send_info,
638 segment_size,
639 );
640
641 let packet_size = match outcome {
642 Ok(0) => break Ok(0),
643
644 Ok(bytes_written) => bytes_written,
645
646 Err(e) => break Err(e),
647 };
648
649 if single_packet || !self.cfg.with_gso {
652 break outcome;
653 }
654
655 #[cfg(not(feature = "gcongestion"))]
656 let max_send_size = if !gcongestion_enabled {
657 tune_max_send_size(
659 segment_size,
660 qconn.send_quantum(),
661 send_buf.len(),
662 )
663 } else {
664 usize::MAX
665 };
666
667 #[cfg(feature = "gcongestion")]
668 let max_send_size = usize::MAX;
669
670 let buffer_is_full = self.write_state.num_pkts ==
674 UDP_MAX_SEGMENT_COUNT ||
675 self.write_state.bytes_written >= max_send_size;
676
677 if buffer_is_full {
678 break outcome;
679 }
680
681 match segment_size {
686 Some(size)
687 if packet_size != size || packet_size < GSO_THRESHOLD =>
688 break outcome,
689 None => segment_size = Some(packet_size),
690 _ => (),
691 }
692
693 if gcongestion_enabled {
694 if let Some(initial_release_decision) = initial_release_decision {
697 match qconn.get_next_release_time() {
698 Some(release)
699 if release.can_burst() ||
700 release.time_eq(
701 &initial_release_decision,
702 now,
703 ) => {},
704 _ => break outcome,
705 }
706 }
707 }
708 };
709
710 let tx_time = if gcongestion_enabled {
711 initial_release_decision
712 .filter(|_| self.pacing_enabled(qconn))
713 .and_then(|v| v.time(now))
716 } else {
717 send_info
718 .filter(|_| self.pacing_enabled(qconn))
719 .map(|v| v.at)
720 };
721
722 self.write_state.conn_established = qconn.is_established();
723 self.write_state.tx_time = tx_time;
724 self.write_state.segment_size =
725 segment_size.unwrap_or(self.write_state.bytes_written);
726
727 if !gcongestion_enabled {
728 if let Some(time) = tx_time {
729 const DEFAULT_MAX_INTO_FUTURE: Duration =
730 Duration::from_millis(1);
731 if time
732 .checked_duration_since(now)
733 .map(|d| d > DEFAULT_MAX_INTO_FUTURE)
734 .unwrap_or(false)
735 {
736 self.write_state.next_release_time =
737 Some(now + DEFAULT_MAX_INTO_FUTURE.mul_f32(0.8));
738 self.write_state.has_pending_data = false;
739 return Ok(0);
740 }
741 }
742 }
743
744 buffer_write_outcome
745 }
746
747 fn select_path(
757 &mut self, qconn: &QuicheConnection,
758 ) -> Option<(SocketAddr, SocketAddr)> {
759 if self.write_state.selected_path.is_some() {
760 return self.write_state.selected_path;
761 }
762
763 let from = self.cfg.local_addr;
764
765 if self.write_state.pending_paths.len() == 0 {
767 self.write_state.pending_paths = qconn.paths_iter(from);
768 }
769
770 let to = self.write_state.pending_paths.next()?;
771
772 Some((from, to))
773 }
774
775 #[cfg(not(feature = "gcongestion"))]
776 fn pacing_enabled(&self, qconn: &QuicheConnection) -> bool {
777 self.cfg.pacing_offload && qconn.pacing_enabled()
778 }
779
780 #[cfg(feature = "gcongestion")]
781 fn pacing_enabled(&self, _qconn: &QuicheConnection) -> bool {
782 self.cfg.pacing_offload
783 }
784
785 fn write_packet_to_buffer(
786 &mut self, qconn: &mut QuicheConnection, send_buf: &mut [u8],
787 send_info: &mut Option<SendInfo>, segment_size: Option<usize>,
788 ) -> QuicResult<usize> {
789 let mut send_buf = &mut send_buf[self.write_state.bytes_written..];
790 if send_buf.len() > segment_size.unwrap_or(usize::MAX) {
791 send_buf = &mut send_buf[..segment_size.unwrap_or(usize::MAX)];
794 }
795
796 let (from, to) = self.select_path(qconn).unzip();
807
808 match qconn.send_on_path(send_buf, from, to) {
809 Ok((packet_size, info)) => {
810 let _ = send_info.get_or_insert(info);
811
812 self.write_state.bytes_written += packet_size;
813 self.write_state.num_pkts += 1;
814
815 let from = send_info.as_ref().map(|info| info.from);
816 let to = send_info.as_ref().map(|info| info.to);
817
818 self.write_state.selected_path = from.zip(to);
819
820 self.write_state.has_pending_data = true;
821
822 Ok(packet_size)
823 },
824
825 Err(QuicheError::Done) => {
826 let has_pending_paths = self.write_state.pending_paths.len() > 0;
832
833 self.write_state.has_pending_data = has_pending_paths;
835
836 Ok(0)
837 },
838
839 Err(e) => {
840 let error_code = if let Some(local_error) = qconn.local_error() {
841 local_error.error_code
842 } else {
843 let internal_error_code =
844 quiche::WireErrorCode::InternalError as u64;
845 let _ = qconn.close(false, internal_error_code, &[]);
846
847 internal_error_code
848 };
849
850 self.audit_log_stats
851 .set_sent_conn_close_transport_error_code(error_code as i64);
852
853 Err(Box::new(e))
854 },
855 }
856 }
857
858 async fn flush_buffer_to_socket(&mut self, send_buf: &[u8]) {
859 if self.write_state.bytes_written > 0 {
860 let current_send_buf = &send_buf[..self.write_state.bytes_written];
861
862 let (from, to) = self.write_state.selected_path.unzip();
863
864 let to = to.unwrap_or(self.cfg.peer_addr);
865 let from = from.filter(|_| self.cfg.with_pktinfo);
866
867 let send_res = if let (Some(udp_socket), true) =
868 (self.socket.as_udp_socket(), self.cfg.with_gso)
869 {
870 send_to(
872 udp_socket,
873 to,
874 from,
875 current_send_buf,
876 self.write_state.segment_size,
877 self.write_state.tx_time,
878 self.metrics
879 .write_errors(labels::QuicWriteError::WouldBlock),
880 self.metrics.send_to_wouldblock_duration_s(),
881 )
882 .await
883 } else {
884 self.socket.send_to(current_send_buf, to).await
885 };
886
887 #[cfg(feature = "perf-quic-listener-metrics")]
888 self.measure_complete_handshake_time();
889
890 match send_res {
891 Ok(n) =>
892 if n < self.write_state.bytes_written {
893 self.metrics
894 .write_errors(labels::QuicWriteError::Partial)
895 .inc();
896 },
897
898 Err(_) => {
899 self.metrics.write_errors(labels::QuicWriteError::Err).inc();
900 },
901 }
902 }
903 }
904
905 fn handshake_deadline(&self, qconn: &QuicheConnection) -> Option<Instant> {
908 if qconn.is_established() {
909 return None;
910 }
911
912 self.handshake_info.deadline()
913 }
914
915 fn enforce_handshake_deadline(
918 &self, qconn: &mut QuicheConnection,
919 ) -> QuicResult<()> {
920 if !qconn.is_established() && self.handshake_info.is_expired() {
921 let err = quiche::WireErrorCode::ApplicationError as u64;
922 let _ = qconn.close(false, err, &[]);
923 return Err(HandshakeError::Timeout.into());
924 }
925
926 Ok(())
927 }
928
929 fn process_incoming(
931 &mut self, qconn: &mut QuicheConnection, mut pkt: Incoming,
932 ) -> QuicResult<()> {
933 self.enforce_handshake_deadline(qconn)?;
936
937 let recv_info = quiche::RecvInfo {
938 from: pkt.peer_addr,
939 to: pkt.local_addr,
940 };
941
942 if let Some(gro) = pkt.gro {
943 for dgram in pkt.buf.chunks_mut(gro as usize) {
944 qconn.recv(dgram, recv_info)?;
945 }
946 } else {
947 qconn.recv(&mut pkt.buf, recv_info)?;
948 }
949
950 Ok(())
951 }
952
953 async fn wait_for_data_or_handshake<A: ApplicationOverQuic>(
969 &mut self, qconn: &mut QuicheConnection, quic_application: &mut A,
970 ) -> QuicResult<WaitForDataOrHandshakeDirective> {
971 if quic_application.should_act() {
972 quic_application.wait_for_data(qconn).await?;
982 Ok(WaitForDataOrHandshakeDirective::Noop)
983 } else {
984 let send_buf = self.wait_for_quiche(qconn).await?;
989 Ok(WaitForDataOrHandshakeDirective::Flush(send_buf))
990 }
991 }
992
993 async fn wait_for_quiche(
1014 &mut self, qconn: &mut QuicheConnection,
1015 ) -> QuicResult<TransientSendBuf> {
1016 let send_buf = std::future::poll_fn(|_| {
1017 let mut send_buf =
1021 TransientSendBuf::acquire(self.cfg.pool_send_buffer);
1022
1023 match self.gather_data_from_quiche_conn(
1024 qconn,
1025 send_buf.as_mut(),
1026 true,
1027 ) {
1028 Ok(bytes_written) => {
1029 if bytes_written == 0 && self.write_state.bytes_written == 0 {
1033 Poll::Pending
1034 } else {
1035 Poll::Ready(Ok(send_buf))
1036 }
1037 },
1038 _ => Poll::Ready(Err(quiche::Error::TlsFail)),
1039 }
1040 })
1041 .await?;
1042 Ok(send_buf)
1043 }
1044}
1045
1046#[must_use]
1052enum WaitForDataOrHandshakeDirective {
1053 Noop,
1054 Flush(TransientSendBuf),
1055}
1056
1057pub struct Running<Tx, M, A> {
1058 pub(crate) params: IoWorkerParams<Tx, M>,
1059 pub(crate) context: ConnectionStageContext<A>,
1060 pub(crate) qconn: Box<QuicheConnection>,
1062}
1063
1064impl<Tx, M, A> Running<Tx, M, A> {
1065 pub fn ssl(&mut self) -> &mut SslRef {
1066 (*self.qconn).as_mut()
1068 }
1069}
1070
1071pub(crate) struct Closing<Tx, M, A> {
1072 pub(crate) params: IoWorkerParams<Tx, M>,
1073 pub(crate) context: ConnectionStageContext<A>,
1074 pub(crate) work_loop_result: QuicResult<()>,
1075 pub(crate) qconn: Box<QuicheConnection>,
1077}
1078
1079pub enum RunningOrClosing<Tx, M, A> {
1080 Running(Running<Tx, M, A>),
1081 Closing(Closing<Tx, M, A>),
1082}
1083
1084impl<Tx, M> IoWorker<Tx, M, Handshake>
1085where
1086 Tx: DatagramSocketSend + Send,
1087 M: Metrics,
1088{
1089 pub(crate) async fn run<A>(
1090 mut self, mut qconn: Box<QuicheConnection>,
1091 mut ctx: ConnectionStageContext<A>,
1092 ) -> RunningOrClosing<Tx, M, A>
1093 where
1094 A: ApplicationOverQuic,
1095 {
1096 std::future::poll_fn(|cx| {
1100 let ssl = (*qconn).as_mut();
1102 ssl.set_task_waker(Some(cx.waker().clone()));
1103
1104 Poll::Ready(())
1105 })
1106 .await;
1107
1108 #[cfg(target_os = "linux")]
1109 if let Some(incoming) = ctx.in_pkt.as_mut() {
1110 self.audit_log_stats
1111 .set_initial_so_mark_data(incoming.so_mark_data.take());
1112 }
1113
1114 let mut work_loop_result = self.work_loop(&mut qconn, &mut ctx).await;
1115 notify_path_events(ctx.connection_hook.as_deref(), &mut qconn);
1116 if work_loop_result.is_ok() && qconn.is_closed() {
1117 work_loop_result = Err(HandshakeError::ConnectionClosed.into());
1118 }
1119
1120 if let Err(err) = &work_loop_result {
1121 self.metrics.failed_handshakes(err.into()).inc();
1122
1123 return RunningOrClosing::Closing(Closing {
1124 params: self.into(),
1125 context: ctx,
1126 work_loop_result,
1127 qconn,
1128 });
1129 };
1130
1131 let on_conn_established_result =
1132 self.on_conn_established(&mut qconn, &mut ctx.application);
1133 notify_path_events(ctx.connection_hook.as_deref(), &mut qconn);
1134
1135 match on_conn_established_result {
1136 Ok(()) => RunningOrClosing::Running(Running {
1137 params: self.into(),
1138 context: ctx,
1139 qconn,
1140 }),
1141 Err(e) => {
1142 foundations::telemetry::log::warn!(
1143 "Handshake stage on_connection_established failed"; "error"=>%e
1144 );
1145
1146 RunningOrClosing::Closing(Closing {
1147 params: self.into(),
1148 context: ctx,
1149 work_loop_result,
1150 qconn,
1151 })
1152 },
1153 }
1154 }
1155
1156 fn on_conn_established<App: ApplicationOverQuic>(
1157 &mut self, qconn: &mut QuicheConnection, driver: &mut App,
1158 ) -> QuicResult<()> {
1159 if self.audit_log_stats.transport_handshake_duration_us() == -1 {
1163 self.handshake_info.set_elapsed();
1164 let handshake_info = &self.handshake_info;
1165
1166 self.audit_log_stats
1167 .set_transport_handshake_duration(handshake_info.elapsed());
1168
1169 driver.on_conn_established(qconn, handshake_info)?;
1170 }
1171
1172 if let Some(cid) = self.cfg.pending_cid.take() {
1173 self.unmap_cid(cid);
1174 }
1175
1176 Ok(())
1177 }
1178}
1179
1180impl<Tx, M, S> From<IoWorker<Tx, M, S>> for IoWorkerParams<Tx, M> {
1181 fn from(value: IoWorker<Tx, M, S>) -> Self {
1182 Self {
1183 socket: value.socket,
1184 shutdown_tx: value.shutdown_tx,
1185 cfg: value.cfg,
1186 audit_log_stats: value.audit_log_stats,
1187 write_state: value.write_state,
1188 conn_map_cmd_tx: value.conn_map_cmd_tx,
1189 cid_generator: value.cid_generator,
1190 #[cfg(feature = "perf-quic-listener-metrics")]
1191 init_rx_time: value.init_rx_time,
1192 metrics: value.metrics,
1193 handshake_info: value.handshake_info,
1194 }
1195 }
1196}
1197
1198impl<Tx, M> IoWorker<Tx, M, RunningApplication>
1199where
1200 Tx: DatagramSocketSend + Send,
1201 M: Metrics,
1202{
1203 pub(crate) async fn run<A: ApplicationOverQuic>(
1204 mut self, mut qconn: Box<QuicheConnection>,
1205 mut ctx: ConnectionStageContext<A>,
1206 ) -> Closing<Tx, M, A> {
1207 let mut work_loop_result = self.enforce_handshake_deadline(&mut qconn);
1210
1211 if work_loop_result.is_ok() {
1212 work_loop_result =
1217 self.conn_stage.on_read(true, &mut qconn, &mut ctx);
1218 notify_path_events(ctx.connection_hook.as_deref(), &mut qconn);
1219 }
1220
1221 if work_loop_result.is_ok() {
1222 work_loop_result = self.work_loop(&mut qconn, &mut ctx).await;
1223 notify_path_events(ctx.connection_hook.as_deref(), &mut qconn);
1224 }
1225
1226 if !qconn.is_established() {
1230 let reason = match &work_loop_result {
1231 Err(err) => err.into(),
1232 Ok(()) => labels::HandshakeError::Disconnect,
1233 };
1234
1235 self.metrics.failed_handshakes(reason).inc();
1236 }
1237
1238 Closing {
1239 params: self.into(),
1240 context: ctx,
1241 work_loop_result,
1242 qconn,
1243 }
1244 }
1245}
1246
1247impl<Tx, M> IoWorker<Tx, M, Close>
1248where
1249 Tx: DatagramSocketSend + Send,
1250 M: Metrics,
1251{
1252 pub(crate) async fn close<A: ApplicationOverQuic>(
1253 mut self, qconn: &mut QuicheConnection,
1254 ctx: &mut ConnectionStageContext<A>,
1255 ) {
1256 if self.conn_stage.work_loop_result.is_ok() &&
1257 self.bw_estimator.max_bandwidth > 0
1258 {
1259 let metrics = &self.metrics;
1260
1261 metrics
1262 .max_bandwidth_mbps()
1263 .observe(self.bw_estimator.max_bandwidth as f64 * 1e-6);
1264
1265 metrics
1266 .max_loss_pct()
1267 .observe(self.bw_estimator.max_loss_pct as f64 * 100.);
1268 }
1269
1270 if ctx.application.should_act() {
1271 ctx.application.on_conn_close(
1272 qconn,
1273 &self.metrics,
1274 &self.conn_stage.work_loop_result,
1275 );
1276 notify_path_events(ctx.connection_hook.as_deref(), qconn);
1277 }
1278
1279 let mut send_buf = TransientSendBuf::acquire(self.cfg.pool_send_buffer);
1287 let _ =
1288 self.gather_data_from_quiche_conn(qconn, send_buf.as_mut(), false);
1289 self.flush_buffer_to_socket(send_buf.as_ref()).await;
1290
1291 *ctx.stats.lock().unwrap() = QuicConnectionStats::from_conn(qconn);
1292
1293 if let Some(err) = qconn.peer_error() {
1294 if err.is_app {
1295 self.audit_log_stats
1296 .set_recvd_conn_close_application_error_code(
1297 err.error_code as _,
1298 );
1299 } else {
1300 self.audit_log_stats
1301 .set_recvd_conn_close_transport_error_code(
1302 err.error_code as _,
1303 );
1304 }
1305 }
1306
1307 if let Some(err) = qconn.local_error() {
1308 if err.is_app {
1309 self.audit_log_stats
1310 .set_sent_conn_close_application_error_code(
1311 err.error_code as _,
1312 );
1313 } else {
1314 self.audit_log_stats
1315 .set_sent_conn_close_transport_error_code(
1316 err.error_code as _,
1317 );
1318 }
1319 }
1320
1321 self.close_connection(qconn);
1322
1323 if let Err(work_loop_error) = self.conn_stage.work_loop_result {
1324 self.audit_log_stats
1325 .set_connection_close_reason(work_loop_error);
1326 }
1327 }
1328
1329 fn close_connection(&mut self, qconn: &mut QuicheConnection) {
1330 if let Some(cid) = self.cfg.pending_cid.take() {
1331 self.unmap_cid(cid);
1332 }
1333 while let Some(retired_cid) = qconn.retired_scid_next() {
1334 self.unmap_cid(retired_cid);
1335 }
1336 for cid in qconn.source_ids().cloned() {
1337 self.unmap_cid(cid.into_owned());
1338 }
1339
1340 self.metrics.connections_in_memory().dec();
1341 }
1342}
1343
1344fn min_of_some<T: Ord>(v1: Option<T>, v2: Option<T>) -> Option<T> {
1346 match (v1, v2) {
1347 (Some(a), Some(b)) => Some(a.min(b)),
1348 (Some(v), _) | (_, Some(v)) => Some(v),
1349 (None, None) => None,
1350 }
1351}
1352
1353struct TrackMidHandshakeFlush<M: Metrics> {
1356 complete: bool,
1357 metrics: M,
1358}
1359
1360impl<M: Metrics> TrackMidHandshakeFlush<M> {
1361 fn new(metrics: M) -> Self {
1362 Self {
1363 complete: false,
1364 metrics,
1365 }
1366 }
1367
1368 fn mark_complete(&mut self) {
1369 self.complete = true;
1370 }
1371}
1372
1373impl<M: Metrics> Drop for TrackMidHandshakeFlush<M> {
1374 fn drop(&mut self) {
1375 if !self.complete {
1376 self.metrics.skipped_mid_handshake_flush_count().inc();
1377 }
1378 }
1379}
1380
1381fn random_u128() -> u128 {
1382 let mut buf = [0; 16];
1383 boring::rand::rand_bytes(&mut buf).expect("boring's RAND_bytes never fails");
1384 u128::from_ne_bytes(buf)
1385}
1386
1387#[cfg(test)]
1388mod pooled_send_buf_tests {
1389 use super::*;
1390
1391 #[test]
1396 fn caps_retained_buffers() {
1397 std::thread::spawn(|| {
1398 let bufs: Vec<PooledSendBuf> = (0..SEND_BUF_POOL_CAP + 4)
1402 .map(|_| PooledSendBuf::acquire())
1403 .collect();
1404 drop(bufs);
1405
1406 let retained = SEND_BUF_POOL.with(|pool| pool.borrow().len());
1407 assert_eq!(retained, SEND_BUF_POOL_CAP);
1408 })
1409 .join()
1410 .unwrap();
1411 }
1412
1413 #[test]
1414 fn reuses_a_returned_buffer() {
1415 std::thread::spawn(|| {
1416 let first_ptr = {
1417 let buf = PooledSendBuf::acquire();
1418 assert_eq!(buf.len(), SEND_BUFFER_SIZE);
1419 buf.as_ptr()
1420 }; let reused = PooledSendBuf::acquire();
1423 assert_eq!(
1424 reused.as_ptr(),
1425 first_ptr,
1426 "acquire should hand back the pooled allocation"
1427 );
1428 })
1429 .join()
1430 .unwrap();
1431 }
1432
1433 #[test]
1434 fn returns_to_the_dropping_thread() {
1435 let buf = std::thread::spawn(PooledSendBuf::acquire).join().unwrap();
1439
1440 std::thread::spawn(move || {
1441 assert_eq!(SEND_BUF_POOL.with(|pool| pool.borrow().len()), 0);
1442 drop(buf);
1443 assert_eq!(SEND_BUF_POOL.with(|pool| pool.borrow().len()), 1);
1444 })
1445 .join()
1446 .unwrap();
1447 }
1448}