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, TimeoutKind};
11use crate::gate::KillHandle;
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.
48fn 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: "SO_REUSEPORT",
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    pub(crate) fn tcp_options(&self) -> &TcpOptions {
273        &self.tcp
274    }
275
276    /// Facts about this listener.
277    #[must_use]
278    pub fn info(&self) -> &ListenerInfo {
279        &self.info
280    }
281
282    /// The bound address.
283    ///
284    /// # Errors
285    ///
286    /// `Os` when `getsockname` fails.
287    pub fn local_addr(&self) -> Result<SocketAddr, NetError> {
288        self.inner
289            .local_addr()
290            .map_err(|e| NetError::from_os("getsockname", e))
291    }
292
293    /// Adopts an existing listening socket (checked with `SO_ACCEPTCONN` and `SO_TYPE`).
294    ///
295    /// # Errors
296    ///
297    /// `InvalidConfig { field: "socket", reason: NotListening }`.
298    ///
299    /// ```
300    /// let std_listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
301    /// let listener = net_io::TcpListener::from_std(std_listener)?;
302    /// assert!(listener.local_addr().is_ok());
303    /// # Ok::<(), net_io::NetError>(())
304    /// ```
305    pub fn from_std(listener: std::net::TcpListener) -> Result<TcpListener, NetError> {
306        match sys::is_listening_stream(listener.sock()) {
307            Ok(true) => {}
308            Ok(false) => {
309                return Err(NetError::InvalidConfig {
310                    field: "socket",
311                    reason: ConfigReason::NotListening,
312                });
313            }
314            Err(e) => return Err(NetError::from_os("getsockopt", e)),
315        }
316        listener
317            .set_nonblocking(true)
318            .map_err(|e| NetError::from_os("ioctl", e))?;
319        TcpListener::finish(
320            listener,
321            ListenOptions::new().tcp_options(TcpOptions::new()),
322            ListenerId(0),
323            None,
324        )
325        .map(|mut l| {
326            l.info.backlog = None;
327            l
328        })
329    }
330
331    /// Gives the socket up (it stays non-blocking).
332    #[must_use]
333    pub fn into_std(self) -> std::net::TcpListener {
334        self.inner
335    }
336
337    /// A second handle to the same listening socket.
338    ///
339    /// # Errors
340    ///
341    /// `Os` when duplication fails.
342    pub fn try_clone(&self) -> Result<TcpListener, NetError> {
343        let inner = self
344            .inner
345            .try_clone()
346            .map_err(|e| NetError::from_os("dup", e))?;
347        Ok(TcpListener {
348            inner,
349            info: self.info.clone(),
350            tcp: self.tcp,
351            acceptor: Mutex::new(Acceptor::new()),
352        })
353    }
354}
355
356#[cfg(unix)]
357impl std::os::fd::AsFd for TcpListener {
358    fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
359        self.inner.as_fd()
360    }
361}
362
363#[cfg(unix)]
364impl From<TcpListener> for std::os::fd::OwnedFd {
365    fn from(l: TcpListener) -> std::os::fd::OwnedFd {
366        l.inner.into()
367    }
368}
369
370#[cfg(unix)]
371impl TryFrom<std::os::fd::OwnedFd> for TcpListener {
372    type Error = NetError;
373    fn try_from(fd: std::os::fd::OwnedFd) -> Result<TcpListener, NetError> {
374        TcpListener::from_std(std::net::TcpListener::from(fd))
375    }
376}
377
378#[cfg(windows)]
379impl std::os::windows::io::AsSocket for TcpListener {
380    fn as_socket(&self) -> std::os::windows::io::BorrowedSocket<'_> {
381        self.inner.as_socket()
382    }
383}
384
385#[cfg(windows)]
386impl From<TcpListener> for std::os::windows::io::OwnedSocket {
387    fn from(l: TcpListener) -> std::os::windows::io::OwnedSocket {
388        l.inner.into()
389    }
390}
391
392#[cfg(windows)]
393impl TryFrom<std::os::windows::io::OwnedSocket> for TcpListener {
394    type Error = NetError;
395    fn try_from(s: std::os::windows::io::OwnedSocket) -> Result<TcpListener, NetError> {
396        TcpListener::from_std(std::net::TcpListener::from(s))
397    }
398}
399
400/// Options for an outbound connection.
401///
402/// ```no_run
403/// use net_io::{ConnectOptions, TcpConnection};
404/// use std::time::Duration;
405/// let options = ConnectOptions::new().timeout(Duration::from_secs(3));
406/// let conn = TcpConnection::connect_host("example.com", 443, options)?;
407/// println!("connected to {}", conn.peer_addr());
408/// # Ok::<(), net_io::NetError>(())
409/// ```
410#[must_use]
411#[derive(Clone, Debug)]
412pub struct ConnectOptions {
413    timeout: Duration,
414    local_addr: Option<SocketAddr>,
415    tcp: TcpOptions,
416    limits: Limits,
417}
418
419impl Default for ConnectOptions {
420    fn default() -> ConnectOptions {
421        ConnectOptions {
422            timeout: Duration::from_secs(10),
423            local_addr: None,
424            tcp: TcpOptions::new().nodelay(true),
425            limits: Limits::default(),
426        }
427    }
428}
429
430impl ConnectOptions {
431    /// Timeout 10 s, `TCP_NODELAY` on, no local bind.
432    pub fn new() -> ConnectOptions {
433        ConnectOptions::default()
434    }
435
436    /// The total time for all attempts.
437    pub fn timeout(mut self, total: Duration) -> ConnectOptions {
438        self.timeout = total;
439        self
440    }
441
442    /// Binds to this address before connecting.
443    pub fn local_addr(mut self, addr: SocketAddr) -> ConnectOptions {
444        self.local_addr = Some(addr);
445        self
446    }
447
448    /// Options applied before the connect.
449    pub fn tcp_options(mut self, options: TcpOptions) -> ConnectOptions {
450        self.tcp = options;
451        self
452    }
453
454    /// Limits for the connection.
455    pub fn limits(mut self, limits: Limits) -> ConnectOptions {
456        self.limits = limits;
457        self
458    }
459}
460
461/// A connected TCP socket that carries its own limits.
462///
463/// Every read honours the current phase's deadline (a connection from `accept`
464/// starts `Idle` under the idle timeout; one from `connect` starts `Receiving`
465/// under the read timeout), the exchange deadline, and the inbound minimum
466/// rate; every write honours the write deadline and the outbound minimum rate.
467/// One owner: other threads act on it only through its [`KillHandle`].
468///
469/// # Examples
470///
471/// ```no_run
472/// use net_io::{Limits, Phase};
473/// use std::time::{Duration, Instant};
474///
475/// let mut conn = net_io::connect("127.0.0.1:7000")?;
476/// conn.set_limits(Limits::builder().read_timeout(Duration::from_secs(5)).build()?);
477/// conn.set_phase(Phase::Receiving);
478/// conn.set_exchange_deadline(Some(Instant::now() + Duration::from_secs(20)));
479/// let mut buf = [0u8; 1024];
480/// let n = conn.recv(&mut buf)?;
481/// # let _ = n;
482/// # Ok::<(), net_io::NetError>(())
483/// ```
484pub struct TcpConnection {
485    link: Option<Link>,
486}
487
488impl TcpConnection {
489    /// Connects with a 10 s timeout across every resolved address (see [`crate::connect`]).
490    ///
491    /// # Errors
492    ///
493    /// `ConnectTimeout`, `ConnectionRefused`, `HostUnreachable`, `NetworkUnreachable`, `NoAddresses`.
494    pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<TcpConnection, NetError> {
495        TcpConnection::connect_with(addr, ConnectOptions::new())
496    }
497
498    /// Connects with `options`.
499    ///
500    /// # Errors
501    ///
502    /// As [`TcpConnection::connect`].
503    pub fn connect_with<A: ToSocketAddrs>(
504        addr: A,
505        options: ConnectOptions,
506    ) -> Result<TcpConnection, NetError> {
507        let addrs = resolve(addr)?;
508        TcpConnection::connect_addrs(&addrs, &options)
509    }
510
511    /// Resolves `host` with the standard library and connects to the addresses in
512    /// order; each attempt gets the remaining time divided by the remaining
513    /// addresses (at least 1 s).
514    ///
515    /// # Errors
516    ///
517    /// `NoAddresses`, or the last connect error.
518    pub fn connect_host(
519        host: &str,
520        port: u16,
521        options: ConnectOptions,
522    ) -> Result<TcpConnection, NetError> {
523        let addrs = resolve((host, port))?;
524        TcpConnection::connect_addrs(&addrs, &options)
525    }
526
527    fn connect_addrs(
528        addrs: &[SocketAddr],
529        options: &ConnectOptions,
530    ) -> Result<TcpConnection, NetError> {
531        let start = Instant::now();
532        let deadline = start + options.timeout;
533        let mut last = NetError::NoAddresses;
534        for (i, addr) in addrs.iter().enumerate() {
535            let left = deadline.saturating_duration_since(Instant::now());
536            if left.is_zero() {
537                break;
538            }
539            let remaining = (addrs.len() - i) as u32;
540            let attempt = (left / remaining).max(Duration::from_secs(1)).min(left);
541            match connect_one(*addr, options, attempt) {
542                Ok(conn) => return Ok(conn),
543                Err(e) => last = e,
544            }
545        }
546        if matches!(last, NetError::NoAddresses) {
547            if let Some(addr) = addrs.first() {
548                last = NetError::ConnectTimeout {
549                    addr: *addr,
550                    after: options.timeout,
551                };
552            }
553        }
554        Err(last)
555    }
556
557    pub(crate) fn accepted(
558        stream: std::net::TcpStream,
559        peer: SocketAddr,
560        tcp: &TcpOptions,
561        listener: ListenerId,
562    ) -> Result<TcpConnection, NetError> {
563        let local = stream
564            .local_addr()
565            .map_err(|e| NetError::from_os("getsockname", e))?;
566        if !OPTIONS_INHERITED && !tcp.is_empty() {
567            tcp.apply(&stream, local.is_ipv6())?;
568        }
569        let info = ConnectionInfo::new_tcp(
570            crate::id::issue_single(),
571            Some(listener),
572            WorkerId(0),
573            Origin::Accepted,
574            local,
575            peer,
576            Instant::now(),
577        );
578        let limits = Limits::default().effective(DriverKind::Blocking);
579        Ok(TcpConnection::from_link(Link::new(
580            stream,
581            info,
582            limits,
583            ClockRef::Monotonic,
584            Arc::new(KillSlot::new()),
585            WakeSource::Thread,
586            Phase::Idle,
587        )))
588    }
589
590    pub(crate) fn from_link(link: Link) -> TcpConnection {
591        TcpConnection { link: Some(link) }
592    }
593
594    pub(crate) fn take_link(&mut self) -> Option<Link> {
595        self.link.take()
596    }
597
598    fn link(&mut self) -> Result<&mut Link, NetError> {
599        self.link.as_mut().ok_or(NetError::Closed)
600    }
601
602    fn link_ref(&self) -> &Link {
603        match &self.link {
604            Some(l) => l,
605            // Only `take_link` (crate-internal, at the end of a handler) empties it.
606            None => unreachable_link(),
607        }
608    }
609
610    /// One receive; `Ok(0)` means the peer closed its side. A zero-length `buf`
611    /// returns `Ok(0)` without a system call (check `buf.len()`).
612    ///
613    /// # Errors
614    ///
615    /// `TimedOut { kind }`, `SlowPeer { In }`, `ConnectionReset`, `Killed`, `Closed`.
616    pub fn recv(&mut self, buf: &mut [u8]) -> Result<usize, NetError> {
617        let link = self.link()?;
618        loop {
619            match link.read(buf)? {
620                Got::Data(n) => return Ok(n),
621                Got::Eof => return Ok(0),
622                Got::Woken => continue,
623            }
624        }
625    }
626
627    /// Receives until `buf` is full.
628    ///
629    /// # Errors
630    ///
631    /// As [`TcpConnection::recv`]; `Closed` when the peer closes first.
632    pub fn recv_exact(&mut self, buf: &mut [u8]) -> Result<(), NetError> {
633        let mut filled = 0;
634        while filled < buf.len() {
635            match self.recv(&mut buf[filled..])? {
636                0 => return Err(NetError::Closed),
637                n => filled += n,
638            }
639        }
640        Ok(())
641    }
642
643    /// One receive into the first non-empty slice.
644    ///
645    /// # Errors
646    ///
647    /// As [`TcpConnection::recv`].
648    pub fn recv_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize, NetError> {
649        match bufs.iter_mut().find(|b| !b.is_empty()) {
650            Some(first) => self.recv(first),
651            None => Ok(0),
652        }
653    }
654
655    /// Reads without consuming (`MSG_PEEK`), within the read timeout.
656    ///
657    /// # Errors
658    ///
659    /// As [`TcpConnection::recv`].
660    pub fn peek(&self, buf: &mut [u8]) -> Result<usize, NetError> {
661        let link = self.link_ref();
662        if buf.is_empty() {
663            return Ok(0);
664        }
665        let timeout = link.limits.read_timeout;
666        match sys::wait(link.stream.sock(), Interest::Read, None, Some(timeout)) {
667            Ok(Ready::TimedOut) => {
668                return Err(NetError::TimedOut {
669                    kind: TimeoutKind::Read,
670                });
671            }
672            Ok(_) => {}
673            Err(e) => return Err(NetError::from_os("poll", e)),
674        }
675        link.stream
676            .peek(buf)
677            .map_err(|e| NetError::from_os("recv", e))
678    }
679
680    /// One send after the gate check; may be partial.
681    ///
682    /// # Errors
683    ///
684    /// `TimedOut`, `SlowPeer { Out }`, `BrokenPipe`, `ConnectionReset`, `EgressDenied`, `Killed`.
685    pub fn send(&mut self, buf: &[u8]) -> Result<usize, NetError> {
686        self.link()?.write(buf)
687    }
688
689    /// Sends all of `buf`, continuing from the exact offset; the gate check runs
690    /// before every system call.
691    ///
692    /// # Errors
693    ///
694    /// As [`TcpConnection::send`].
695    pub fn send_all(&mut self, buf: &[u8]) -> Result<(), NetError> {
696        self.link()?.write_all(buf)
697    }
698
699    /// One vectored send (up to 64 slices) after the gate check; may be partial.
700    ///
701    /// # Errors
702    ///
703    /// As [`TcpConnection::send`].
704    pub fn send_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize, NetError> {
705        self.link()?.write_vectored(bufs)
706    }
707
708    /// Sends every slice, advancing them in place (no copy).
709    ///
710    /// # Errors
711    ///
712    /// As [`TcpConnection::send`].
713    pub fn send_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<(), NetError> {
714        self.link()?.write_all_vectored(bufs)
715    }
716
717    /// Half-close: `Write` sends FIN and keeps reading; `Read` stops reading.
718    ///
719    /// # Errors
720    ///
721    /// `Closed`, `Os`.
722    pub fn shutdown(&mut self, how: Shutdown) -> Result<(), NetError> {
723        self.link()?.shutdown(how)
724    }
725
726    /// Graceful close: FIN, then reads and discards up to 64 KiB for up to 2 s so
727    /// unread input does not cause an RST that destroys the last response.
728    ///
729    /// # Errors
730    ///
731    /// None in this version; the signature leaves room for a flush error.
732    pub fn close(mut self) -> Result<(), NetError> {
733        if let Some(link) = self.link.take() {
734            let reason = link.reason_or(CloseReason::SessionClosed);
735            link.finish(reason);
736        }
737        Ok(())
738    }
739
740    /// Abortive close: RST, kernel resources freed at once.
741    pub fn abort(mut self) {
742        if let Some(link) = self.link.take() {
743            let reason = link.reason_or(CloseReason::SessionError);
744            let reason = if reason.is_abortive() {
745                reason
746            } else {
747                CloseReason::SessionError
748            };
749            link.finish(reason);
750        }
751    }
752
753    /// What net-io knows about this connection.
754    #[must_use]
755    pub fn info(&self) -> &ConnectionInfo {
756        &self.link_ref().info
757    }
758
759    /// The connection's id.
760    #[must_use]
761    pub fn id(&self) -> ConnectionId {
762        self.link_ref().info.id
763    }
764
765    /// The socket peer.
766    #[must_use]
767    pub fn peer_addr(&self) -> SocketAddr {
768        self.link_ref()
769            .info
770            .peer
771            .socket_addr()
772            .unwrap_or_else(|| SocketAddr::from(([0, 0, 0, 0], 0)))
773    }
774
775    /// Our end.
776    #[must_use]
777    pub fn local_addr(&self) -> SocketAddr {
778        self.link_ref()
779            .info
780            .local
781            .socket_addr()
782            .unwrap_or_else(|| SocketAddr::from(([0, 0, 0, 0], 0)))
783    }
784
785    /// A handle other threads can use to abort this connection.
786    #[must_use]
787    pub fn kill_handle(&self) -> KillHandle {
788        let link = self.link_ref();
789        KillHandle {
790            slot: link.slot.clone(),
791            generation: link.generation,
792            id: link.info.id,
793        }
794    }
795
796    /// Replaces the limits from the next operation on (unset values: blocking-driver defaults).
797    pub fn set_limits(&mut self, limits: Limits) {
798        if let Ok(link) = self.link() {
799            let e: Effective = limits.effective(DriverKind::Blocking);
800            link.set_limits(e);
801        }
802    }
803
804    /// Chooses which deadlines apply.
805    pub fn set_phase(&mut self, phase: Phase) {
806        if let Ok(link) = self.link() {
807            link.set_phase(phase);
808        }
809    }
810
811    /// The deadline for the current exchange (`None` clears it).
812    pub fn set_exchange_deadline(&mut self, at: Option<Instant>) {
813        if let Ok(link) = self.link() {
814            link.set_exchange_deadline(at);
815        }
816    }
817
818    /// Live socket options.
819    #[must_use]
820    pub fn options(&self) -> SocketOptions<'_> {
821        let link = self.link_ref();
822        SocketOptions {
823            stream: &link.stream,
824            v6: link.info.local.socket_addr().is_some_and(|a| a.is_ipv6()),
825        }
826    }
827
828    /// The socket's pending error (`SO_ERROR`).
829    ///
830    /// # Errors
831    ///
832    /// `Os` when the OS call fails.
833    pub fn take_error(&self) -> Result<Option<io::Error>, NetError> {
834        sys::take_error(self.link_ref().stream.sock())
835            .map_err(|e| NetError::from_os("getsockopt", e))
836    }
837}
838
839#[cold]
840fn unreachable_link() -> ! {
841    // A TcpConnection is emptied only by the server after its handler returned,
842    // when no code can reach it any more.
843    std::process::abort()
844}
845
846fn connect_one(
847    addr: SocketAddr,
848    options: &ConnectOptions,
849    attempt: Duration,
850) -> Result<TcpConnection, NetError> {
851    let owned = sys::tcp_socket(addr.is_ipv6()).map_err(|e| NetError::from_os("socket", e))?;
852    let stream = std::net::TcpStream::from(owned);
853    stream
854        .set_nonblocking(true)
855        .map_err(|e| NetError::from_os("ioctl", e))?;
856    options.tcp.apply(&stream, addr.is_ipv6())?;
857    if let Some(local) = options.local_addr {
858        sys::bind_to(stream.sock(), local).map_err(|e| bind_error(local, e))?;
859    }
860    let started = Instant::now();
861    match sys::connect_start(stream.sock(), addr) {
862        Ok(false) => {}
863        Ok(true) => {
864            match sys::wait(stream.sock(), Interest::Write, None, Some(attempt)) {
865                Ok(Ready::TimedOut) => {
866                    return Err(NetError::ConnectTimeout {
867                        addr,
868                        after: attempt,
869                    });
870                }
871                Ok(_) => {}
872                Err(e) => return Err(NetError::from_os("poll", e)),
873            }
874            match sys::take_error(stream.sock()) {
875                Ok(None) => {}
876                Ok(Some(e)) => return Err(connect_error(addr, started.elapsed(), e)),
877                Err(e) => return Err(NetError::from_os("getsockopt", e)),
878            }
879        }
880        Err(e) => return Err(connect_error(addr, started.elapsed(), e)),
881    }
882    sys::prepare_dialed(&stream).map_err(|e| NetError::from_os("ioctl", e))?;
883    let local = stream
884        .local_addr()
885        .map_err(|e| NetError::from_os("getsockname", e))?;
886    let info = ConnectionInfo::new_tcp(
887        crate::id::issue_single(),
888        None,
889        WorkerId(0),
890        Origin::Dialed,
891        local,
892        crate::addr::normalize_socket(addr),
893        Instant::now(),
894    );
895    Ok(TcpConnection::from_link(Link::new(
896        stream,
897        info,
898        options.limits.effective(DriverKind::Blocking),
899        ClockRef::Monotonic,
900        Arc::new(KillSlot::new()),
901        WakeSource::Thread,
902        Phase::Receiving,
903    )))
904}
905
906impl Read for TcpConnection {
907    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
908        self.recv(buf).map_err(io::Error::from)
909    }
910}
911
912impl Write for TcpConnection {
913    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
914        self.send(buf).map_err(io::Error::from)
915    }
916
917    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
918        self.send_vectored(bufs).map_err(io::Error::from)
919    }
920
921    fn flush(&mut self) -> io::Result<()> {
922        Ok(())
923    }
924}
925
926impl Drop for TcpConnection {
927    /// Never blocks: FIN, then close without the linger; use `close()` for the full graceful close.
928    fn drop(&mut self) {
929        if let Some(link) = self.link.take() {
930            let reason = link.reason_or(CloseReason::SessionClosed);
931            link.finish_quick(reason);
932        }
933    }
934}
935
936impl std::fmt::Debug for TcpConnection {
937    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
938        match &self.link {
939            Some(l) => f
940                .debug_struct("TcpConnection")
941                .field("info", &l.info)
942                .finish(),
943            None => f.write_str("TcpConnection(closed)"),
944        }
945    }
946}
947
948#[cfg(unix)]
949impl std::os::fd::AsFd for TcpConnection {
950    fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
951        std::os::fd::AsFd::as_fd(&self.link_ref().stream)
952    }
953}
954
955#[cfg(windows)]
956impl std::os::windows::io::AsSocket for TcpConnection {
957    fn as_socket(&self) -> std::os::windows::io::BorrowedSocket<'_> {
958        std::os::windows::io::AsSocket::as_socket(&self.link_ref().stream)
959    }
960}
961
962#[cfg(test)]
963mod tests {
964    use super::*;
965
966    #[test]
967    fn test_listen_accept_echo_round_trip() {
968        let l = TcpListener::bind("127.0.0.1:0").unwrap();
969        let addr = l.local_addr().unwrap();
970        let t = std::thread::spawn(move || {
971            let (mut c, info) = l.accept().unwrap();
972            assert_eq!(info.origin, Origin::Accepted);
973            let mut buf = [0u8; 4];
974            c.recv_exact(&mut buf).unwrap();
975            c.send_all(&buf).unwrap();
976            c.close().unwrap();
977        });
978        let mut c = TcpConnection::connect(addr).unwrap();
979        c.send_all(b"ping").unwrap();
980        let mut buf = [0u8; 4];
981        c.recv_exact(&mut buf).unwrap();
982        assert_eq!(&buf, b"ping");
983        assert_eq!(c.recv(&mut buf).unwrap(), 0);
984        t.join().unwrap();
985    }
986
987    #[test]
988    fn test_connect_refused_maps_to_connection_refused() {
989        // Bind then drop to get a free port with nothing listening.
990        let port = std::net::TcpListener::bind("127.0.0.1:0")
991            .unwrap()
992            .local_addr()
993            .unwrap()
994            .port();
995        let err = TcpConnection::connect_with(
996            ("127.0.0.1", port),
997            ConnectOptions::new().timeout(Duration::from_secs(5)),
998        )
999        .unwrap_err();
1000        assert!(
1001            matches!(
1002                err,
1003                NetError::ConnectionRefused { .. } | NetError::ConnectTimeout { .. }
1004            ),
1005            "{err:?}"
1006        );
1007    }
1008
1009    #[test]
1010    fn test_bind_in_use_maps_to_addr_in_use() {
1011        let a = TcpListener::bind("127.0.0.1:0").unwrap();
1012        let err = TcpListener::bind(a.local_addr().unwrap()).unwrap_err();
1013        assert!(matches!(err, NetError::AddrInUse { .. }), "{err:?}");
1014    }
1015
1016    /// Edge cases 127 and 128: bind errors map by class, whatever the OS's number.
1017    #[test]
1018    fn test_bind_errors_map_by_class() {
1019        #[cfg(unix)]
1020        use crate::sys::errno::{EACCES as ACCESS, EADDRNOTAVAIL as NOT_AVAILABLE};
1021        #[cfg(windows)]
1022        use crate::sys::errno::{WSAEACCES as ACCESS, WSAEADDRNOTAVAIL as NOT_AVAILABLE};
1023        let addr: SocketAddr = "[::1]:80".parse().unwrap();
1024        let e = bind_error(addr, io::Error::from_raw_os_error(NOT_AVAILABLE));
1025        assert!(matches!(e, NetError::AddrNotAvailable { .. }), "{e:?}");
1026        let e = bind_error(addr, io::Error::from_raw_os_error(ACCESS));
1027        assert!(matches!(e, NetError::PermissionDenied { .. }), "{e:?}");
1028    }
1029
1030    #[test]
1031    fn test_read_timeout_fires() {
1032        let l = TcpListener::bind("127.0.0.1:0").unwrap();
1033        let addr = l.local_addr().unwrap();
1034        let _server = std::thread::spawn(move || {
1035            let (c, _) = l.accept().unwrap();
1036            std::thread::sleep(Duration::from_secs(2));
1037            drop(c);
1038        });
1039        let limits = Limits::builder()
1040            .read_timeout(Duration::from_millis(200))
1041            .min_rate(crate::limits::MinRate::off())
1042            .build()
1043            .unwrap();
1044        let mut c =
1045            TcpConnection::connect_with(addr, ConnectOptions::new().limits(limits)).unwrap();
1046        let mut buf = [0u8; 8];
1047        let start = Instant::now();
1048        let err = c.recv(&mut buf).unwrap_err();
1049        assert!(
1050            matches!(
1051                err,
1052                NetError::TimedOut {
1053                    kind: TimeoutKind::Read
1054                }
1055            ),
1056            "{err:?}"
1057        );
1058        assert!(start.elapsed() < Duration::from_secs(2));
1059    }
1060
1061    #[test]
1062    fn test_kill_wakes_blocked_recv() {
1063        let l = TcpListener::bind("127.0.0.1:0").unwrap();
1064        let addr = l.local_addr().unwrap();
1065        let _server = std::thread::spawn(move || {
1066            let (c, _) = l.accept().unwrap();
1067            std::thread::sleep(Duration::from_secs(3));
1068            drop(c);
1069        });
1070        let mut c = TcpConnection::connect(addr).unwrap();
1071        let kill = c.kill_handle();
1072        std::thread::spawn(move || {
1073            std::thread::sleep(Duration::from_millis(100));
1074            assert!(kill.kill(crate::error::KillReason(4)));
1075        });
1076        let mut buf = [0u8; 8];
1077        let start = Instant::now();
1078        let err = c.recv(&mut buf).unwrap_err();
1079        assert!(matches!(err, NetError::Killed { .. }), "{err:?}");
1080        assert!(start.elapsed() < Duration::from_secs(2));
1081    }
1082}