Skip to main content

nuclei/net/
close.rs

1//! A stream's ends: [`shut`](crate::net::TcpStream::shut), std's shutdown made once the bytes
2//! written behind reached the kernel, and [`abort`](crate::net::TcpStream::abort), a TCP
3//! stream's close that resets the connection now (`proofs/tla/close`).
4//!
5//! The shut is not named `shutdown`: std's own, which a handle reaches through `Deref`, keeps
6//! that name and its meaning, and an inherent method of that name would take its place in every
7//! caller's `stream.shutdown(how)`, a future where a result was.
8
9use core::future::poll_fn;
10use std::io;
11use std::net::{Shutdown, TcpStream};
12#[cfg(unix)]
13use std::os::unix::net::UnixStream;
14use std::sync::Arc;
15use std::time::Duration;
16
17use crate::io::source::Source;
18use crate::io::{Handle, IoObject, sys};
19
20/// Whether the kernel drops what its receive queue holds as it shuts the reading half (the
21/// BSDs, macOS among them, and Windows), so the handle drops what it read ahead too. Linux keeps
22/// the queue readable, and the read-ahead with it.
23const KERNEL_DROPS_QUEUE: bool = !cfg!(any(target_os = "linux", target_os = "android"));
24
25macro_rules! shut {
26    ($($ty:ty),* $(,)?) => {$(
27        impl Handle<$ty> {
28            /// Shuts down the reading half of the stream, its writing half, or both, as std's
29            /// `shutdown` does, once the bytes written behind reached the kernel.
30            ///
31            /// `Write` and `Both` wait until the kernel took every byte the handle accepted, so
32            /// the peer reads all of them before the end of the stream, then ask the kernel; a
33            /// peer that never reads holds them up, as it would hold up std's blocking writes.
34            /// `Read` leaves the writes alone. After `Read` or `Both`, reads answer as std's do:
35            /// Linux hands out what came before the shutdown, then the end; macOS and Windows end
36            /// at once, so there the handle drops what it read ahead.
37            ///
38            /// It is not named `shutdown`, so that `stream.shutdown(how)` still calls std's own,
39            /// reached through `Deref`, which shuts the halves at once: the bytes still behind
40            /// then never reach the peer, and it reads the end before them.
41            ///
42            /// # Errors
43            ///
44            /// The error that stopped the bytes written behind, and what `shutdown(2)` answers:
45            /// a half shut already on macOS (`NotConnected`), as std's.
46            pub async fn shut(&self, how: Shutdown) -> io::Result<()> {
47                shut(self.source(), how).await
48            }
49        }
50    )*};
51}
52
53shut!(TcpStream);
54#[cfg(unix)]
55shut!(UnixStream);
56
57/// [`Handle::shut`] of the stream whose source is `source`.
58async fn shut(source: &Arc<Source>, how: Shutdown) -> io::Result<()> {
59    match how {
60        Shutdown::Read => shut_read(source).await?,
61        Shutdown::Write | Shutdown::Both => {
62            poll_fn(|cx| source.poll_shutdown(cx, how)).await?;
63        }
64    }
65    if how != Shutdown::Write && KERNEL_DROPS_QUEUE {
66        poll_fn(|cx| source.poll_drop_ahead(cx)).await?;
67    }
68    Ok(())
69}
70
71/// The kernel's shutdown of the reading half: the ring's entry on a thread whose shard runs a
72/// ring, else the call itself. The lease keeps the descriptor's number the stream's until the
73/// call ran.
74async fn shut_read(source: &Arc<Source>) -> io::Result<()> {
75    let lease = source.lease()?;
76    #[cfg(ring)]
77    if let Ok(mut job) = crate::fs::ring::shutdown(&lease, libc::SHUT_RD) {
78        return poll_fn(|cx| job.poll(cx.waker()))
79            .await
80            .inspect_err(crate::rt::counters::io_error);
81    }
82    sys::shutdown(lease.raw(), Shutdown::Read).inspect_err(crate::rt::counters::io_error)
83}
84
85impl Handle<TcpStream> {
86    /// Closes the stream now and resets the connection: `SO_LINGER` goes to zero, the bytes
87    /// written behind and the buffers lent to writes still out are dropped, nothing lingers, and
88    /// the close sends the peer a reset, so its reads fail with `ConnectionReset` rather than end
89    /// as if every byte came. The kernel drops what its own queue holds too.
90    ///
91    /// A plain drop instead delivers the bytes behind, for at most the runtime's
92    /// [`write_behind_linger`](crate::runtime::Builder::write_behind_linger), and ends the
93    /// stream cleanly; one whose time runs out resets the connection the same way.
94    ///
95    /// # Errors
96    ///
97    /// When `SO_LINGER` cannot be set: the stream then closes as a drop closes it.
98    pub fn abort(self) -> io::Result<()> {
99        super::opt::set_linger_time(self.raw_io(), Some(Duration::ZERO))?;
100        self.close_now();
101        Ok(())
102    }
103}