Skip to main content

net_io/
tcp.rs

1//! TCP listeners and connections.
2
3use std::io::{self, IoSlice, IoSliceMut, Read, Write};
4use std::net::{Shutdown, SocketAddr, ToSocketAddrs};
5use std::sync::{Arc, Mutex, PoisonError};
6use std::time::{Duration, Instant};
7
8use crate::accept::{Accepted, Acceptor};
9use crate::driver::DriverKind;
10use crate::error::{CloseReason, ConfigReason, LimitKind, NetError, RefusalCause, TimeoutKind};
11use crate::gate::{AdmitStage, Gate, GateCell, KillHandle, guarded};
12use crate::id::{ConnectionId, ListenerId, WorkerId};
13use crate::info::{ConnectionInfo, Endpoint, Origin, Transport};
14use crate::limits::{Effective, Limits};
15use crate::link::{ClockRef, Got, Link, WakeSource};
16use crate::opts::{ListenOptions, SocketOptions, TcpOptions};
17use crate::proxy::ProxyMode;
18use crate::session::Phase;
19use crate::sys::{self, AddrFailure, AsSock, Interest, KillSlot, Ready};
20
21/// Facts about one bound listener.
22///
23/// ```
24/// let listener = net_io::listen("127.0.0.1:0")?;
25/// assert!(listener.info().local.socket_addr().is_some());
26/// # Ok::<(), net_io::NetError>(())
27/// ```
28#[non_exhaustive]
29#[derive(Clone, Debug)]
30pub struct ListenerInfo {
31    /// The listener's index in its server (0 for a plain listener).
32    pub id: ListenerId,
33    /// Its name, if one was given.
34    pub name: Option<String>,
35    /// The bound address.
36    pub local: Endpoint,
37    /// The effective backlog; `None` when it cannot be read (macOS, adopted sockets).
38    pub backlog: Option<u32>,
39    /// The kind of socket.
40    pub transport: Transport,
41    /// Whether it expects a PROXY header.
42    pub proxy: ProxyMode,
43    /// Whether it serves TLS.
44    pub tls: bool,
45}
46
47/// Linux silently caps the backlog at `net.core.somaxconn`; read it once.
48pub(crate) fn effective_backlog(requested: u32) -> Option<u32> {
49    if cfg!(target_os = "linux") {
50        let cap = std::fs::read_to_string("/proc/sys/net/core/somaxconn")
51            .ok()
52            .and_then(|s| s.trim().parse::<u32>().ok());
53        Some(cap.map_or(requested, |c| requested.min(c)))
54    } else if cfg!(windows) {
55        Some(requested)
56    } else {
57        None
58    }
59}
60
61/// Whether accepted sockets inherit the listener's options (spike S-4, decision D-055).
62const OPTIONS_INHERITED: bool = cfg!(any(target_os = "linux", windows));
63
64pub(crate) fn bind_error(addr: SocketAddr, e: io::Error) -> NetError {
65    match sys::classify_addr_error(&e) {
66        AddrFailure::InUse => NetError::AddrInUse { addr },
67        AddrFailure::NotAvailable => NetError::AddrNotAvailable { addr },
68        AddrFailure::Permission => NetError::PermissionDenied { what: "bind" },
69        _ if e.kind() == io::ErrorKind::Unsupported => NetError::Unsupported {
70            feature: "a listen option this OS does not have",
71            platform: std::env::consts::OS,
72        },
73        _ => NetError::Bind { addr, source: e },
74    }
75}
76
77fn connect_error(addr: SocketAddr, after: Duration, e: io::Error) -> NetError {
78    match sys::classify_addr_error(&e) {
79        AddrFailure::Refused => NetError::ConnectionRefused { addr },
80        AddrFailure::HostUnreachable => NetError::HostUnreachable { addr },
81        AddrFailure::NetworkUnreachable => NetError::NetworkUnreachable { addr },
82        AddrFailure::TimedOut => NetError::ConnectTimeout { addr, after },
83        AddrFailure::NotAvailable => NetError::AddrNotAvailable { addr },
84        AddrFailure::Permission => NetError::PermissionDenied { what: "connect" },
85        _ => NetError::Os {
86            op: "connect",
87            source: e,
88        },
89    }
90}
91
92fn resolve<A: ToSocketAddrs>(addr: A) -> Result<Vec<SocketAddr>, NetError> {
93    let addrs: Vec<SocketAddr> = addr
94        .to_socket_addrs()
95        .map_err(|e| NetError::Os {
96            op: "resolve",
97            source: e,
98        })?
99        .collect();
100    if addrs.is_empty() {
101        return Err(NetError::NoAddresses);
102    }
103    Ok(addrs)
104}
105
106/// A listening TCP socket created by net-io, so options that must precede `bind` can be set.
107///
108/// # Examples
109///
110/// ```no_run
111/// let listener = net_io::listen("0.0.0.0:7000")?;
112/// loop {
113///     let (mut conn, info) = listener.accept()?;
114///     println!("connection {} from {:?}", info.id, info.client_addr());
115///     conn.send_all(b"hello\n")?;
116///     conn.close()?;
117/// }
118/// # Ok::<(), net_io::NetError>(())
119/// ```
120#[derive(Debug)]
121pub struct TcpListener {
122    inner: std::net::TcpListener,
123    info: ListenerInfo,
124    tcp: TcpOptions,
125    acceptor: Mutex<Acceptor>,
126}
127
128impl TcpListener {
129    /// Binds with safe defaults (see [`crate::listen`]).
130    ///
131    /// # Errors
132    ///
133    /// `AddrInUse`, `PermissionDenied`, `AddrNotAvailable`, `NoAddresses`, `Bind`.
134    pub fn bind<A: ToSocketAddrs>(addr: A) -> Result<TcpListener, NetError> {
135        TcpListener::bind_with(addr, ListenOptions::new())
136    }
137
138    /// Binds with `options`, applied in the order `docs/API.md` gives.
139    ///
140    /// # Errors
141    ///
142    /// As [`TcpListener::bind`]; `Unsupported` for an option the platform lacks.
143    pub fn bind_with<A: ToSocketAddrs>(
144        addr: A,
145        options: ListenOptions,
146    ) -> Result<TcpListener, NetError> {
147        let mut last = NetError::NoAddresses;
148        for a in resolve(addr)? {
149            match sys::tcp_listener(a, &options.params()) {
150                Ok(l) => return TcpListener::finish(l, options, ListenerId(0), None),
151                Err(e) => last = bind_error(a, e),
152            }
153        }
154        Err(last)
155    }
156
157    pub(crate) fn finish(
158        inner: std::net::TcpListener,
159        options: ListenOptions,
160        id: ListenerId,
161        name: Option<String>,
162    ) -> Result<TcpListener, NetError> {
163        let local = inner
164            .local_addr()
165            .map_err(|e| NetError::from_os("getsockname", e))?;
166        if OPTIONS_INHERITED && !options.tcp.is_empty() {
167            options.tcp.apply(&inner, local.is_ipv6())?;
168        }
169        Ok(TcpListener {
170            info: ListenerInfo {
171                id,
172                name,
173                local: Endpoint::Ip(local),
174                backlog: effective_backlog(options.backlog),
175                transport: Transport::Tcp,
176                proxy: ProxyMode::Off,
177                tls: false,
178            },
179            inner,
180            tcp: options.tcp,
181            acceptor: Mutex::new(Acceptor::new()),
182        })
183    }
184
185    /// Blocks until a connection arrives; applies per-connection options, normalizes
186    /// the peer, issues a `ConnectionId`. No admission: a plain listener has no gate.
187    ///
188    /// # Errors
189    ///
190    /// `Limit { which: Descriptors }` when exhaustion persists past one backoff step; `Os`.
191    pub fn accept(&self) -> Result<(TcpConnection, ConnectionInfo), NetError> {
192        match self.accept_until(None)? {
193            Some(pair) => Ok(pair),
194            None => Err(NetError::TimedOut {
195                kind: TimeoutKind::Read,
196            }),
197        }
198    }
199
200    /// As [`TcpListener::accept`], but `Ok(None)` when nothing arrived within `timeout`.
201    ///
202    /// # Errors
203    ///
204    /// As [`TcpListener::accept`].
205    ///
206    /// ```
207    /// let listener = net_io::listen("127.0.0.1:0")?;
208    /// let got = listener.accept_timeout(std::time::Duration::from_millis(10))?;
209    /// assert!(got.is_none());
210    /// # Ok::<(), net_io::NetError>(())
211    /// ```
212    pub fn accept_timeout(
213        &self,
214        timeout: Duration,
215    ) -> Result<Option<(TcpConnection, ConnectionInfo)>, NetError> {
216        self.accept_until(Some(Instant::now() + timeout))
217    }
218
219    fn accept_until(
220        &self,
221        deadline: Option<Instant>,
222    ) -> Result<Option<(TcpConnection, ConnectionInfo)>, NetError> {
223        let mut paused_once = false;
224        loop {
225            let outcome = self
226                .acceptor
227                .lock()
228                .unwrap_or_else(PoisonError::into_inner)
229                .accept(&self.inner);
230            match outcome {
231                Accepted::Conn((stream, peer)) => {
232                    let conn = TcpConnection::accepted(stream, peer, &self.tcp, self.info.id)?;
233                    let info = conn.info().clone();
234                    return Ok(Some((conn, info)));
235                }
236                Accepted::Skipped(_) => continue,
237                Accepted::Again => {
238                    let timeout = match deadline {
239                        None => None,
240                        Some(d) => {
241                            let left = d.saturating_duration_since(Instant::now());
242                            if left.is_zero() {
243                                return Ok(None);
244                            }
245                            Some(left)
246                        }
247                    };
248                    match sys::wait(self.inner.sock(), Interest::Read, None, timeout) {
249                        Ok(Ready::TimedOut) => return Ok(None),
250                        Ok(_) => continue,
251                        Err(e) => return Err(NetError::from_os("poll", e)),
252                    }
253                }
254                Accepted::Pause(backoff) => {
255                    if paused_once {
256                        return Err(NetError::Limit {
257                            which: LimitKind::Descriptors,
258                        });
259                    }
260                    paused_once = true;
261                    std::thread::sleep(backoff);
262                }
263                Accepted::Failed(e) => return Err(NetError::from_os("accept", e)),
264            }
265        }
266    }
267
268    pub(crate) fn std(&self) -> &std::net::TcpListener {
269        &self.inner
270    }
271
272    /// Records the listener's id and name in its facts (adopted listeners).
273    pub(crate) fn set_identity(&mut self, id: ListenerId, name: Option<String>) {
274        self.info.id = id;
275        self.info.name = name;
276    }
277
278    /// Records whether the listener serves TLS in its facts.
279    pub(crate) fn set_tls(&mut self, on: bool) {
280        self.info.tls = on;
281    }
282
283    /// Records the listener's PROXY requirement in its facts.
284    pub(crate) fn set_proxy_mode(&mut self, mode: crate::proxy::ProxyMode) {
285        self.info.proxy = mode;
286    }
287
288    pub(crate) fn tcp_options(&self) -> &TcpOptions {
289        &self.tcp
290    }
291
292    /// Facts about this listener.
293    #[must_use]
294    pub fn info(&self) -> &ListenerInfo {
295        &self.info
296    }
297
298    /// The bound address.
299    ///
300    /// # Errors
301    ///
302    /// `Os` when `getsockname` fails.
303    pub fn local_addr(&self) -> Result<SocketAddr, NetError> {
304        self.inner
305            .local_addr()
306            .map_err(|e| NetError::from_os("getsockname", e))
307    }
308
309    /// Adopts an existing listening socket (checked with `SO_ACCEPTCONN` and `SO_TYPE`).
310    ///
311    /// # Errors
312    ///
313    /// `InvalidConfig { field: "socket", reason: NotListening }`.
314    ///
315    /// ```
316    /// let std_listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
317    /// let listener = net_io::TcpListener::from_std(std_listener)?;
318    /// assert!(listener.local_addr().is_ok());
319    /// # Ok::<(), net_io::NetError>(())
320    /// ```
321    pub fn from_std(listener: std::net::TcpListener) -> Result<TcpListener, NetError> {
322        match sys::is_listening_stream(listener.sock()) {
323            Ok(true) => {}
324            Ok(false) => {
325                return Err(NetError::InvalidConfig {
326                    field: "socket",
327                    reason: ConfigReason::NotListening,
328                });
329            }
330            Err(e) => return Err(NetError::from_os("getsockopt", e)),
331        }
332        listener
333            .set_nonblocking(true)
334            .map_err(|e| NetError::from_os("ioctl", e))?;
335        TcpListener::finish(
336            listener,
337            ListenOptions::new().tcp_options(TcpOptions::new()),
338            ListenerId(0),
339            None,
340        )
341        .map(|mut l| {
342            l.info.backlog = None;
343            l
344        })
345    }
346
347    /// Gives the socket up (it stays non-blocking).
348    #[must_use]
349    pub fn into_std(self) -> std::net::TcpListener {
350        self.inner
351    }
352
353    /// A second handle to the same listening socket.
354    ///
355    /// # Errors
356    ///
357    /// `Os` when duplication fails.
358    pub fn try_clone(&self) -> Result<TcpListener, NetError> {
359        let inner = self
360            .inner
361            .try_clone()
362            .map_err(|e| NetError::from_os("dup", e))?;
363        Ok(TcpListener {
364            inner,
365            info: self.info.clone(),
366            tcp: self.tcp,
367            acceptor: Mutex::new(Acceptor::new()),
368        })
369    }
370}
371
372#[cfg(unix)]
373impl std::os::fd::AsFd for TcpListener {
374    fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
375        self.inner.as_fd()
376    }
377}
378
379#[cfg(unix)]
380impl From<TcpListener> for std::os::fd::OwnedFd {
381    fn from(l: TcpListener) -> std::os::fd::OwnedFd {
382        l.inner.into()
383    }
384}
385
386#[cfg(unix)]
387impl TryFrom<std::os::fd::OwnedFd> for TcpListener {
388    type Error = NetError;
389    fn try_from(fd: std::os::fd::OwnedFd) -> Result<TcpListener, NetError> {
390        TcpListener::from_std(std::net::TcpListener::from(fd))
391    }
392}
393
394#[cfg(windows)]
395impl std::os::windows::io::AsSocket for TcpListener {
396    fn as_socket(&self) -> std::os::windows::io::BorrowedSocket<'_> {
397        self.inner.as_socket()
398    }
399}
400
401#[cfg(windows)]
402impl From<TcpListener> for std::os::windows::io::OwnedSocket {
403    fn from(l: TcpListener) -> std::os::windows::io::OwnedSocket {
404        l.inner.into()
405    }
406}
407
408#[cfg(windows)]
409impl TryFrom<std::os::windows::io::OwnedSocket> for TcpListener {
410    type Error = NetError;
411    fn try_from(s: std::os::windows::io::OwnedSocket) -> Result<TcpListener, NetError> {
412        TcpListener::from_std(std::net::TcpListener::from(s))
413    }
414}
415
416/// Options for an outbound connection.
417///
418/// ```no_run
419/// use net_io::{ConnectOptions, TcpConnection};
420/// use std::time::Duration;
421/// let options = ConnectOptions::new().timeout(Duration::from_secs(3));
422/// let conn = TcpConnection::connect_host("example.com", 443, options)?;
423/// println!("connected to {}", conn.peer_addr());
424/// # Ok::<(), net_io::NetError>(())
425/// ```
426#[must_use]
427#[derive(Clone, Debug)]
428pub struct ConnectOptions {
429    timeout: Duration,
430    local_addr: Option<SocketAddr>,
431    tcp: TcpOptions,
432    limits: Limits,
433    happy_eyeballs: bool,
434    attempt_delay: Duration,
435}
436
437impl Default for ConnectOptions {
438    fn default() -> ConnectOptions {
439        ConnectOptions {
440            timeout: Duration::from_secs(10),
441            local_addr: None,
442            tcp: TcpOptions::new().nodelay(true),
443            limits: Limits::default(),
444            happy_eyeballs: false,
445            attempt_delay: Duration::from_millis(250),
446        }
447    }
448}
449
450impl ConnectOptions {
451    /// Timeout 10 s, `TCP_NODELAY` on, no local bind.
452    pub fn new() -> ConnectOptions {
453        ConnectOptions::default()
454    }
455
456    /// The total time for all attempts.
457    pub fn timeout(mut self, total: Duration) -> ConnectOptions {
458        self.timeout = total;
459        self
460    }
461
462    /// Binds to this address before connecting.
463    pub fn local_addr(mut self, addr: SocketAddr) -> ConnectOptions {
464        self.local_addr = Some(addr);
465        self
466    }
467
468    /// Options applied before the connect.
469    pub fn tcp_options(mut self, options: TcpOptions) -> ConnectOptions {
470        self.tcp = options;
471        self
472    }
473
474    /// Limits for the connection.
475    pub fn limits(mut self, limits: Limits) -> ConnectOptions {
476        self.limits = limits;
477        self
478    }
479
480    /// Happy Eyeballs (RFC 8305, simplified because `std` resolution returns both
481    /// families at once): attempts overlap instead of waiting for each other, IPv6
482    /// first and the families interleaved, and the first established connection wins.
483    /// The total timeout still bounds everything; the others are reset.
484    pub fn happy_eyeballs(mut self, on: bool) -> ConnectOptions {
485        self.happy_eyeballs = on;
486        self
487    }
488
489    /// How long an attempt runs alone before the next one starts (250 ms; held to
490    /// 100 ms through 2 s). An attempt that fails starts the next at once.
491    pub fn attempt_delay(mut self, d: Duration) -> ConnectOptions {
492        self.attempt_delay = d.clamp(Duration::from_millis(100), Duration::from_secs(2));
493        self
494    }
495}
496
497/// A connected TCP socket that carries its own limits.
498///
499/// Every read honours the current phase's deadline (a connection from `accept`
500/// starts `Idle` under the idle timeout; one from `connect` starts `Receiving`
501/// under the read timeout), the exchange deadline, and the inbound minimum
502/// rate; every write honours the write deadline and the outbound minimum rate.
503/// One owner: other threads act on it only through its [`KillHandle`].
504///
505/// # Examples
506///
507/// ```no_run
508/// use net_io::{Limits, Phase};
509/// use std::time::{Duration, Instant};
510///
511/// let mut conn = net_io::connect("127.0.0.1:7000")?;
512/// conn.set_limits(Limits::builder().read_timeout(Duration::from_secs(5)).build()?);
513/// conn.set_phase(Phase::Receiving);
514/// conn.set_exchange_deadline(Some(Instant::now() + Duration::from_secs(20)));
515/// let mut buf = [0u8; 1024];
516/// let n = conn.recv(&mut buf)?;
517/// # let _ = n;
518/// # Ok::<(), net_io::NetError>(())
519/// ```
520pub struct TcpConnection {
521    link: Option<Link>,
522}
523
524impl TcpConnection {
525    /// Connects with a 10 s timeout across every resolved address (see [`crate::connect`]).
526    ///
527    /// # Errors
528    ///
529    /// `ConnectTimeout`, `ConnectionRefused`, `HostUnreachable`, `NetworkUnreachable`, `NoAddresses`.
530    pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<TcpConnection, NetError> {
531        TcpConnection::connect_with(addr, ConnectOptions::new())
532    }
533
534    /// Connects with `options`.
535    ///
536    /// # Errors
537    ///
538    /// As [`TcpConnection::connect`].
539    pub fn connect_with<A: ToSocketAddrs>(
540        addr: A,
541        options: ConnectOptions,
542    ) -> Result<TcpConnection, NetError> {
543        let addrs = resolve(addr)?;
544        TcpConnection::connect_addrs(&addrs, &options)
545    }
546
547    /// Resolves `host` with the standard library and connects to the addresses in
548    /// order; each attempt gets the remaining time divided by the remaining
549    /// addresses (at least 1 s).
550    ///
551    /// # Errors
552    ///
553    /// `NoAddresses`, or the last connect error.
554    pub fn connect_host(
555        host: &str,
556        port: u16,
557        options: ConnectOptions,
558    ) -> Result<TcpConnection, NetError> {
559        let addrs = resolve((host, port))?;
560        TcpConnection::connect_addrs(&addrs, &options)
561    }
562
563    fn connect_addrs(
564        addrs: &[SocketAddr],
565        options: &ConnectOptions,
566    ) -> Result<TcpConnection, NetError> {
567        if options.happy_eyeballs && addrs.len() > 1 {
568            return connect_racing(addrs, options);
569        }
570        let start = Instant::now();
571        let deadline = start + options.timeout;
572        let mut last = NetError::NoAddresses;
573        for (i, addr) in addrs.iter().enumerate() {
574            let left = deadline.saturating_duration_since(Instant::now());
575            if left.is_zero() {
576                break;
577            }
578            let remaining = (addrs.len() - i) as u32;
579            let attempt = (left / remaining).max(Duration::from_secs(1)).min(left);
580            match connect_one(*addr, options, attempt) {
581                Ok(conn) => return Ok(conn),
582                Err(e) => last = e,
583            }
584        }
585        if matches!(last, NetError::NoAddresses) {
586            if let Some(addr) = addrs.first() {
587                last = NetError::ConnectTimeout {
588                    addr: *addr,
589                    after: options.timeout,
590                };
591            }
592        }
593        Err(last)
594    }
595
596    pub(crate) fn accepted(
597        stream: std::net::TcpStream,
598        peer: SocketAddr,
599        tcp: &TcpOptions,
600        listener: ListenerId,
601    ) -> Result<TcpConnection, NetError> {
602        let local = stream
603            .local_addr()
604            .map_err(|e| NetError::from_os("getsockname", e))?;
605        if !OPTIONS_INHERITED && !tcp.is_empty() {
606            tcp.apply(&stream, local.is_ipv6())?;
607        }
608        let info = ConnectionInfo::new_tcp(
609            crate::id::issue_single(),
610            Some(listener),
611            WorkerId(0),
612            Origin::Accepted,
613            local,
614            peer,
615            Instant::now(),
616        );
617        let limits = Arc::new(Limits::default().effective(DriverKind::Blocking));
618        Ok(TcpConnection::from_link(Link::new(
619            sys::Stream::Tcp(stream),
620            info,
621            limits,
622            ClockRef::Monotonic,
623            Arc::new(KillSlot::new()),
624            WakeSource::Thread,
625            Phase::Idle,
626        )))
627    }
628
629    pub(crate) fn from_link(link: Link) -> TcpConnection {
630        TcpConnection { link: Some(link) }
631    }
632
633    /// The process at an end of a named pipe.
634    #[cfg(all(windows, feature = "local"))]
635    pub(crate) fn pipe_pid(&self, server: bool) -> Result<u32, NetError> {
636        match &self.link_ref().stream {
637            sys::Stream::Pipe(p) => if server {
638                p.server_pid()
639            } else {
640                p.client_pid()
641            }
642            .map_err(|e| NetError::from_os("GetNamedPipeClientProcessId", e)),
643            _ => Err(NetError::Unsupported {
644                feature: "client_pid on a socket",
645                platform: std::env::consts::OS,
646            }),
647        }
648    }
649
650    pub(crate) fn take_link(&mut self) -> Option<Link> {
651        self.link.take()
652    }
653
654    pub(crate) fn link(&mut self) -> Result<&mut Link, NetError> {
655        self.link.as_mut().ok_or(NetError::Closed)
656    }
657
658    pub(crate) fn link_ref(&self) -> &Link {
659        match &self.link {
660            Some(l) => l,
661            // Only `take_link` (crate-internal, at the end of a handler) empties it.
662            None => unreachable_link(),
663        }
664    }
665
666    /// One receive; `Ok(0)` means the peer closed its side. A zero-length `buf`
667    /// returns `Ok(0)` without a system call (check `buf.len()`).
668    ///
669    /// # Errors
670    ///
671    /// `TimedOut { kind }`, `SlowPeer { In }`, `ConnectionReset`, `Killed`, `Closed`.
672    pub fn recv(&mut self, buf: &mut [u8]) -> Result<usize, NetError> {
673        let link = self.link()?;
674        loop {
675            match link.read(buf)? {
676                Got::Data(n) => return Ok(n),
677                Got::Eof => return Ok(0),
678                Got::Woken => continue,
679            }
680        }
681    }
682
683    /// Receives until `buf` is full.
684    ///
685    /// # Errors
686    ///
687    /// As [`TcpConnection::recv`]; `Closed` when the peer closes first.
688    pub fn recv_exact(&mut self, buf: &mut [u8]) -> Result<(), NetError> {
689        let mut filled = 0;
690        while filled < buf.len() {
691            match self.recv(&mut buf[filled..])? {
692                0 => return Err(NetError::Closed),
693                n => filled += n,
694            }
695        }
696        Ok(())
697    }
698
699    /// One receive into the first non-empty slice.
700    ///
701    /// # Errors
702    ///
703    /// As [`TcpConnection::recv`].
704    pub fn recv_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize, NetError> {
705        match bufs.iter_mut().find(|b| !b.is_empty()) {
706            Some(first) => self.recv(first),
707            None => Ok(0),
708        }
709    }
710
711    /// Reads without consuming (`MSG_PEEK`), within the read timeout.
712    ///
713    /// # Errors
714    ///
715    /// As [`TcpConnection::recv`]; `Unsupported` on a TLS connection.
716    pub fn peek(&self, buf: &mut [u8]) -> Result<usize, NetError> {
717        let link = self.link_ref();
718        if buf.is_empty() {
719            return Ok(0);
720        }
721        if link.has_tls() {
722            // The socket holds ciphertext; peeking at it would mean nothing here.
723            return Err(NetError::Unsupported {
724                feature: "peek on a TLS connection",
725                platform: std::env::consts::OS,
726            });
727        }
728        let timeout = link.limits.read_timeout;
729        match link.stream.wait(Interest::Read, None, Some(timeout)) {
730            Ok(Ready::TimedOut) => {
731                return Err(NetError::TimedOut {
732                    kind: TimeoutKind::Read,
733                });
734            }
735            Ok(_) => {}
736            Err(e) => return Err(NetError::from_os("poll", e)),
737        }
738        link.stream
739            .peek(buf)
740            .map_err(|e| NetError::from_os("recv", e))
741    }
742
743    /// One send after the gate check; may be partial.
744    ///
745    /// # Errors
746    ///
747    /// `TimedOut`, `SlowPeer { Out }`, `BrokenPipe`, `ConnectionReset`, `EgressDenied`, `Killed`.
748    pub fn send(&mut self, buf: &[u8]) -> Result<usize, NetError> {
749        self.link()?.write(buf)
750    }
751
752    /// Sends all of `buf`, continuing from the exact offset; the gate check runs
753    /// before every system call.
754    ///
755    /// # Errors
756    ///
757    /// As [`TcpConnection::send`].
758    pub fn send_all(&mut self, buf: &[u8]) -> Result<(), NetError> {
759        self.link()?.write_all(buf)
760    }
761
762    /// One vectored send (up to 64 slices) after the gate check; may be partial.
763    ///
764    /// # Errors
765    ///
766    /// As [`TcpConnection::send`].
767    pub fn send_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize, NetError> {
768        self.link()?.write_vectored(bufs)
769    }
770
771    /// Sends `len` bytes of `file` from `offset`: zero copy where the OS can (`sendfile`
772    /// on Unix), by reading and writing otherwise (TLS; Windows outside the reactor). A
773    /// gate check runs before each 1 MiB. The file's position does not matter, and on
774    /// Unix it does not move.
775    ///
776    /// # Errors
777    ///
778    /// As [`TcpConnection::send`]; `FileTruncated { sent }` when the file ends early,
779    /// and the connection is then reset. Send files that do not change while in flight.
780    pub fn send_file(
781        &mut self,
782        file: &std::fs::File,
783        offset: u64,
784        len: u64,
785    ) -> Result<u64, NetError> {
786        self.link()?.send_file(file, offset, len)
787    }
788
789    /// Sends every slice, advancing them in place (no copy).
790    ///
791    /// # Errors
792    ///
793    /// As [`TcpConnection::send`].
794    pub fn send_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<(), NetError> {
795        self.link()?.write_all_vectored(bufs)
796    }
797
798    /// Half-close: `Write` sends FIN and keeps reading; `Read` stops reading.
799    ///
800    /// # Errors
801    ///
802    /// `Closed`, `Os`.
803    pub fn shutdown(&mut self, how: Shutdown) -> Result<(), NetError> {
804        self.link()?.shutdown(how)
805    }
806
807    /// Graceful close: FIN, then reads and discards up to 64 KiB for up to 2 s so
808    /// unread input does not cause an RST that destroys the last response.
809    ///
810    /// # Errors
811    ///
812    /// None in this version; the signature leaves room for a flush error.
813    pub fn close(mut self) -> Result<(), NetError> {
814        if let Some(link) = self.link.take() {
815            let reason = link.reason_or(CloseReason::SessionClosed);
816            link.finish(reason);
817        }
818        Ok(())
819    }
820
821    /// Abortive close: RST, kernel resources freed at once.
822    pub fn abort(mut self) {
823        if let Some(link) = self.link.take() {
824            let reason = link.reason_or(CloseReason::SessionError);
825            let reason = if reason.is_abortive() {
826                reason
827            } else {
828                CloseReason::SessionError
829            };
830            link.finish(reason);
831        }
832    }
833
834    /// What net-io knows about this connection.
835    #[must_use]
836    pub fn info(&self) -> &ConnectionInfo {
837        &self.link_ref().info
838    }
839
840    /// The connection's id.
841    #[must_use]
842    pub fn id(&self) -> ConnectionId {
843        self.link_ref().info.id
844    }
845
846    /// The socket peer.
847    #[must_use]
848    pub fn peer_addr(&self) -> SocketAddr {
849        self.link_ref()
850            .info
851            .peer
852            .socket_addr()
853            .unwrap_or_else(|| SocketAddr::from(([0, 0, 0, 0], 0)))
854    }
855
856    /// Our end.
857    #[must_use]
858    pub fn local_addr(&self) -> SocketAddr {
859        self.link_ref()
860            .info
861            .local
862            .socket_addr()
863            .unwrap_or_else(|| SocketAddr::from(([0, 0, 0, 0], 0)))
864    }
865
866    /// A handle other threads can use to abort this connection.
867    #[must_use]
868    pub fn kill_handle(&self) -> KillHandle {
869        let link = self.link_ref();
870        KillHandle {
871            slot: link.slot.clone(),
872            generation: link.generation,
873            id: link.info.id,
874        }
875    }
876
877    /// Replaces the limits from the next operation on (unset values: blocking-driver defaults).
878    pub fn set_limits(&mut self, limits: Limits) {
879        if let Ok(link) = self.link() {
880            let e: Effective = limits.effective(DriverKind::Blocking);
881            link.set_limits(e);
882        }
883    }
884
885    /// Chooses which deadlines apply.
886    pub fn set_phase(&mut self, phase: Phase) {
887        if let Ok(link) = self.link() {
888            link.set_phase(phase);
889        }
890    }
891
892    /// The deadline for the current exchange (`None` clears it).
893    pub fn set_exchange_deadline(&mut self, at: Option<Instant>) {
894        if let Ok(link) = self.link() {
895            link.set_exchange_deadline(at);
896        }
897    }
898
899    /// Registers this dialed connection with `gate` (`Gate::admit` with
900    /// `AdmitStage::Dialed`): from then on `Gate::check` runs before every write
901    /// and `Gate::closed` runs once at close, so a back-channel or outbound
902    /// connection can be revoked like an accepted one.
903    ///
904    /// # Errors
905    ///
906    /// `Refused { cause: Gate(code) }` when the gate refuses (the connection is
907    /// then aborted and later calls return `Closed`); `InvalidConfig { field:
908    /// "gate" }` when a gate is already set; `Closed`.
909    ///
910    /// ```no_run
911    /// use std::sync::Arc;
912    /// let mut upstream = net_io::connect("10.0.0.9:9000")?;
913    /// upstream.set_gate(Arc::new(net_io::AllowAll))?;
914    /// upstream.send_all(b"PING\r\n")?;
915    /// # Ok::<(), net_io::NetError>(())
916    /// ```
917    pub fn set_gate<G: Gate>(&mut self, gate: Arc<G>) -> Result<(), NetError> {
918        let link = self.link()?;
919        if link.gate.is_some() {
920            return Err(NetError::InvalidConfig {
921                field: "gate",
922                reason: ConfigReason::Inconsistent,
923            });
924        }
925        let code = match guarded(|| gate.admit(&link.info, AdmitStage::Dialed)) {
926            Some(Ok(admitted)) => {
927                link.info.lane = admitted.lane;
928                link.info.gate = admitted.tag;
929                link.gate = Some(Box::new(GateCell {
930                    gate,
931                    ticket: Some(admitted.ticket),
932                }));
933                return Ok(());
934            }
935            Some(Err(refusal)) => refusal.code(),
936            None => 0,
937        };
938        let cause = RefusalCause::Gate(code);
939        if let Some(link) = self.link.take() {
940            link.finish(CloseReason::Refused(cause));
941        }
942        Err(NetError::Refused { cause })
943    }
944
945    /// Live socket options.
946    #[must_use]
947    pub fn options(&self) -> SocketOptions<'_> {
948        let link = self.link_ref();
949        SocketOptions {
950            sock: link.stream.sock(),
951            v6: link.info.local.socket_addr().is_some_and(|a| a.is_ipv6()),
952        }
953    }
954
955    /// The socket's pending error (`SO_ERROR`).
956    ///
957    /// # Errors
958    ///
959    /// `Os` when the OS call fails.
960    pub fn take_error(&self) -> Result<Option<io::Error>, NetError> {
961        sys::take_error(self.link_ref().stream.sock())
962            .map_err(|e| NetError::from_os("getsockopt", e))
963    }
964}
965
966#[cold]
967fn unreachable_link() -> ! {
968    // A TcpConnection is emptied only by the server after its handler returned,
969    // when no code can reach it any more.
970    std::process::abort()
971}
972
973/// A non-blocking socket ready to connect to `addr`.
974fn dial_socket(
975    addr: SocketAddr,
976    options: &ConnectOptions,
977) -> Result<std::net::TcpStream, NetError> {
978    let owned = sys::tcp_socket(addr.is_ipv6()).map_err(|e| NetError::from_os("socket", e))?;
979    let stream = std::net::TcpStream::from(owned);
980    stream
981        .set_nonblocking(true)
982        .map_err(|e| NetError::from_os("ioctl", e))?;
983    options.tcp.apply(&stream, addr.is_ipv6())?;
984    if let Some(local) = options.local_addr {
985        sys::bind_to(stream.sock(), local).map_err(|e| bind_error(local, e))?;
986    }
987    Ok(stream)
988}
989
990/// IPv6 first, then the families take turns.
991fn interleaved(addrs: &[SocketAddr]) -> Vec<SocketAddr> {
992    let (v6, v4): (Vec<_>, Vec<_>) = addrs.iter().copied().partition(SocketAddr::is_ipv6);
993    let (mut v6, mut v4) = (v6.into_iter(), v4.into_iter());
994    let mut out = Vec::with_capacity(addrs.len());
995    loop {
996        match (v6.next(), v4.next()) {
997            (None, None) => return out,
998            (a, b) => out.extend(a.into_iter().chain(b)),
999        }
1000    }
1001}
1002
1003/// At most this many attempts run at once, and never two start closer than this.
1004const MAX_RACING: usize = 16;
1005const RACE_SPACING: Duration = Duration::from_millis(10);
1006
1007/// Happy Eyeballs: see [`ConnectOptions::happy_eyeballs`]. Dropping a losing attempt
1008/// resets it, so nothing lingers half open at the far end.
1009fn connect_racing(
1010    addrs: &[SocketAddr],
1011    options: &ConnectOptions,
1012) -> Result<TcpConnection, NetError> {
1013    let ordered = interleaved(addrs);
1014    let start = Instant::now();
1015    let deadline = start + options.timeout;
1016    let mut racing: Vec<(std::net::TcpStream, SocketAddr, Instant)> = Vec::new();
1017    let mut queue = ordered.iter().copied();
1018    let mut waiting = queue.next();
1019    let mut next_at = start;
1020    let mut last_start: Option<Instant> = None;
1021    let mut last = NetError::NoAddresses;
1022    loop {
1023        let now = Instant::now();
1024        if now >= deadline {
1025            break;
1026        }
1027        let spaced = last_start.map_or(now, |t| t + RACE_SPACING);
1028        if let Some(addr) = waiting.filter(|_| {
1029            racing.len() < MAX_RACING && now >= spaced && (now >= next_at || racing.is_empty())
1030        }) {
1031            waiting = queue.next();
1032            last_start = Some(now);
1033            next_at = now + options.attempt_delay;
1034            let stream = match dial_socket(addr, options) {
1035                Ok(s) => s,
1036                Err(e) => {
1037                    last = e;
1038                    next_at = now;
1039                    continue;
1040                }
1041            };
1042            match sys::connect_start(stream.sock(), addr) {
1043                Ok(false) => return dialed(stream, addr, options),
1044                Ok(true) => racing.push((stream, addr, now)),
1045                Err(e) => {
1046                    last = connect_error(addr, Duration::ZERO, e);
1047                    next_at = now;
1048                }
1049            }
1050            continue;
1051        }
1052        if racing.is_empty() && waiting.is_none() {
1053            break;
1054        }
1055        let mut until = deadline;
1056        if waiting.is_some() {
1057            until = until.min(next_at.max(spaced).max(now + Duration::from_millis(1)));
1058        }
1059        let wait = until.saturating_duration_since(now);
1060        if racing.is_empty() {
1061            std::thread::sleep(wait);
1062            continue;
1063        }
1064        let socks: Vec<_> = racing.iter().map(|(s, ..)| s.sock()).collect();
1065        let ready = sys::wait_writable(&socks, wait).map_err(|e| NetError::from_os("poll", e))?;
1066        drop(socks);
1067        for i in (0..racing.len()).rev() {
1068            if ready & (1 << i) == 0 {
1069                continue;
1070            }
1071            let (stream, addr, began) = racing.swap_remove(i);
1072            match sys::take_error(stream.sock()) {
1073                Ok(None) => {
1074                    for (loser, ..) in &racing {
1075                        let _ = sys::set_linger(loser.sock(), Some(Duration::ZERO));
1076                    }
1077                    return dialed(stream, addr, options);
1078                }
1079                Ok(Some(e)) => {
1080                    last = connect_error(addr, began.elapsed(), e);
1081                    next_at = Instant::now();
1082                }
1083                Err(e) => last = NetError::from_os("getsockopt", e),
1084            }
1085        }
1086    }
1087    for (loser, ..) in &racing {
1088        let _ = sys::set_linger(loser.sock(), Some(Duration::ZERO));
1089    }
1090    if matches!(last, NetError::NoAddresses) {
1091        if let Some(addr) = ordered.first() {
1092            last = NetError::ConnectTimeout {
1093                addr: *addr,
1094                after: options.timeout,
1095            };
1096        }
1097    }
1098    Err(last)
1099}
1100
1101fn connect_one(
1102    addr: SocketAddr,
1103    options: &ConnectOptions,
1104    attempt: Duration,
1105) -> Result<TcpConnection, NetError> {
1106    let stream = dial_socket(addr, options)?;
1107    let started = Instant::now();
1108    match sys::connect_start(stream.sock(), addr) {
1109        Ok(false) => {}
1110        Ok(true) => {
1111            match sys::wait(stream.sock(), Interest::Write, None, Some(attempt)) {
1112                Ok(Ready::TimedOut) => {
1113                    return Err(NetError::ConnectTimeout {
1114                        addr,
1115                        after: attempt,
1116                    });
1117                }
1118                Ok(_) => {}
1119                Err(e) => return Err(NetError::from_os("poll", e)),
1120            }
1121            match sys::take_error(stream.sock()) {
1122                Ok(None) => {}
1123                Ok(Some(e)) => return Err(connect_error(addr, started.elapsed(), e)),
1124                Err(e) => return Err(NetError::from_os("getsockopt", e)),
1125            }
1126        }
1127        Err(e) => return Err(connect_error(addr, started.elapsed(), e)),
1128    }
1129    dialed(stream, addr, options)
1130}
1131
1132/// A connection from a socket that just connected to `addr`.
1133fn dialed(
1134    stream: std::net::TcpStream,
1135    addr: SocketAddr,
1136    options: &ConnectOptions,
1137) -> Result<TcpConnection, NetError> {
1138    sys::prepare_dialed(&stream).map_err(|e| NetError::from_os("ioctl", e))?;
1139    let local = stream
1140        .local_addr()
1141        .map_err(|e| NetError::from_os("getsockname", e))?;
1142    let info = ConnectionInfo::new_tcp(
1143        crate::id::issue_single(),
1144        None,
1145        WorkerId(0),
1146        Origin::Dialed,
1147        local,
1148        crate::addr::normalize_socket(addr),
1149        Instant::now(),
1150    );
1151    Ok(TcpConnection::from_link(Link::new(
1152        sys::Stream::Tcp(stream),
1153        info,
1154        Arc::new(options.limits.effective(DriverKind::Blocking)),
1155        ClockRef::Monotonic,
1156        Arc::new(KillSlot::new()),
1157        WakeSource::Thread,
1158        Phase::Receiving,
1159    )))
1160}
1161
1162impl Read for TcpConnection {
1163    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1164        self.recv(buf).map_err(io::Error::from)
1165    }
1166}
1167
1168impl Write for TcpConnection {
1169    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1170        self.send(buf).map_err(io::Error::from)
1171    }
1172
1173    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
1174        self.send_vectored(bufs).map_err(io::Error::from)
1175    }
1176
1177    fn flush(&mut self) -> io::Result<()> {
1178        Ok(())
1179    }
1180}
1181
1182impl Drop for TcpConnection {
1183    /// Never blocks: FIN, then close without the linger; use `close()` for the full graceful close.
1184    fn drop(&mut self) {
1185        if let Some(link) = self.link.take() {
1186            let reason = link.reason_or(CloseReason::SessionClosed);
1187            link.finish_quick(reason);
1188        }
1189    }
1190}
1191
1192impl std::fmt::Debug for TcpConnection {
1193    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1194        match &self.link {
1195            Some(l) => f
1196                .debug_struct("TcpConnection")
1197                .field("info", &l.info)
1198                .finish(),
1199            None => f.write_str("TcpConnection(closed)"),
1200        }
1201    }
1202}
1203
1204#[cfg(unix)]
1205impl std::os::fd::AsFd for TcpConnection {
1206    fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
1207        std::os::fd::AsFd::as_fd(&self.link_ref().stream)
1208    }
1209}
1210
1211#[cfg(windows)]
1212impl std::os::windows::io::AsSocket for TcpConnection {
1213    fn as_socket(&self) -> std::os::windows::io::BorrowedSocket<'_> {
1214        std::os::windows::io::AsSocket::as_socket(&self.link_ref().stream)
1215    }
1216}
1217
1218#[cfg(test)]
1219mod tests {
1220    use super::*;
1221
1222    #[test]
1223    fn test_listen_accept_echo_round_trip() {
1224        let l = TcpListener::bind("127.0.0.1:0").unwrap();
1225        let addr = l.local_addr().unwrap();
1226        let t = std::thread::spawn(move || {
1227            let (mut c, info) = l.accept().unwrap();
1228            assert_eq!(info.origin, Origin::Accepted);
1229            let mut buf = [0u8; 4];
1230            c.recv_exact(&mut buf).unwrap();
1231            c.send_all(&buf).unwrap();
1232            c.close().unwrap();
1233        });
1234        let mut c = TcpConnection::connect(addr).unwrap();
1235        c.send_all(b"ping").unwrap();
1236        let mut buf = [0u8; 4];
1237        c.recv_exact(&mut buf).unwrap();
1238        assert_eq!(&buf, b"ping");
1239        assert_eq!(c.recv(&mut buf).unwrap(), 0);
1240        t.join().unwrap();
1241    }
1242
1243    #[test]
1244    fn test_connect_refused_maps_to_connection_refused() {
1245        // Bind then drop to get a free port with nothing listening.
1246        let port = std::net::TcpListener::bind("127.0.0.1:0")
1247            .unwrap()
1248            .local_addr()
1249            .unwrap()
1250            .port();
1251        let err = TcpConnection::connect_with(
1252            ("127.0.0.1", port),
1253            ConnectOptions::new().timeout(Duration::from_secs(5)),
1254        )
1255        .unwrap_err();
1256        assert!(
1257            matches!(
1258                err,
1259                NetError::ConnectionRefused { .. } | NetError::ConnectTimeout { .. }
1260            ),
1261            "{err:?}"
1262        );
1263    }
1264
1265    #[test]
1266    fn test_bind_in_use_maps_to_addr_in_use() {
1267        let a = TcpListener::bind("127.0.0.1:0").unwrap();
1268        let err = TcpListener::bind(a.local_addr().unwrap()).unwrap_err();
1269        assert!(matches!(err, NetError::AddrInUse { .. }), "{err:?}");
1270    }
1271
1272    /// Edge cases 127 and 128: bind errors map by class, whatever the OS's number.
1273    #[test]
1274    fn test_bind_errors_map_by_class() {
1275        #[cfg(unix)]
1276        use crate::sys::errno::{EACCES as ACCESS, EADDRNOTAVAIL as NOT_AVAILABLE};
1277        #[cfg(windows)]
1278        use crate::sys::errno::{WSAEACCES as ACCESS, WSAEADDRNOTAVAIL as NOT_AVAILABLE};
1279        let addr: SocketAddr = "[::1]:80".parse().unwrap();
1280        let e = bind_error(addr, io::Error::from_raw_os_error(NOT_AVAILABLE));
1281        assert!(matches!(e, NetError::AddrNotAvailable { .. }), "{e:?}");
1282        let e = bind_error(addr, io::Error::from_raw_os_error(ACCESS));
1283        assert!(matches!(e, NetError::PermissionDenied { .. }), "{e:?}");
1284    }
1285
1286    #[test]
1287    fn test_read_timeout_fires() {
1288        let l = TcpListener::bind("127.0.0.1:0").unwrap();
1289        let addr = l.local_addr().unwrap();
1290        let _server = std::thread::spawn(move || {
1291            let (c, _) = l.accept().unwrap();
1292            std::thread::sleep(Duration::from_secs(2));
1293            drop(c);
1294        });
1295        let limits = Limits::builder()
1296            .read_timeout(Duration::from_millis(200))
1297            .min_rate(crate::limits::MinRate::off())
1298            .build()
1299            .unwrap();
1300        let mut c =
1301            TcpConnection::connect_with(addr, ConnectOptions::new().limits(limits)).unwrap();
1302        let mut buf = [0u8; 8];
1303        let start = Instant::now();
1304        let err = c.recv(&mut buf).unwrap_err();
1305        assert!(
1306            matches!(
1307                err,
1308                NetError::TimedOut {
1309                    kind: TimeoutKind::Read
1310                }
1311            ),
1312            "{err:?}"
1313        );
1314        assert!(start.elapsed() < Duration::from_secs(2));
1315    }
1316
1317    #[test]
1318    fn test_kill_wakes_blocked_recv() {
1319        let l = TcpListener::bind("127.0.0.1:0").unwrap();
1320        let addr = l.local_addr().unwrap();
1321        let _server = std::thread::spawn(move || {
1322            let (c, _) = l.accept().unwrap();
1323            std::thread::sleep(Duration::from_secs(3));
1324            drop(c);
1325        });
1326        let mut c = TcpConnection::connect(addr).unwrap();
1327        let kill = c.kill_handle();
1328        std::thread::spawn(move || {
1329            std::thread::sleep(Duration::from_millis(100));
1330            assert!(kill.kill(crate::error::KillReason(4)));
1331        });
1332        let mut buf = [0u8; 8];
1333        let start = Instant::now();
1334        let err = c.recv(&mut buf).unwrap_err();
1335        assert!(matches!(err, NetError::Killed { .. }), "{err:?}");
1336        assert!(start.elapsed() < Duration::from_secs(2));
1337    }
1338
1339    #[test]
1340    fn test_interleaved_starts_with_v6_and_takes_turns() {
1341        let a = |t: &str| t.parse::<SocketAddr>().unwrap();
1342        let (v4a, v4b, v4c) = (a("1.1.1.1:1"), a("2.2.2.2:1"), a("3.3.3.3:1"));
1343        let (v6a, v6b) = (a("[::1]:1"), a("[::2]:1"));
1344        assert_eq!(
1345            interleaved(&[v4a, v4b, v4c, v6a, v6b]),
1346            [v6a, v4a, v6b, v4b, v4c]
1347        );
1348        assert_eq!(interleaved(&[v4a, v4b]), [v4a, v4b]);
1349        assert!(interleaved(&[]).is_empty());
1350    }
1351
1352    #[test]
1353    fn test_attempt_delay_is_held_to_its_range() {
1354        let low = ConnectOptions::new().attempt_delay(Duration::from_millis(1));
1355        assert_eq!(low.attempt_delay, Duration::from_millis(100));
1356        let high = ConnectOptions::new().attempt_delay(Duration::from_secs(9));
1357        assert_eq!(high.attempt_delay, Duration::from_secs(2));
1358    }
1359
1360    #[test]
1361    fn test_racing_a_refused_address_moves_on_at_once() {
1362        let closed = {
1363            let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
1364            l.local_addr().unwrap()
1365        };
1366        let live = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
1367        let options = ConnectOptions::new()
1368            .happy_eyeballs(true)
1369            .attempt_delay(Duration::from_secs(2));
1370        let started = Instant::now();
1371        let c = TcpConnection::connect_addrs(&[closed, live.local_addr().unwrap()], &options);
1372        assert_eq!(c.unwrap().peer_addr(), live.local_addr().unwrap());
1373        // Windows retries a refused connect for about 2 s before it reports it.
1374        if cfg!(unix) {
1375            assert!(
1376                started.elapsed() < Duration::from_secs(2),
1377                "no attempt delay"
1378            );
1379        }
1380    }
1381
1382    #[test]
1383    fn test_racing_every_address_refused_reports_the_last() {
1384        let closed: Vec<SocketAddr> = (0..2)
1385            .map(|_| {
1386                let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
1387                l.local_addr().unwrap()
1388            })
1389            .collect();
1390        let options = ConnectOptions::new()
1391            .timeout(Duration::from_secs(5))
1392            .happy_eyeballs(true);
1393        let e = TcpConnection::connect_addrs(&closed, &options).err();
1394        assert!(
1395            matches!(e, Some(NetError::ConnectionRefused { .. })),
1396            "{e:?}"
1397        );
1398    }
1399
1400    /// A silent address (documentation range: nothing answers it) cannot hold the
1401    /// connection up longer than the attempt delay.
1402    #[test]
1403    fn test_racing_a_silent_address_starts_the_next_after_the_delay() {
1404        let silent: SocketAddr = "192.0.2.1:9".parse().unwrap();
1405        let live = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
1406        let options = ConnectOptions::new()
1407            .timeout(Duration::from_secs(5))
1408            .happy_eyeballs(true)
1409            .attempt_delay(Duration::from_millis(100));
1410        let started = Instant::now();
1411        let c = TcpConnection::connect_addrs(&[silent, live.local_addr().unwrap()], &options);
1412        assert_eq!(c.unwrap().peer_addr(), live.local_addr().unwrap());
1413        assert!(started.elapsed() < Duration::from_secs(2));
1414    }
1415
1416    #[test]
1417    fn test_listen_options_that_an_os_lacks_are_refused() {
1418        let bind = |o: ListenOptions| TcpListener::bind_with("127.0.0.1:0", o);
1419        let defer = ListenOptions::new().defer_accept(Some(Duration::from_secs(1)));
1420        let cpu = ListenOptions::new().incoming_cpu(Some(0));
1421        if cfg!(target_os = "linux") {
1422            assert!(bind(defer).is_ok());
1423            assert!(bind(cpu).is_ok());
1424        } else {
1425            assert!(matches!(bind(defer), Err(NetError::Unsupported { .. })));
1426            assert!(matches!(bind(cpu), Err(NetError::Unsupported { .. })));
1427        }
1428    }
1429
1430    /// Fast open is off by default on some hosts, and off means refused or ignored,
1431    /// never a broken listener.
1432    #[test]
1433    fn test_fast_open_listener_still_accepts() {
1434        let options = ListenOptions::new().fast_open(Some(16));
1435        if let Ok(l) = TcpListener::bind_with("127.0.0.1:0", options) {
1436            let addr = l.local_addr().unwrap();
1437            let _c = TcpConnection::connect(addr).unwrap();
1438            assert!(l.accept().is_ok());
1439        }
1440    }
1441
1442    #[test]
1443    fn test_user_timeout_where_the_os_has_it() {
1444        let l = TcpListener::bind("127.0.0.1:0").unwrap();
1445        let options = ConnectOptions::new()
1446            .tcp_options(TcpOptions::new().user_timeout(Duration::from_secs(5)));
1447        let c = TcpConnection::connect_with(l.local_addr().unwrap(), options);
1448        if cfg!(any(target_os = "linux", windows)) {
1449            assert!(c.is_ok());
1450        } else {
1451            assert!(matches!(c, Err(NetError::Unsupported { .. })));
1452        }
1453    }
1454}