1
//! Support for defining behavior when encountering an EOF during
2
//! a bidirectional copy.
3
//!
4
//! When performing a bidirectional copy
5
//! with [`copy_bidirectional`](crate::copy_bidirectional)
6
//! or [`copy_buf_bidirectional`](crate::copy_buf_bidirectional),
7
//! it's sometimes the case that one side is done transmitting before the other is.
8
//! In this case, the caller needs to specify what will happen upon receiving an EOF
9
//! from each of the two streams.
10
//!
11
//! Typically, the caller wants to _propagate_ the EOF from the stream that has
12
//! given it to the other stream, by calling an operation like
13
//! [shutdown](std::net::TcpStream::shutdown) or
14
//! [close](futures::io::AsyncWriteExt::close).
15
//! But since multiple operations can be appropriate depending on the circumstances,
16
//! we define a [`EofStrategy`] trait that tells the bidirectional `copy` function
17
//! how to react.
18

            
19
use std::{
20
    io,
21
    pin::Pin,
22
    task::{Context, Poll},
23
};
24

            
25
use futures::{AsyncWrite, io::BufReader};
26
use pin_project::pin_project;
27

            
28
#[cfg(unix)]
29
use std::os::fd::{AsFd, AsRawFd};
30

            
31
#[cfg(windows)]
32
use std::os::windows::io::{AsRawSocket, AsSocket};
33

            
34
/// Propagate an EOF during a bidirectional copy.
35
///
36
/// Each `EofStrategy<W>` implementation responds to an EOF on a reader by
37
/// doing "something" to a writer of type `W`.
38
/// It might do this by close the `W`,
39
/// by doing nothing at all,
40
/// or by invoking some more type-specific operation.
41
pub trait EofStrategy<W> {
42
    /// Try to transmit an EOF to `w`.
43
    ///
44
    /// On success, return Ok.  On failure, return an error.
45
    /// If we must try again, register this task with `cx`,
46
    /// and return [`Poll::Pending`].
47
    fn poll_send_eof(
48
        self: Pin<&mut Self>,
49
        cx: &mut Context<'_>,
50
        w: Pin<&mut W>,
51
    ) -> Poll<io::Result<()>>;
52
}
53

            
54
/// "Handle" an EOF by doing nothing.
55
#[derive(Default, Debug, Clone)]
56
#[allow(clippy::exhaustive_structs)]
57
pub struct Noop;
58

            
59
impl<W> EofStrategy<W> for Noop {
60
    fn poll_send_eof(
61
        self: Pin<&mut Self>,
62
        _cx: &mut Context<'_>,
63
        _w: Pin<&mut W>,
64
    ) -> Poll<io::Result<()>> {
65
        Poll::Ready(Ok(()))
66
    }
67
}
68

            
69
/// Handle an EOF by closing the stream.
70
///
71
/// Note that using this strategy can result in prematurely closed connections:
72
/// As soon as one stream reaches EOF, the other one will be closed,
73
/// even if it still had something to say.
74
/// For protocols like TCP that support half-open connections,
75
/// it's better to use [`FdShutdown`] or [`SocketShutdown`] if possible.
76
#[derive(Default, Debug, Clone)]
77
#[allow(clippy::exhaustive_structs)]
78
pub struct Close;
79

            
80
impl<W: AsyncWrite> EofStrategy<W> for Close {
81
104
    fn poll_send_eof(
82
104
        self: Pin<&mut Self>,
83
104
        cx: &mut Context<'_>,
84
104
        w: Pin<&mut W>,
85
104
    ) -> Poll<io::Result<()>> {
86
104
        w.poll_close(cx)
87
104
    }
88
}
89

            
90
/// Handle an EOF by calling the unix `shutdown(2)` function.
91
///
92
/// This object uses [`shutdown(2)`] to tell the socket that we are done writing,
93
/// but not done reading.
94
///
95
/// On unix-like systems, this object is generally the best choice for production usage.
96
/// It works on any time that implements [`AsFd`].
97
///
98
/// On Windows, see [`SocketShutdown`].
99
///
100
/// # Availability
101
///
102
/// This type is only available on unix-like systems.
103
///
104
/// [`shutdown(2)`]: https://manpages.debian.org/trixie/finit-sysv/shutdown.8.en.html
105
#[cfg(any(doc, unix))]
106
#[derive(Default, Debug, Clone)]
107
#[allow(clippy::exhaustive_structs)]
108
pub struct FdShutdown;
109

            
110
#[cfg(unix)]
111
impl<W: AsFd> EofStrategy<W> for FdShutdown {
112
    fn poll_send_eof(
113
        self: Pin<&mut Self>,
114
        _cx: &mut Context<'_>,
115
        w: Pin<&mut W>,
116
    ) -> Poll<io::Result<()>> {
117
        let fd = w.as_fd();
118
        Poll::Ready(
119
            match unsafe { libc::shutdown(fd.as_raw_fd(), libc::SHUT_WR) } {
120
                -1 => Err(io::Error::last_os_error()),
121
                _ => Ok(()),
122
            },
123
        )
124
    }
125
}
126

            
127
/// Handle an EOF by calling the Windows `shutdown` function.
128
///
129
/// This object uses [`shutdown`] to tell the socket that we are done writing,
130
/// but not done reading.
131
///
132
/// On unix-like systems, this object is generally the best choice for production usage.
133
/// It works on any time that implements [`AsSocket`].
134
///
135
/// On Unix, see [`FdShutdown`].
136
///
137
/// # Availability
138
///
139
/// This type is only available on unix-like systems.#[derive(Default, Debug, Clone)]
140
///
141
/// [`AsSocket`]: https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html
142
/// [`shutdown`]: https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown
143
#[allow(clippy::exhaustive_structs)]
144
#[cfg(any(doc, windows))]
145
pub struct SocketShutdown;
146

            
147
#[cfg(windows)]
148
impl<W: AsSocket> EofStrategy<W> for SocketShutdown {
149
    fn poll_send_eof(
150
        self: Pin<&mut Self>,
151
        _cx: &mut Context<'_>,
152
        w: Pin<&mut W>,
153
    ) -> Poll<io::Result<()>> {
154
        use windows_sys::Win32::Networking::WinSock::{SD_SEND, SOCKET, shutdown};
155
        let socket = w.as_socket();
156
        Poll::Ready(
157
            match unsafe { shutdown(socket.as_raw_socket() as SOCKET, SD_SEND) } {
158
                -1 => Err(io::Error::last_os_error()),
159
                _ => Ok(()),
160
            },
161
        )
162
    }
163
}
164

            
165
/// Internal helper: Wrap a `EofStrategy<W>` to implement `EofStrategy<BufReader<W>>`.
166
#[derive(Default, Debug, Clone)]
167
#[pin_project]
168
pub(crate) struct BufReaderEofWrapper<E>(#[pin] pub(crate) E);
169

            
170
impl<W, E: EofStrategy<W>> EofStrategy<BufReader<W>> for BufReaderEofWrapper<E> {
171
    fn poll_send_eof(
172
        self: Pin<&mut Self>,
173
        cx: &mut Context<'_>,
174
        w: Pin<&mut BufReader<W>>,
175
    ) -> Poll<io::Result<()>> {
176
        self.project().0.poll_send_eof(cx, w.get_pin_mut())
177
    }
178
}