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::Incoming;
49use crate::quic::connection::QuicConnectionStats;
50use crate::quic::connection::SharedConnectionIdGenerator;
51use crate::quic::router::ConnectionMapCommand;
52use crate::quic::QuicheConnection;
53use crate::QuicResult;
54
55use boring::ssl::SslRef;
56use datagram_socket::DatagramSocketSend;
57use datagram_socket::DatagramSocketSendExt;
58use datagram_socket::MaybeConnectedSocket;
59use datagram_socket::QuicAuditStats;
60use foundations::telemetry::log;
61use quiche::ConnectionId;
62use quiche::Error as QuicheError;
63use quiche::SendInfo;
64use tokio::select;
65use tokio::sync::mpsc;
66use tokio::time;
67
68pub(crate) const INCOMING_QUEUE_SIZE: usize = 2048;
70
71pub(crate) const CHECK_INCOMING_QUEUE_RATIO: usize = INCOMING_QUEUE_SIZE / 16;
74
75const RELEASE_TIMER_THRESHOLD: Duration = Duration::from_micros(250);
76
77const GSO_THRESHOLD: usize = 1_000;
79
80const SEND_BUFFER_SIZE: usize = crate::buf_factory::BufFactory::MAX_BUF_SIZE;
88
89const TRANSIENT_SEND_BUFFER_SIZE: usize = 1500;
95
96fn alloc_send_buffer() -> Box<[u8]> {
104 vec![0u8; SEND_BUFFER_SIZE].into_boxed_slice()
105}
106
107thread_local! {
108 static SEND_BUF_POOL: std::cell::RefCell<Vec<Box<[u8]>>> =
116 const { std::cell::RefCell::new(Vec::new()) };
117}
118
119const SEND_BUF_POOL_CAP: usize = 16;
130
131struct PooledSendBuf(Box<[u8]>);
138
139impl PooledSendBuf {
140 fn acquire() -> Self {
141 let buf = SEND_BUF_POOL
142 .with(|pool| pool.borrow_mut().pop())
143 .unwrap_or_else(|| {
144 crate::metrics::quic::send_buffer_pool_allocated().inc();
148 alloc_send_buffer()
149 });
150 Self(buf)
154 }
155}
156
157impl Drop for PooledSendBuf {
158 fn drop(&mut self) {
159 let buf = std::mem::take(&mut self.0);
164 SEND_BUF_POOL.with(|pool| {
169 let mut pool = pool.borrow_mut();
170 if pool.len() < SEND_BUF_POOL_CAP {
171 pool.push(buf);
172 } else {
173 crate::metrics::quic::send_buffer_pool_discarded().inc();
178 }
179 });
180 }
181}
182
183impl std::ops::Deref for PooledSendBuf {
184 type Target = [u8];
185
186 fn deref(&self) -> &[u8] {
187 &self.0
188 }
189}
190
191impl std::ops::DerefMut for PooledSendBuf {
192 fn deref_mut(&mut self) -> &mut [u8] {
193 &mut self.0
194 }
195}
196
197enum TransientSendBuf {
203 Pooled(PooledSendBuf),
204 Unpooled(Box<[u8]>),
205}
206
207impl TransientSendBuf {
208 fn acquire(pool_send_buffer: bool) -> Self {
209 if pool_send_buffer {
210 Self::Pooled(PooledSendBuf::acquire())
211 } else {
212 Self::Unpooled(
213 vec![0u8; TRANSIENT_SEND_BUFFER_SIZE].into_boxed_slice(),
214 )
215 }
216 }
217}
218
219impl AsRef<[u8]> for TransientSendBuf {
220 fn as_ref(&self) -> &[u8] {
221 match self {
222 Self::Pooled(buf) => &buf[..],
223 Self::Unpooled(buf) => &buf[..],
224 }
225 }
226}
227
228impl AsMut<[u8]> for TransientSendBuf {
229 fn as_mut(&mut self) -> &mut [u8] {
230 match self {
231 Self::Pooled(buf) => &mut buf[..],
232 Self::Unpooled(buf) => &mut buf[..],
233 }
234 }
235}
236
237pub struct WriterConfig {
238 pub pending_cid: Option<ConnectionId<'static>>,
239 pub peer_addr: SocketAddr,
240 pub local_addr: SocketAddr,
241 pub with_gso: bool,
242 pub pacing_offload: bool,
243 pub with_pktinfo: bool,
244 pub pool_send_buffer: bool,
248}
249
250#[derive(Default)]
251pub(crate) struct WriteState {
252 conn_established: bool,
253 bytes_written: usize,
254 segment_size: usize,
255 num_pkts: usize,
256 tx_time: Option<Instant>,
257 has_pending_data: bool,
258 next_release_time: Option<Instant>,
261 selected_path: Option<(SocketAddr, SocketAddr)>,
264 pending_paths: quiche::SocketAddrIter,
266}
267
268pub(crate) struct IoWorkerParams<Tx, M> {
269 pub(crate) socket: MaybeConnectedSocket<Tx>,
270 pub(crate) shutdown_tx: mpsc::Sender<()>,
271 pub(crate) cfg: WriterConfig,
272 pub(crate) audit_log_stats: Arc<QuicAuditStats>,
273 pub(crate) write_state: WriteState,
274 pub(crate) conn_map_cmd_tx: mpsc::UnboundedSender<ConnectionMapCommand>,
275 pub(crate) cid_generator: Option<SharedConnectionIdGenerator>,
276 #[cfg(feature = "perf-quic-listener-metrics")]
277 pub(crate) init_rx_time: Option<SystemTime>,
278 pub(crate) metrics: M,
279}
280
281pub(crate) struct IoWorker<Tx, M, S> {
282 socket: MaybeConnectedSocket<Tx>,
283 shutdown_tx: mpsc::Sender<()>,
287 cfg: WriterConfig,
288 audit_log_stats: Arc<QuicAuditStats>,
289 write_state: WriteState,
290 conn_map_cmd_tx: mpsc::UnboundedSender<ConnectionMapCommand>,
291 cid_generator: Option<SharedConnectionIdGenerator>,
292 #[cfg(feature = "perf-quic-listener-metrics")]
293 init_rx_time: Option<SystemTime>,
294 metrics: M,
295 conn_stage: S,
296 bw_estimator: BandwidthReporter,
297}
298
299impl<Tx, M, S> IoWorker<Tx, M, S>
300where
301 Tx: DatagramSocketSend + Send,
302 M: Metrics,
303 S: ConnectionStage,
304{
305 pub(crate) fn new(params: IoWorkerParams<Tx, M>, conn_stage: S) -> Self {
306 let bw_estimator =
307 BandwidthReporter::new(params.metrics.utilized_bandwidth());
308
309 log::trace!("Creating IoWorker with stage: {conn_stage:?}");
310
311 Self {
312 socket: params.socket,
313 shutdown_tx: params.shutdown_tx,
314 cfg: params.cfg,
315 audit_log_stats: params.audit_log_stats,
316 write_state: params.write_state,
317 conn_map_cmd_tx: params.conn_map_cmd_tx,
318 cid_generator: params.cid_generator,
319 #[cfg(feature = "perf-quic-listener-metrics")]
320 init_rx_time: params.init_rx_time,
321 metrics: params.metrics,
322 conn_stage,
323 bw_estimator,
324 }
325 }
326
327 fn fill_available_scids(&self, qconn: &mut QuicheConnection) {
328 if qconn.scids_left() == 0 {
329 return;
330 }
331 let Some(cid_generator) = self.cid_generator.as_deref() else {
332 return;
333 };
334
335 let current_cid = qconn.source_id().into_owned();
336 for _ in 0..qconn.scids_left() {
337 let reset_token = random_u128();
339 let new_cid = cid_generator.new_connection_id();
340
341 if self
342 .conn_map_cmd_tx
343 .send(ConnectionMapCommand::MapCid {
344 existing_cid: current_cid.clone(),
345 new_cid: new_cid.clone(),
346 })
347 .is_err()
348 {
349 return;
351 }
352
353 if qconn.new_scid(&new_cid, reset_token, false).is_err() {
354 return;
356 }
357 }
358 }
359
360 fn unmap_cid(&self, cid: ConnectionId<'static>) {
361 let _ = self
363 .conn_map_cmd_tx
364 .send(ConnectionMapCommand::UnmapCid(cid));
365 }
366
367 fn refresh_connection_ids(&self, qconn: &mut QuicheConnection) {
368 self.fill_available_scids(qconn);
370
371 while let Some(retired_cid) = qconn.retired_scid_next() {
373 self.unmap_cid(retired_cid);
374 }
375 }
376
377 async fn work_loop<A: ApplicationOverQuic>(
378 &mut self, qconn: &mut QuicheConnection,
379 ctx: &mut ConnectionStageContext<A>,
380 ) -> QuicResult<()> {
381 const DEFAULT_SLEEP: Duration = Duration::from_secs(60);
382 let mut current_deadline: Option<Instant> = None;
383 let sleep = time::sleep(DEFAULT_SLEEP);
384 tokio::pin!(sleep);
385
386 let mut persistent_send_buf: Option<Box<[u8]>> =
392 (!self.cfg.pool_send_buffer).then(alloc_send_buffer);
393
394 loop {
395 let now = Instant::now();
396
397 self.write_state.has_pending_data = true;
398
399 let mut pooled_send_buf: Option<PooledSendBuf> = None;
405
406 while self.write_state.has_pending_data {
407 let mut packets_sent = 0;
408
409 let mut did_recv = false;
414 while let Some(pkt) = ctx
415 .in_pkt
416 .take()
417 .or_else(|| ctx.incoming_pkt_receiver.try_recv().ok())
418 {
419 self.process_incoming(qconn, pkt)?;
420 did_recv = true;
421 }
422
423 self.conn_stage.on_read(did_recv, qconn, ctx)?;
424 self.refresh_connection_ids(qconn);
425
426 let can_release = match self.write_state.next_release_time {
427 None => true,
428 Some(next_release) =>
429 next_release
430 .checked_duration_since(now)
431 .unwrap_or_default() <
432 RELEASE_TIMER_THRESHOLD,
433 };
434
435 self.write_state.has_pending_data &= can_release;
436
437 while self.write_state.has_pending_data &&
438 packets_sent < CHECK_INCOMING_QUEUE_RATIO
439 {
440 let send_buf: &mut [u8] =
447 if let Some(buf) = persistent_send_buf.as_deref_mut() {
448 buf
449 } else {
450 &mut pooled_send_buf
451 .get_or_insert_with(PooledSendBuf::acquire)[..]
452 };
453
454 self.gather_data_from_quiche_conn(qconn, send_buf, false)?;
455
456 if qconn.is_closed() {
458 return Ok(());
459 }
460
461 let mut flush_operation_token =
462 TrackMidHandshakeFlush::new(self.metrics.clone());
463
464 self.flush_buffer_to_socket(&send_buf[..]).await;
465
466 flush_operation_token.mark_complete();
467
468 packets_sent += self.write_state.num_pkts;
469
470 if let ControlFlow::Break(reason) =
471 self.conn_stage.on_flush(qconn, ctx)
472 {
473 return reason;
474 }
475 }
476 }
477
478 drop(pooled_send_buf);
483
484 self.bw_estimator.update(qconn, now);
485
486 self.audit_log_stats
487 .set_max_bandwidth(self.bw_estimator.max_bandwidth);
488 self.audit_log_stats.set_max_loss_pct(
489 (self.bw_estimator.max_loss_pct * 100_f32).round() as u8,
490 );
491
492 let new_deadline = min_of_some(
493 qconn.timeout_instant(),
494 self.write_state.next_release_time,
495 );
496 let new_deadline =
497 min_of_some(new_deadline, self.conn_stage.wait_deadline());
498
499 if new_deadline != current_deadline {
500 current_deadline = new_deadline;
501
502 sleep
503 .as_mut()
504 .reset(new_deadline.unwrap_or(now + DEFAULT_SLEEP).into());
505 }
506
507 let incoming_recv = &mut ctx.incoming_pkt_receiver;
508 let application = &mut ctx.application;
509
510 select! {
511 biased;
512 () = &mut sleep => {
513 qconn.on_timeout();
520
521 self.write_state.next_release_time = None;
522 current_deadline = None;
523 sleep.as_mut().reset((now + DEFAULT_SLEEP).into());
524 }
525 Some(pkt) = incoming_recv.recv() => ctx.in_pkt = Some(pkt),
526 directive = self.wait_for_data_or_handshake(qconn, application) => {
527 match directive? {
528 WaitForDataOrHandshakeDirective::Flush(send_buf) => {
529 self.flush_buffer_to_socket(send_buf.as_ref()).await;
534 }
535 WaitForDataOrHandshakeDirective::Noop => {}
536 }
537 },
538 };
539
540 if let ControlFlow::Break(reason) = self.conn_stage.post_wait(qconn) {
541 return reason;
542 }
543 }
544 }
545
546 #[cfg(feature = "perf-quic-listener-metrics")]
547 fn measure_complete_handshake_time(&mut self) {
548 if let Some(init_rx_time) = self.init_rx_time.take() {
549 if let Ok(delta) = init_rx_time.elapsed() {
550 self.metrics
551 .handshake_time_seconds(
552 labels::QuicHandshakeStage::HandshakeResponse,
553 )
554 .observe(delta.as_nanos() as u64);
555 }
556 }
557 }
558
559 fn gather_data_from_quiche_conn(
564 &mut self, qconn: &mut QuicheConnection, send_buf: &mut [u8],
565 single_packet: bool,
566 ) -> QuicResult<usize> {
567 let mut segment_size = None;
568 let mut send_info = None;
569
570 self.write_state.num_pkts = 0;
571 self.write_state.bytes_written = 0;
572
573 self.write_state.selected_path = None;
574
575 let now = Instant::now();
576
577 let send_buf = {
578 let trunc = UDP_MAX_GSO_PACKET_SIZE.min(send_buf.len());
579 &mut send_buf[..trunc]
580 };
581
582 #[cfg(feature = "gcongestion")]
583 let gcongestion_enabled = true;
584
585 #[cfg(not(feature = "gcongestion"))]
586 let gcongestion_enabled = qconn.gcongestion_enabled().unwrap_or(false);
587
588 let initial_release_decision = if gcongestion_enabled {
589 let initial_release_decision = qconn
590 .get_next_release_time()
591 .filter(|_| self.pacing_enabled(qconn));
592
593 if let Some(future_release_time) =
594 initial_release_decision.as_ref().and_then(|v| v.time(now))
595 {
596 let max_into_fut = qconn.max_release_into_future();
597
598 if future_release_time.duration_since(now) >= max_into_fut {
599 self.write_state.next_release_time =
600 Some(now + max_into_fut.mul_f32(0.8));
601 self.write_state.has_pending_data = false;
602 return Ok(0);
603 }
604 }
605
606 initial_release_decision
607 } else {
608 None
609 };
610
611 let buffer_write_outcome = loop {
612 let outcome = self.write_packet_to_buffer(
613 qconn,
614 send_buf,
615 &mut send_info,
616 segment_size,
617 );
618
619 let packet_size = match outcome {
620 Ok(0) => break Ok(0),
621
622 Ok(bytes_written) => bytes_written,
623
624 Err(e) => break Err(e),
625 };
626
627 if single_packet || !self.cfg.with_gso {
630 break outcome;
631 }
632
633 #[cfg(not(feature = "gcongestion"))]
634 let max_send_size = if !gcongestion_enabled {
635 tune_max_send_size(
637 segment_size,
638 qconn.send_quantum(),
639 send_buf.len(),
640 )
641 } else {
642 usize::MAX
643 };
644
645 #[cfg(feature = "gcongestion")]
646 let max_send_size = usize::MAX;
647
648 let buffer_is_full = self.write_state.num_pkts ==
652 UDP_MAX_SEGMENT_COUNT ||
653 self.write_state.bytes_written >= max_send_size;
654
655 if buffer_is_full {
656 break outcome;
657 }
658
659 match segment_size {
664 Some(size)
665 if packet_size != size || packet_size < GSO_THRESHOLD =>
666 break outcome,
667 None => segment_size = Some(packet_size),
668 _ => (),
669 }
670
671 if gcongestion_enabled {
672 if let Some(initial_release_decision) = initial_release_decision {
675 match qconn.get_next_release_time() {
676 Some(release)
677 if release.can_burst() ||
678 release.time_eq(
679 &initial_release_decision,
680 now,
681 ) => {},
682 _ => break outcome,
683 }
684 }
685 }
686 };
687
688 let tx_time = if gcongestion_enabled {
689 initial_release_decision
690 .filter(|_| self.pacing_enabled(qconn))
691 .and_then(|v| v.time(now))
693 } else {
694 send_info
695 .filter(|_| self.pacing_enabled(qconn))
696 .map(|v| v.at)
697 };
698
699 self.write_state.conn_established = qconn.is_established();
700 self.write_state.tx_time = tx_time;
701 self.write_state.segment_size =
702 segment_size.unwrap_or(self.write_state.bytes_written);
703
704 if !gcongestion_enabled {
705 if let Some(time) = tx_time {
706 const DEFAULT_MAX_INTO_FUTURE: Duration =
707 Duration::from_millis(1);
708 if time
709 .checked_duration_since(now)
710 .map(|d| d > DEFAULT_MAX_INTO_FUTURE)
711 .unwrap_or(false)
712 {
713 self.write_state.next_release_time =
714 Some(now + DEFAULT_MAX_INTO_FUTURE.mul_f32(0.8));
715 self.write_state.has_pending_data = false;
716 return Ok(0);
717 }
718 }
719 }
720
721 buffer_write_outcome
722 }
723
724 fn select_path(
734 &mut self, qconn: &QuicheConnection,
735 ) -> Option<(SocketAddr, SocketAddr)> {
736 if self.write_state.selected_path.is_some() {
737 return self.write_state.selected_path;
738 }
739
740 let from = self.cfg.local_addr;
741
742 if self.write_state.pending_paths.len() == 0 {
744 self.write_state.pending_paths = qconn.paths_iter(from);
745 }
746
747 let to = self.write_state.pending_paths.next()?;
748
749 Some((from, to))
750 }
751
752 #[cfg(not(feature = "gcongestion"))]
753 fn pacing_enabled(&self, qconn: &QuicheConnection) -> bool {
754 self.cfg.pacing_offload && qconn.pacing_enabled()
755 }
756
757 #[cfg(feature = "gcongestion")]
758 fn pacing_enabled(&self, _qconn: &QuicheConnection) -> bool {
759 self.cfg.pacing_offload
760 }
761
762 fn write_packet_to_buffer(
763 &mut self, qconn: &mut QuicheConnection, send_buf: &mut [u8],
764 send_info: &mut Option<SendInfo>, segment_size: Option<usize>,
765 ) -> QuicResult<usize> {
766 let mut send_buf = &mut send_buf[self.write_state.bytes_written..];
767 if send_buf.len() > segment_size.unwrap_or(usize::MAX) {
768 send_buf = &mut send_buf[..segment_size.unwrap_or(usize::MAX)];
771 }
772
773 let (from, to) = self.select_path(qconn).unzip();
784
785 match qconn.send_on_path(send_buf, from, to) {
786 Ok((packet_size, info)) => {
787 let _ = send_info.get_or_insert(info);
788
789 self.write_state.bytes_written += packet_size;
790 self.write_state.num_pkts += 1;
791
792 let from = send_info.as_ref().map(|info| info.from);
793 let to = send_info.as_ref().map(|info| info.to);
794
795 self.write_state.selected_path = from.zip(to);
796
797 self.write_state.has_pending_data = true;
798
799 Ok(packet_size)
800 },
801
802 Err(QuicheError::Done) => {
803 let has_pending_paths = self.write_state.pending_paths.len() > 0;
809
810 self.write_state.has_pending_data = has_pending_paths;
812
813 Ok(0)
814 },
815
816 Err(e) => {
817 let error_code = if let Some(local_error) = qconn.local_error() {
818 local_error.error_code
819 } else {
820 let internal_error_code =
821 quiche::WireErrorCode::InternalError as u64;
822 let _ = qconn.close(false, internal_error_code, &[]);
823
824 internal_error_code
825 };
826
827 self.audit_log_stats
828 .set_sent_conn_close_transport_error_code(error_code as i64);
829
830 Err(Box::new(e))
831 },
832 }
833 }
834
835 async fn flush_buffer_to_socket(&mut self, send_buf: &[u8]) {
836 if self.write_state.bytes_written > 0 {
837 let current_send_buf = &send_buf[..self.write_state.bytes_written];
838
839 let (from, to) = self.write_state.selected_path.unzip();
840
841 let to = to.unwrap_or(self.cfg.peer_addr);
842 let from = from.filter(|_| self.cfg.with_pktinfo);
843
844 let send_res = if let (Some(udp_socket), true) =
845 (self.socket.as_udp_socket(), self.cfg.with_gso)
846 {
847 send_to(
849 udp_socket,
850 to,
851 from,
852 current_send_buf,
853 self.write_state.segment_size,
854 self.write_state.tx_time,
855 self.metrics
856 .write_errors(labels::QuicWriteError::WouldBlock),
857 self.metrics.send_to_wouldblock_duration_s(),
858 )
859 .await
860 } else {
861 self.socket.send_to(current_send_buf, to).await
862 };
863
864 #[cfg(feature = "perf-quic-listener-metrics")]
865 self.measure_complete_handshake_time();
866
867 match send_res {
868 Ok(n) =>
869 if n < self.write_state.bytes_written {
870 self.metrics
871 .write_errors(labels::QuicWriteError::Partial)
872 .inc();
873 },
874
875 Err(_) => {
876 self.metrics.write_errors(labels::QuicWriteError::Err).inc();
877 },
878 }
879 }
880 }
881
882 fn process_incoming(
884 &mut self, qconn: &mut QuicheConnection, mut pkt: Incoming,
885 ) -> QuicResult<()> {
886 let recv_info = quiche::RecvInfo {
887 from: pkt.peer_addr,
888 to: pkt.local_addr,
889 };
890
891 if let Some(gro) = pkt.gro {
892 for dgram in pkt.buf.chunks_mut(gro as usize) {
893 qconn.recv(dgram, recv_info)?;
894 }
895 } else {
896 qconn.recv(&mut pkt.buf, recv_info)?;
897 }
898
899 Ok(())
900 }
901
902 async fn wait_for_data_or_handshake<A: ApplicationOverQuic>(
921 &mut self, qconn: &mut QuicheConnection, quic_application: &mut A,
922 ) -> QuicResult<WaitForDataOrHandshakeDirective> {
923 if quic_application.should_act() {
924 quic_application.wait_for_data(qconn).await?;
934 Ok(WaitForDataOrHandshakeDirective::Noop)
935 } else {
936 let send_buf = self.wait_for_quiche(qconn).await?;
941 Ok(WaitForDataOrHandshakeDirective::Flush(send_buf))
942 }
943 }
944
945 async fn wait_for_quiche(
966 &mut self, qconn: &mut QuicheConnection,
967 ) -> QuicResult<TransientSendBuf> {
968 let send_buf = std::future::poll_fn(|_| {
969 let mut send_buf =
973 TransientSendBuf::acquire(self.cfg.pool_send_buffer);
974
975 match self.gather_data_from_quiche_conn(
976 qconn,
977 send_buf.as_mut(),
978 true,
979 ) {
980 Ok(bytes_written) => {
981 if bytes_written == 0 && self.write_state.bytes_written == 0 {
988 Poll::Pending
989 } else {
990 Poll::Ready(Ok(send_buf))
991 }
992 },
993 _ => Poll::Ready(Err(quiche::Error::TlsFail)),
994 }
995 })
996 .await?;
997 Ok(send_buf)
998 }
999}
1000
1001#[must_use]
1007enum WaitForDataOrHandshakeDirective {
1008 Noop,
1009 Flush(TransientSendBuf),
1010}
1011
1012pub struct Running<Tx, M, A> {
1013 pub(crate) params: IoWorkerParams<Tx, M>,
1014 pub(crate) context: ConnectionStageContext<A>,
1015 pub(crate) qconn: Box<QuicheConnection>,
1017}
1018
1019impl<Tx, M, A> Running<Tx, M, A> {
1020 pub fn ssl(&mut self) -> &mut SslRef {
1021 (*self.qconn).as_mut()
1023 }
1024}
1025
1026pub(crate) struct Closing<Tx, M, A> {
1027 pub(crate) params: IoWorkerParams<Tx, M>,
1028 pub(crate) context: ConnectionStageContext<A>,
1029 pub(crate) work_loop_result: QuicResult<()>,
1030 pub(crate) qconn: Box<QuicheConnection>,
1032}
1033
1034pub enum RunningOrClosing<Tx, M, A> {
1035 Running(Running<Tx, M, A>),
1036 Closing(Closing<Tx, M, A>),
1037}
1038
1039impl<Tx, M> IoWorker<Tx, M, Handshake>
1040where
1041 Tx: DatagramSocketSend + Send,
1042 M: Metrics,
1043{
1044 pub(crate) async fn run<A>(
1045 mut self, mut qconn: Box<QuicheConnection>,
1046 mut ctx: ConnectionStageContext<A>,
1047 ) -> RunningOrClosing<Tx, M, A>
1048 where
1049 A: ApplicationOverQuic,
1050 {
1051 std::future::poll_fn(|cx| {
1057 let ssl = (*qconn).as_mut();
1059 ssl.set_task_waker(Some(cx.waker().clone()));
1060
1061 Poll::Ready(())
1062 })
1063 .await;
1064
1065 #[cfg(target_os = "linux")]
1066 if let Some(incoming) = ctx.in_pkt.as_mut() {
1067 self.audit_log_stats
1068 .set_initial_so_mark_data(incoming.so_mark_data.take());
1069 }
1070
1071 let mut work_loop_result = self.work_loop(&mut qconn, &mut ctx).await;
1072 if work_loop_result.is_ok() && qconn.is_closed() {
1073 work_loop_result = Err(HandshakeError::ConnectionClosed.into());
1074 }
1075
1076 if let Err(err) = &work_loop_result {
1077 self.metrics.failed_handshakes(err.into()).inc();
1078
1079 return RunningOrClosing::Closing(Closing {
1080 params: self.into(),
1081 context: ctx,
1082 work_loop_result,
1083 qconn,
1084 });
1085 };
1086
1087 match self.on_conn_established(&mut qconn, &mut ctx.application) {
1088 Ok(()) => RunningOrClosing::Running(Running {
1089 params: self.into(),
1090 context: ctx,
1091 qconn,
1092 }),
1093 Err(e) => {
1094 foundations::telemetry::log::warn!(
1095 "Handshake stage on_connection_established failed"; "error"=>%e
1096 );
1097
1098 RunningOrClosing::Closing(Closing {
1099 params: self.into(),
1100 context: ctx,
1101 work_loop_result,
1102 qconn,
1103 })
1104 },
1105 }
1106 }
1107
1108 fn on_conn_established<App: ApplicationOverQuic>(
1109 &mut self, qconn: &mut QuicheConnection, driver: &mut App,
1110 ) -> QuicResult<()> {
1111 if self.audit_log_stats.transport_handshake_duration_us() == -1 {
1115 self.conn_stage.handshake_info.set_elapsed();
1116 let handshake_info = &self.conn_stage.handshake_info;
1117
1118 self.audit_log_stats
1119 .set_transport_handshake_duration(handshake_info.elapsed());
1120
1121 driver.on_conn_established(qconn, handshake_info)?;
1122 }
1123
1124 if let Some(cid) = self.cfg.pending_cid.take() {
1125 self.unmap_cid(cid);
1126 }
1127
1128 Ok(())
1129 }
1130}
1131
1132impl<Tx, M, S> From<IoWorker<Tx, M, S>> for IoWorkerParams<Tx, M> {
1133 fn from(value: IoWorker<Tx, M, S>) -> Self {
1134 Self {
1135 socket: value.socket,
1136 shutdown_tx: value.shutdown_tx,
1137 cfg: value.cfg,
1138 audit_log_stats: value.audit_log_stats,
1139 write_state: value.write_state,
1140 conn_map_cmd_tx: value.conn_map_cmd_tx,
1141 cid_generator: value.cid_generator,
1142 #[cfg(feature = "perf-quic-listener-metrics")]
1143 init_rx_time: value.init_rx_time,
1144 metrics: value.metrics,
1145 }
1146 }
1147}
1148
1149impl<Tx, M> IoWorker<Tx, M, RunningApplication>
1150where
1151 Tx: DatagramSocketSend + Send,
1152 M: Metrics,
1153{
1154 pub(crate) async fn run<A: ApplicationOverQuic>(
1155 mut self, mut qconn: Box<QuicheConnection>,
1156 mut ctx: ConnectionStageContext<A>,
1157 ) -> Closing<Tx, M, A> {
1158 if let Err(e) = self.conn_stage.on_read(true, &mut qconn, &mut ctx) {
1163 return Closing {
1164 params: self.into(),
1165 context: ctx,
1166 work_loop_result: Err(e),
1167 qconn,
1168 };
1169 };
1170
1171 let work_loop_result = self.work_loop(&mut qconn, &mut ctx).await;
1172
1173 Closing {
1174 params: self.into(),
1175 context: ctx,
1176 work_loop_result,
1177 qconn,
1178 }
1179 }
1180}
1181
1182impl<Tx, M> IoWorker<Tx, M, Close>
1183where
1184 Tx: DatagramSocketSend + Send,
1185 M: Metrics,
1186{
1187 pub(crate) async fn close<A: ApplicationOverQuic>(
1188 mut self, qconn: &mut QuicheConnection,
1189 ctx: &mut ConnectionStageContext<A>,
1190 ) {
1191 if self.conn_stage.work_loop_result.is_ok() &&
1192 self.bw_estimator.max_bandwidth > 0
1193 {
1194 let metrics = &self.metrics;
1195
1196 metrics
1197 .max_bandwidth_mbps()
1198 .observe(self.bw_estimator.max_bandwidth as f64 * 1e-6);
1199
1200 metrics
1201 .max_loss_pct()
1202 .observe(self.bw_estimator.max_loss_pct as f64 * 100.);
1203 }
1204
1205 if ctx.application.should_act() {
1206 ctx.application.on_conn_close(
1207 qconn,
1208 &self.metrics,
1209 &self.conn_stage.work_loop_result,
1210 );
1211 }
1212
1213 let mut send_buf = TransientSendBuf::acquire(self.cfg.pool_send_buffer);
1221 let _ =
1222 self.gather_data_from_quiche_conn(qconn, send_buf.as_mut(), false);
1223 self.flush_buffer_to_socket(send_buf.as_ref()).await;
1224
1225 *ctx.stats.lock().unwrap() = QuicConnectionStats::from_conn(qconn);
1226
1227 if let Some(err) = qconn.peer_error() {
1228 if err.is_app {
1229 self.audit_log_stats
1230 .set_recvd_conn_close_application_error_code(
1231 err.error_code as _,
1232 );
1233 } else {
1234 self.audit_log_stats
1235 .set_recvd_conn_close_transport_error_code(
1236 err.error_code as _,
1237 );
1238 }
1239 }
1240
1241 if let Some(err) = qconn.local_error() {
1242 if err.is_app {
1243 self.audit_log_stats
1244 .set_sent_conn_close_application_error_code(
1245 err.error_code as _,
1246 );
1247 } else {
1248 self.audit_log_stats
1249 .set_sent_conn_close_transport_error_code(
1250 err.error_code as _,
1251 );
1252 }
1253 }
1254
1255 self.close_connection(qconn);
1256
1257 if let Err(work_loop_error) = self.conn_stage.work_loop_result {
1258 self.audit_log_stats
1259 .set_connection_close_reason(work_loop_error);
1260 }
1261 }
1262
1263 fn close_connection(&mut self, qconn: &mut QuicheConnection) {
1264 if let Some(cid) = self.cfg.pending_cid.take() {
1265 self.unmap_cid(cid);
1266 }
1267 while let Some(retired_cid) = qconn.retired_scid_next() {
1268 self.unmap_cid(retired_cid);
1269 }
1270 for cid in qconn.source_ids().cloned() {
1271 self.unmap_cid(cid.into_owned());
1272 }
1273
1274 self.metrics.connections_in_memory().dec();
1275 }
1276}
1277
1278fn min_of_some<T: Ord>(v1: Option<T>, v2: Option<T>) -> Option<T> {
1280 match (v1, v2) {
1281 (Some(a), Some(b)) => Some(a.min(b)),
1282 (Some(v), _) | (_, Some(v)) => Some(v),
1283 (None, None) => None,
1284 }
1285}
1286
1287struct TrackMidHandshakeFlush<M: Metrics> {
1290 complete: bool,
1291 metrics: M,
1292}
1293
1294impl<M: Metrics> TrackMidHandshakeFlush<M> {
1295 fn new(metrics: M) -> Self {
1296 Self {
1297 complete: false,
1298 metrics,
1299 }
1300 }
1301
1302 fn mark_complete(&mut self) {
1303 self.complete = true;
1304 }
1305}
1306
1307impl<M: Metrics> Drop for TrackMidHandshakeFlush<M> {
1308 fn drop(&mut self) {
1309 if !self.complete {
1310 self.metrics.skipped_mid_handshake_flush_count().inc();
1311 }
1312 }
1313}
1314
1315fn random_u128() -> u128 {
1316 let mut buf = [0; 16];
1317 boring::rand::rand_bytes(&mut buf).expect("boring's RAND_bytes never fails");
1318 u128::from_ne_bytes(buf)
1319}
1320
1321#[cfg(test)]
1322mod pooled_send_buf_tests {
1323 use super::*;
1324
1325 #[test]
1330 fn caps_retained_buffers() {
1331 std::thread::spawn(|| {
1332 let bufs: Vec<PooledSendBuf> = (0..SEND_BUF_POOL_CAP + 4)
1336 .map(|_| PooledSendBuf::acquire())
1337 .collect();
1338 drop(bufs);
1339
1340 let retained = SEND_BUF_POOL.with(|pool| pool.borrow().len());
1341 assert_eq!(retained, SEND_BUF_POOL_CAP);
1342 })
1343 .join()
1344 .unwrap();
1345 }
1346
1347 #[test]
1348 fn reuses_a_returned_buffer() {
1349 std::thread::spawn(|| {
1350 let first_ptr = {
1351 let buf = PooledSendBuf::acquire();
1352 assert_eq!(buf.len(), SEND_BUFFER_SIZE);
1353 buf.as_ptr()
1354 }; let reused = PooledSendBuf::acquire();
1357 assert_eq!(
1358 reused.as_ptr(),
1359 first_ptr,
1360 "acquire should hand back the pooled allocation"
1361 );
1362 })
1363 .join()
1364 .unwrap();
1365 }
1366
1367 #[test]
1368 fn returns_to_the_dropping_thread() {
1369 let buf = std::thread::spawn(PooledSendBuf::acquire).join().unwrap();
1373
1374 std::thread::spawn(move || {
1375 assert_eq!(SEND_BUF_POOL.with(|pool| pool.borrow().len()), 0);
1376 drop(buf);
1377 assert_eq!(SEND_BUF_POOL.with(|pool| pool.borrow().len()), 1);
1378 })
1379 .join()
1380 .unwrap();
1381 }
1382}