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}