1pub(crate) mod acceptor;
28pub(crate) mod connector;
29
30use super::connection::ConnectionMap;
31use super::connection::HandshakeInfo;
32use super::connection::Incoming;
33use super::connection::InitialQuicConnection;
34use super::connection::QuicConnectionParams;
35use super::io::worker::WriterConfig;
36use super::QuicheConnection;
37use crate::metrics::labels;
38use crate::metrics::quic_expensive_metrics_ip_reduce;
39use crate::metrics::Metrics;
40use crate::quic::connection::SharedConnectionIdGenerator;
41use crate::settings::Config;
42use datagram_socket::DatagramSocketRecv;
43use datagram_socket::DatagramSocketSend;
44use foundations::telemetry::log;
45use quiche::ConnectionId;
46use quiche::Header;
47use quiche::MAX_CONN_ID_LEN;
48use std::default::Default;
49use std::future::Future;
50use std::io;
51use std::net::SocketAddr;
52use std::pin::Pin;
53use std::sync::Arc;
54use std::task::ready;
55use std::task::Context;
56use std::task::Poll;
57use std::time::Instant;
58use std::time::SystemTime;
59use task_killswitch::spawn_with_killswitch;
60use tokio::sync::mpsc;
61
62#[cfg(target_os = "linux")]
63use foundations::telemetry::metrics::Counter;
64#[cfg(target_os = "linux")]
65use foundations::telemetry::metrics::TimeHistogram;
66#[cfg(target_os = "linux")]
67use libc::sockaddr_in;
68#[cfg(target_os = "linux")]
69use libc::sockaddr_in6;
70
71type ConnStream<Tx, M> = mpsc::Receiver<io::Result<InitialQuicConnection<Tx, M>>>;
72
73const PACKET_RX_YIELD_AFTER: usize = 30;
77const CONN_MAP_CMD_BATCH_SIZE: usize = 128;
79
80#[cfg(feature = "perf-quic-listener-metrics")]
81mod listener_stage_timer {
82 use foundations::telemetry::metrics::TimeHistogram;
83 use std::time::Instant;
84
85 pub(super) struct ListenerStageTimer {
86 start: Instant,
87 time_hist: TimeHistogram,
88 }
89
90 impl ListenerStageTimer {
91 pub(super) fn new(
92 start: Instant, time_hist: TimeHistogram,
93 ) -> ListenerStageTimer {
94 ListenerStageTimer { start, time_hist }
95 }
96 }
97
98 impl Drop for ListenerStageTimer {
99 fn drop(&mut self) {
100 self.time_hist
101 .observe((Instant::now() - self.start).as_nanos() as u64);
102 }
103 }
104}
105
106#[derive(Debug)]
107struct PollRecvData {
108 buf: Vec<u8>,
109 src_addr: SocketAddr,
111 dst_addr_override: Option<SocketAddr>,
114 rx_time: Option<SystemTime>,
115 gro: Option<i32>,
116 #[cfg(target_os = "linux")]
117 so_mark_data: Option<[u8; 4]>,
118}
119
120pub enum ConnectionMapCommand {
123 MapCid {
124 existing_cid: ConnectionId<'static>,
125 new_cid: ConnectionId<'static>,
126 },
127 UnmapCid(ConnectionId<'static>),
128}
129
130pub struct InboundPacketRouter<Tx, Rx, M, I>
146where
147 Tx: DatagramSocketSend + Send + 'static,
148 M: Metrics,
149{
150 socket_tx: Arc<Tx>,
151 socket_rx: Rx,
152 local_addr: SocketAddr,
153 config: Config,
154 conns: ConnectionMap,
155 incoming_packet_handler: I,
156 shutdown_tx: Option<mpsc::Sender<()>>,
157 shutdown_rx: mpsc::Receiver<()>,
158 conn_map_cmd_tx: mpsc::UnboundedSender<ConnectionMapCommand>,
159 conn_map_cmd_rx: mpsc::UnboundedReceiver<ConnectionMapCommand>,
160 conn_map_cmd_buf: Vec<ConnectionMapCommand>,
164 accept_sink: mpsc::Sender<io::Result<InitialQuicConnection<Tx, M>>>,
165 metrics: M,
166 #[cfg(target_os = "linux")]
167 udp_drop_count: u32,
168
169 #[cfg(target_os = "linux")]
170 reusable_cmsg_space: Vec<u8>,
171
172 #[cfg(target_os = "linux")]
173 buf: Vec<u8>,
174
175 #[cfg(target_os = "linux")]
177 metrics_handshake_time_seconds: TimeHistogram,
178 #[cfg(target_os = "linux")]
179 metrics_udp_drop_count: Counter,
180}
181
182impl<Tx, Rx, M, I> InboundPacketRouter<Tx, Rx, M, I>
183where
184 Tx: DatagramSocketSend + Send + 'static,
185 Rx: DatagramSocketRecv,
186 M: Metrics,
187 I: InitialPacketHandler,
188{
189 pub(crate) fn new(
190 config: Config, socket_tx: Arc<Tx>, socket_rx: Rx,
191 local_addr: SocketAddr, incoming_packet_handler: I, metrics: M,
192 ) -> (Self, ConnStream<Tx, M>) {
193 let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
194 let (accept_sink, accept_stream) = mpsc::channel(config.listen_backlog);
195 let (conn_map_cmd_tx, conn_map_cmd_rx) = mpsc::unbounded_channel();
196
197 (
198 InboundPacketRouter {
199 local_addr,
200 socket_tx,
201 socket_rx,
202 conns: ConnectionMap::default(),
203 incoming_packet_handler,
204 shutdown_tx: Some(shutdown_tx),
205 shutdown_rx,
206 conn_map_cmd_tx,
207 conn_map_cmd_rx,
208 conn_map_cmd_buf: Vec::with_capacity(4),
209 accept_sink,
210 #[cfg(target_os = "linux")]
211 udp_drop_count: 0,
212 #[cfg(target_os = "linux")]
213 reusable_cmsg_space: nix::cmsg_space!(
218 u32, nix::sys::time::TimeSpec, u16, sockaddr_in, sockaddr_in6, u32 ),
225
226 config,
227
228 #[cfg(target_os = "linux")]
229 buf: Vec::new(),
230 #[cfg(target_os = "linux")]
231 metrics_handshake_time_seconds: metrics.handshake_time_seconds(labels::QuicHandshakeStage::QueueWaiting),
232 #[cfg(target_os = "linux")]
233 metrics_udp_drop_count: metrics.udp_drop_count(),
234
235 metrics,
236
237 },
238 accept_stream,
239 )
240 }
241
242 fn on_incoming(&mut self, mut incoming: Incoming) -> io::Result<()> {
243 #[cfg(feature = "perf-quic-listener-metrics")]
244 let start = std::time::Instant::now();
245
246 if let Some(dcid) = short_dcid(&incoming.buf) {
247 if let Some(ev_sender) = self.conns.get(&dcid) {
248 let _ = ev_sender.try_send(incoming);
249 return Ok(());
250 }
251 }
252
253 let hdr = Header::from_slice(&mut incoming.buf, MAX_CONN_ID_LEN)
254 .map_err(|e| match e {
255 quiche::Error::BufferTooShort | quiche::Error::InvalidPacket =>
256 labels::QuicInvalidInitialPacketError::FailedToParse.into(),
257 e => io::Error::other(e),
258 })?;
259
260 if let Some(ev_sender) = self.conns.get(&hdr.dcid) {
261 let _ = ev_sender.try_send(incoming);
262 return Ok(());
263 }
264
265 #[cfg(feature = "perf-quic-listener-metrics")]
266 let _timer = listener_stage_timer::ListenerStageTimer::new(
267 start,
268 self.metrics.handshake_time_seconds(
269 labels::QuicHandshakeStage::HandshakeProtocol,
270 ),
271 );
272
273 if self.shutdown_tx.is_none() {
274 return Ok(());
275 }
276
277 let local_addr = incoming.local_addr;
278 let peer_addr = incoming.peer_addr;
279
280 #[cfg(feature = "perf-quic-listener-metrics")]
281 let init_rx_time = incoming.rx_time;
282
283 let new_connection = self.incoming_packet_handler.handle_initials(
284 incoming,
285 hdr,
286 self.config.as_mut(),
287 )?;
288
289 match new_connection {
290 Some(new_connection) => self.spawn_new_connection(
291 new_connection,
292 local_addr,
293 peer_addr,
294 #[cfg(feature = "perf-quic-listener-metrics")]
295 init_rx_time,
296 ),
297 None => Ok(()),
298 }
299 }
300
301 fn spawn_new_connection(
304 &mut self, new_connection: NewConnection, local_addr: SocketAddr,
305 peer_addr: SocketAddr,
306 #[cfg(feature = "perf-quic-listener-metrics")] init_rx_time: Option<
307 SystemTime,
308 >,
309 ) -> io::Result<()> {
310 let NewConnection {
311 conn,
312 pending_cid,
313 cid_generator,
314 handshake_start_time,
315 initial_pkt,
316 } = new_connection;
317
318 let Some(ref shutdown_tx) = self.shutdown_tx else {
319 return Ok(());
321 };
322 let Ok(send_permit) = self.accept_sink.try_reserve() else {
323 return Err(
325 labels::QuicInvalidInitialPacketError::AcceptQueueOverflow.into(),
326 );
327 };
328
329 let scid = conn.source_id().into_owned();
330 let writer_cfg = WriterConfig {
331 peer_addr,
332 local_addr,
333 pending_cid: pending_cid.clone(),
334 with_gso: self.config.has_gso,
335 pacing_offload: self.config.pacing_offload,
336 with_pktinfo: if self.local_addr.is_ipv4() {
337 self.config.has_ippktinfo
338 } else {
339 self.config.has_ipv6pktinfo
340 },
341 pool_send_buffer: self.config.pool_send_buffer,
342 };
343
344 let handshake_info = HandshakeInfo::new(
345 handshake_start_time,
346 self.config.handshake_timeout,
347 );
348
349 let conn = InitialQuicConnection::new(QuicConnectionParams {
350 writer_cfg,
351 initial_pkt,
352 shutdown_tx: shutdown_tx.clone(),
353 conn_map_cmd_tx: self.conn_map_cmd_tx.clone(),
354 scid: scid.clone(),
355 cid_generator,
356 metrics: self.metrics.clone(),
357 #[cfg(feature = "perf-quic-listener-metrics")]
358 init_rx_time,
359 handshake_info,
360 quiche_conn: conn,
361 socket: Arc::clone(&self.socket_tx),
362 local_addr,
363 peer_addr,
364 });
365
366 conn.audit_log_stats
367 .set_transport_handshake_start(instant_to_system(
368 handshake_start_time,
369 ));
370
371 self.conns.insert(&scid, &conn);
372
373 if let Some(pending_cid) = pending_cid {
377 self.conns.map_cid(&scid, &pending_cid);
378 }
379
380 self.metrics.accepted_initial_packet_count().inc();
381 if self.config.enable_expensive_packet_count_metrics {
382 if let Some(peer_ip) =
383 quic_expensive_metrics_ip_reduce(conn.peer_addr().ip())
384 {
385 self.metrics
386 .expensive_accepted_initial_packet_count(peer_ip)
387 .inc();
388 }
389 }
390
391 send_permit.send(Ok(conn));
392 Ok(())
393 }
394}
395
396impl<Tx, Rx, M, I> InboundPacketRouter<Tx, Rx, M, I>
397where
398 Tx: DatagramSocketSend + Send + Sync + 'static,
399 Rx: DatagramSocketRecv,
400 M: Metrics,
401 I: InitialPacketHandler,
402{
403 fn poll_recv_from(
406 &mut self, cx: &mut Context<'_>,
407 ) -> Poll<io::Result<PollRecvData>> {
408 let mut buf = Vec::with_capacity(datagram_socket::MAX_DATAGRAM_SIZE);
409 let mut read_buf = tokio::io::ReadBuf::uninit(buf.spare_capacity_mut());
412 let addr = ready!(self.socket_rx.poll_recv_from(cx, &mut read_buf))?;
413 let n = read_buf.filled().len();
414 unsafe {
415 buf.set_len(n);
419 }
420 Poll::Ready(Ok(PollRecvData {
421 buf,
422 src_addr: addr,
423 rx_time: None,
424 gro: None,
425 dst_addr_override: None,
426 #[cfg(target_os = "linux")]
427 so_mark_data: None,
428 }))
429 }
430
431 fn poll_recv_and_rx_time(
432 &mut self, cx: &mut Context<'_>,
433 ) -> Poll<io::Result<PollRecvData>> {
434 #[cfg(not(target_os = "linux"))]
435 {
436 self.poll_recv_from(cx)
437 }
438
439 #[cfg(target_os = "linux")]
440 {
441 use libc::SOL_SOCKET;
442 use libc::SO_MARK;
443 use nix::errno::Errno;
444 use nix::sys::socket::*;
445 use std::net::SocketAddrV4;
446 use std::net::SocketAddrV6;
447 use std::os::fd::AsRawFd;
448 use tokio::io::Interest;
449
450 use crate::buf_factory::BufFactory;
451
452 let Some(udp_socket) = self.socket_rx.as_udp_socket() else {
453 return self.poll_recv_from(cx);
456 };
457
458 self.buf.resize(BufFactory::MAX_BUF_SIZE, 0u8);
461 loop {
462 let iov_s = &mut [io::IoSliceMut::new(&mut self.buf)];
463 match udp_socket.try_io(Interest::READABLE, || {
464 recvmsg::<SockaddrStorage>(
465 udp_socket.as_raw_fd(),
466 iov_s,
467 Some(&mut self.reusable_cmsg_space),
468 MsgFlags::empty(),
469 )
470 .map_err(|x| x.into())
471 }) {
472 Ok(r) => {
473 let filled_buf =
474 r.iovs().next().map(Vec::from).unwrap_or_default();
475 debug_assert_eq!(r.bytes, filled_buf.len());
479
480 let address = match r.address {
481 Some(inner) => inner,
482 _ => return Poll::Ready(Err(Errno::EINVAL.into())),
483 };
484
485 let peer_addr = match address.family() {
486 Some(AddressFamily::Inet) => SocketAddrV4::from(
487 *address.as_sockaddr_in().unwrap(),
488 )
489 .into(),
490 Some(AddressFamily::Inet6) => SocketAddrV6::from(
491 *address.as_sockaddr_in6().unwrap(),
492 )
493 .into(),
494 _ => {
495 return Poll::Ready(Err(Errno::EINVAL.into()));
496 },
497 };
498
499 let mut rx_time = None;
500 let mut gro = None;
501 let mut dst_addr_override = None;
502 let mut mark_bytes: Option<[u8; 4]> = None;
503
504 let Ok(cmsgs) = r.cmsgs() else {
505 return Poll::Ready(Ok(PollRecvData {
507 buf: filled_buf,
508 src_addr: peer_addr,
509 dst_addr_override,
510 rx_time,
511 gro,
512 so_mark_data: mark_bytes,
513 }));
514 };
515
516 for cmsg in cmsgs {
517 match cmsg {
518 ControlMessageOwned::RxqOvfl(c) => {
519 if c != self.udp_drop_count {
520 self.metrics_udp_drop_count.inc_by(
521 (c - self.udp_drop_count) as u64,
522 );
523 self.udp_drop_count = c;
524 }
525 },
526 ControlMessageOwned::ScmTimestampns(val) => {
527 rx_time = SystemTime::UNIX_EPOCH
528 .checked_add(val.into());
529 if let Some(delta) =
530 rx_time.and_then(|rx_time| {
531 rx_time.elapsed().ok()
532 })
533 {
534 self.metrics_handshake_time_seconds
535 .observe(delta.as_nanos() as u64);
536 }
537 },
538 ControlMessageOwned::UdpGroSegments(val) =>
539 gro = Some(val),
540 ControlMessageOwned::Ipv4OrigDstAddr(val) => {
541 let source_addr = std::net::Ipv4Addr::from(
542 u32::to_be(val.sin_addr.s_addr),
543 );
544 let source_port = u16::to_be(val.sin_port);
545
546 let parsed_addr =
547 SocketAddr::V4(SocketAddrV4::new(
548 source_addr,
549 source_port,
550 ));
551
552 dst_addr_override = resolve_dst_addr(
553 &self.local_addr,
554 &parsed_addr,
555 );
556 },
557 ControlMessageOwned::Ipv6OrigDstAddr(val) => {
558 let source_addr = std::net::Ipv6Addr::from(
563 val.sin6_addr.s6_addr,
564 );
565 let source_port = u16::to_be(val.sin6_port);
566 let source_flowinfo =
567 u32::to_be(val.sin6_flowinfo);
568 let source_scope =
569 u32::to_be(val.sin6_scope_id);
570
571 let parsed_addr =
572 SocketAddr::V6(SocketAddrV6::new(
573 source_addr,
574 source_port,
575 source_flowinfo,
576 source_scope,
577 ));
578
579 dst_addr_override = resolve_dst_addr(
580 &self.local_addr,
581 &parsed_addr,
582 );
583 },
584 ControlMessageOwned::Ipv4PacketInfo(_) |
585 ControlMessageOwned::Ipv6PacketInfo(_) => {
586 },
591 ControlMessageOwned::Unknown(raw_cmsg) => {
592 let UnknownCmsg {
593 cmsg_header,
594 data_bytes,
595 } = raw_cmsg;
596
597 if cmsg_header.cmsg_level == SOL_SOCKET &&
598 cmsg_header.cmsg_type == SO_MARK
599 {
600 let Ok(arr) =
601 <[u8; 4]>::try_from(data_bytes)
602 else {
603 continue;
606 };
607
608 let _ = mark_bytes.insert(arr);
609 }
610 },
611 _ => {
612 },
615 };
616 }
617
618 return Poll::Ready(Ok(PollRecvData {
619 buf: filled_buf,
620 src_addr: peer_addr,
621 dst_addr_override,
622 rx_time,
623 gro,
624 so_mark_data: mark_bytes,
625 }));
626 },
627 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
628 ready!(udp_socket.poll_recv_ready(cx))?
636 },
637 Err(e) => return Poll::Ready(Err(e)),
638 }
639 }
640 }
641 }
642
643 fn poll_process_packet(&mut self, cx: &mut Context) -> Poll<()> {
644 let pkt_data = match ready!(self.poll_recv_and_rx_time(cx)) {
645 Ok(v) => v,
646 Err(e) => {
647 log::error!("Incoming packet router encountered recvmsg error"; "error" => e);
648 return Poll::Ready(());
649 },
650 };
651
652 let PollRecvData {
653 buf,
654 src_addr: peer_addr,
655 dst_addr_override,
656 rx_time,
657 gro,
658 #[cfg(target_os = "linux")]
659 so_mark_data,
660 } = pkt_data;
661
662 let send_from = if let Some(dst_addr) = dst_addr_override {
663 log::trace!("overriding local address"; "actual_local" => dst_addr, "configured_local" => self.local_addr);
664 dst_addr
665 } else {
666 self.local_addr
667 };
668
669 let res = self.on_incoming(Incoming {
670 peer_addr,
671 local_addr: send_from,
672 buf,
673 rx_time,
674 gro,
675 #[cfg(target_os = "linux")]
676 so_mark_data,
677 });
678
679 let Err(e) = res else {
682 return Poll::Ready(());
683 };
684
685 let err_type = initial_packet_error_type(&e);
686 self.metrics
687 .rejected_initial_packet_count(err_type.clone())
688 .inc();
689
690 if self.config.enable_expensive_packet_count_metrics {
691 if let Some(peer_ip) =
692 quic_expensive_metrics_ip_reduce(peer_addr.ip())
693 {
694 self.metrics
695 .expensive_rejected_initial_packet_count(
696 err_type.clone(),
697 peer_ip,
698 )
699 .inc();
700 }
701 }
702
703 if matches!(err_type, labels::QuicInvalidInitialPacketError::Unexpected) {
704 let _ = self.accept_sink.try_send(Err(e));
706 }
707
708 Poll::Ready(())
709 }
710
711 fn poll_conn_map_commands(&mut self, cx: &mut Context) -> Poll<()> {
712 let cmd_rx = &mut self.conn_map_cmd_rx;
713 let buf = &mut self.conn_map_cmd_buf;
714 debug_assert!(buf.is_empty());
715
716 while ready!(cmd_rx.poll_recv_many(cx, buf, CONN_MAP_CMD_BATCH_SIZE)) > 0
717 {
718 for cmd in buf.drain(..) {
719 match cmd {
720 ConnectionMapCommand::MapCid {
721 existing_cid,
722 new_cid,
723 } => self.conns.map_cid(&existing_cid, &new_cid),
724 ConnectionMapCommand::UnmapCid(cid) =>
725 self.conns.unmap_cid(&cid),
726 }
727 }
728 }
729
730 Poll::Ready(())
731 }
732}
733
734fn short_dcid(buf: &[u8]) -> Option<ConnectionId<'_>> {
736 let is_short_dcid = buf.first()? >> 7 == 0;
737
738 if is_short_dcid {
739 buf.get(1..1 + MAX_CONN_ID_LEN).map(ConnectionId::from_ref)
740 } else {
741 None
742 }
743}
744
745fn instant_to_system(ts: Instant) -> SystemTime {
748 let now = Instant::now();
749 let system_now = SystemTime::now();
750 if let Some(delta) = now.checked_duration_since(ts) {
751 return system_now - delta;
752 }
753
754 let delta = ts.checked_duration_since(now).expect("now < ts");
755 system_now + delta
756}
757
758#[cfg(target_os = "linux")]
768fn resolve_dst_addr(
769 local: &SocketAddr, parsed: &SocketAddr,
770) -> Option<SocketAddr> {
771 if local != parsed {
772 return Some(*parsed);
773 }
774
775 None
776}
777
778impl<Tx, Rx, M, I> Future for InboundPacketRouter<Tx, Rx, M, I>
779where
780 Tx: DatagramSocketSend + Send + Sync + 'static,
781 Rx: DatagramSocketRecv + Unpin,
782 M: Metrics,
783 I: InitialPacketHandler + Unpin,
784{
785 type Output = io::Result<()>;
786
787 fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
788 loop {
789 if self.shutdown_tx.is_some() && self.accept_sink.is_closed() {
791 self.shutdown_tx = None;
792 }
793
794 if self.shutdown_tx.is_none() &&
796 self.shutdown_rx.poll_recv(cx).is_ready()
797 {
798 return Poll::Ready(Ok(()));
799 }
800
801 if let Err(error) = self.incoming_packet_handler.update(cx) {
803 let sender = self.accept_sink.clone();
806 spawn_with_killswitch(async move {
807 let _ = sender.send(Err(error)).await;
808 });
809 }
810
811 let _ = self.poll_conn_map_commands(cx);
816
817 for _ in 0..PACKET_RX_YIELD_AFTER {
820 ready!(self.poll_process_packet(cx));
821 }
822 }
823 }
824}
825
826fn initial_packet_error_type(
831 e: &io::Error,
832) -> labels::QuicInvalidInitialPacketError {
833 Some(e)
834 .filter(|e| e.kind() == io::ErrorKind::Other)
835 .and_then(io::Error::get_ref)
836 .and_then(|e| e.downcast_ref())
837 .map_or(
838 labels::QuicInvalidInitialPacketError::Unexpected,
839 Clone::clone,
840 )
841}
842
843pub trait InitialPacketHandler {
852 fn update(&mut self, _ctx: &mut Context<'_>) -> io::Result<()> {
853 Ok(())
854 }
855
856 fn handle_initials(
857 &mut self, incoming: Incoming, hdr: Header<'static>,
858 quiche_config: &mut quiche::Config,
859 ) -> io::Result<Option<NewConnection>>;
860}
861
862pub struct NewConnection {
865 conn: Box<QuicheConnection>,
867 pending_cid: Option<ConnectionId<'static>>,
868 initial_pkt: Option<Incoming>,
869 cid_generator: Option<SharedConnectionIdGenerator>,
870 handshake_start_time: Instant,
873}
874
875#[cfg(all(test, unix))]
878mod tests {
879 use super::acceptor::ConnectionAcceptor;
880 use super::acceptor::ConnectionAcceptorConfig;
881 use super::*;
882
883 use crate::http3::settings::Http3Settings;
884 use crate::metrics::DefaultMetrics;
885 use crate::quic::connection::SimpleConnectionIdGenerator;
886 use crate::settings::Config;
887 use crate::settings::Hooks;
888 use crate::settings::QuicSettings;
889 use crate::settings::TlsCertificatePaths;
890 use crate::socket::SocketCapabilities;
891 use crate::ConnectionIdGenerator as _;
892 use crate::ConnectionParams;
893 use crate::ServerH3Driver;
894
895 use datagram_socket::MAX_DATAGRAM_SIZE;
896 use futures::FutureExt as _;
897 use h3i::actions::h3::Action;
898 use std::net::Ipv4Addr;
899 use std::sync::Arc;
900 use std::time::Duration;
901 use tokio::net::UdpSocket;
902 use tokio::time;
903
904 const TEST_CERT_FILE: &str = concat!(
905 env!("CARGO_MANIFEST_DIR"),
906 "/",
907 "../quiche/examples/cert.crt"
908 );
909 const TEST_KEY_FILE: &str = concat!(
910 env!("CARGO_MANIFEST_DIR"),
911 "/",
912 "../quiche/examples/cert.key"
913 );
914
915 fn test_connect(host_port: String) {
916 let h3i_config = h3i::config::Config::new()
917 .with_host_port("test.com".to_string())
918 .with_idle_timeout(2000)
919 .with_connect_to(host_port)
920 .verify_peer(false)
921 .build()
922 .unwrap();
923
924 let conn_close = h3i::quiche::ConnectionError {
925 is_app: true,
926 error_code: h3i::quiche::WireErrorCode::NoError as _,
927 reason: Vec::new(),
928 };
929 let actions = vec![Action::ConnectionClose { error: conn_close }];
930
931 let _ = h3i::client::sync_client::connect(h3i_config, actions, None);
932 }
933
934 #[tokio::test]
935 async fn test_timeout() {
936 let quic_settings = QuicSettings {
939 max_idle_timeout: Some(Duration::from_millis(1)),
940 max_recv_udp_payload_size: MAX_DATAGRAM_SIZE,
941 max_send_udp_payload_size: MAX_DATAGRAM_SIZE,
942 ..Default::default()
943 };
944
945 let tls_cert_settings = TlsCertificatePaths {
946 cert: TEST_CERT_FILE,
947 private_key: TEST_KEY_FILE,
948 kind: crate::settings::CertificateKind::X509,
949 };
950
951 let params = ConnectionParams::new_server(
952 quic_settings,
953 tls_cert_settings,
954 Hooks::default(),
955 );
956 let config = Config::new(¶ms, SocketCapabilities::default()).unwrap();
957
958 let socket = UdpSocket::bind("127.0.0.1:0").await.unwrap();
959 let local_addr = socket.local_addr().unwrap();
960 let host_port = local_addr.to_string();
961 let socket_tx = Arc::new(socket);
962 let socket_rx = Arc::clone(&socket_tx);
963
964 let acceptor = ConnectionAcceptor::new(
965 ConnectionAcceptorConfig {
966 disable_client_ip_validation: config.disable_client_ip_validation,
967 qlog_dir: config.qlog_dir.clone(),
968 qlog_compression: config.qlog_compression,
969 keylog_file: config
970 .keylog_file
971 .as_ref()
972 .and_then(|f| f.try_clone().ok()),
973 #[cfg(target_os = "linux")]
974 with_pktinfo: false,
975 },
976 Arc::clone(&socket_tx),
977 Default::default(),
978 Arc::new(SimpleConnectionIdGenerator),
979 DefaultMetrics,
980 );
981
982 let (socket_driver, mut incoming) = InboundPacketRouter::new(
983 config,
984 socket_tx,
985 socket_rx,
986 local_addr,
987 acceptor,
988 DefaultMetrics,
989 );
990 tokio::spawn(socket_driver);
991
992 std::thread::spawn(move || test_connect(host_port));
994
995 time::pause();
997
998 let (h3_driver, _) = ServerH3Driver::new(Http3Settings::default());
999 let conn = incoming.recv().await.unwrap().unwrap();
1000 let drop_check = conn.incoming_ev_sender.clone();
1001 let _conn = conn.start(h3_driver);
1002
1003 time::advance(Duration::new(30, 0)).await;
1005 time::resume();
1006
1007 drop_check.closed().await;
1010 }
1011
1012 struct NoopDatagramSender;
1013 impl DatagramSocketSend for NoopDatagramSender {
1014 fn poll_send(
1015 &self, _cx: &mut Context, buf: &[u8],
1016 ) -> Poll<io::Result<usize>> {
1017 Poll::Ready(Ok(buf.len()))
1018 }
1019
1020 fn poll_send_to(
1021 &self, _cx: &mut Context, buf: &[u8], _addr: SocketAddr,
1022 ) -> Poll<io::Result<usize>> {
1023 Poll::Ready(Ok(buf.len()))
1024 }
1025 }
1026
1027 struct AlwaysReadyReceiver;
1028 impl DatagramSocketRecv for AlwaysReadyReceiver {
1029 fn poll_recv(
1030 &mut self, _cx: &mut Context, buf: &mut tokio::io::ReadBuf,
1031 ) -> Poll<io::Result<()>> {
1032 const DUMMY_QUIC_PACKET: &[u8] =
1035 b"\x40THIS_20_BYTE_CONN_ID\x06payload_payload_payload";
1036 buf.put_slice(DUMMY_QUIC_PACKET);
1037 Poll::Ready(Ok(()))
1038 }
1039 }
1040
1041 struct NoopInitialHandler;
1042 impl InitialPacketHandler for NoopInitialHandler {
1043 fn handle_initials(
1044 &mut self, _incoming: Incoming, _hdr: Header<'static>,
1045 _quiche_config: &mut quiche::Config,
1046 ) -> io::Result<Option<NewConnection>> {
1047 Ok(None)
1048 }
1049 }
1050
1051 #[test]
1052 fn test_poll_packet_always_ready() {
1053 let tls_cert_settings = TlsCertificatePaths {
1054 cert: TEST_CERT_FILE,
1055 private_key: TEST_KEY_FILE,
1056 kind: crate::settings::CertificateKind::X509,
1057 };
1058 let params = ConnectionParams::new_server(
1059 QuicSettings::default(),
1060 tls_cert_settings,
1061 Hooks::default(),
1062 );
1063
1064 let config = Config::new(¶ms, SocketCapabilities::default()).unwrap();
1065 let local_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0);
1066
1067 let (mut ipr, accept_stream) = InboundPacketRouter::new(
1068 config,
1069 Arc::new(NoopDatagramSender),
1070 AlwaysReadyReceiver,
1071 local_addr,
1072 NoopInitialHandler,
1073 DefaultMetrics,
1074 );
1075 let conn_map_cmd_tx = ipr.conn_map_cmd_tx.clone();
1076
1077 let (ipr_notifier, ipr_done) = std::sync::mpsc::sync_channel::<()>(0);
1079 let ipr = std::thread::spawn(move || {
1080 let mut cx = Context::from_waker(std::task::Waker::noop());
1081 while ipr.poll_unpin(&mut cx).is_pending() {
1082 std::thread::sleep(Duration::from_millis(10));
1083 }
1084 drop(ipr_notifier);
1085 ipr
1086 });
1087
1088 for _ in 0..20 {
1090 let random_cid = SimpleConnectionIdGenerator.new_connection_id();
1091 conn_map_cmd_tx
1092 .send(ConnectionMapCommand::UnmapCid(random_cid))
1093 .unwrap();
1094 }
1095 std::thread::sleep(Duration::from_secs(1));
1097
1098 drop(accept_stream);
1102 let ipr_done_res = ipr_done.recv_timeout(Duration::from_secs(10));
1103 assert_eq!(
1104 ipr_done_res,
1105 Err(std::sync::mpsc::RecvTimeoutError::Disconnected)
1106 );
1107
1108 let ipr = ipr.join().unwrap();
1111 assert!(ipr.conn_map_cmd_rx.is_empty());
1112 }
1113}