1use std::{
4 fmt, io,
5 pin::Pin,
6 task::{Context, Poll},
7 time::Duration,
8};
9
10use socket2::{SockRef, TcpKeepalive};
11use tokio::{
12 io::{AsyncRead, AsyncWrite, ReadBuf},
13 net::TcpStream,
14 time::sleep,
15};
16
17use crate::{
18 auth::TlsServerEndPoint,
19 tls::{ClientTls, ServerTls},
20};
21
22#[derive(Debug)]
24pub enum NetworkStream<S> {
25 Plain(S),
27 ClientTls(ClientTls<S>),
29 ServerTls(ServerTls<S>),
31}
32
33impl<S> NetworkStream<S> {
34 pub const fn plain(stream: S) -> Self {
36 Self::Plain(stream)
37 }
38
39 pub const fn client_tls(stream: ClientTls<S>) -> Self {
41 Self::ClientTls(stream)
42 }
43
44 pub const fn server_tls(stream: ServerTls<S>) -> Self {
46 Self::ServerTls(stream)
47 }
48
49 pub const fn is_tls(&self) -> bool {
51 !matches!(self, Self::Plain(_))
52 }
53
54 pub const fn is_plain(&self) -> bool {
56 matches!(self, Self::Plain(_))
57 }
58
59 pub fn into_plain(self) -> Result<S, AlreadyTls> {
65 match self {
66 Self::Plain(stream) => Ok(stream),
67 Self::ClientTls(_) | Self::ServerTls(_) => Err(AlreadyTls),
68 }
69 }
70
71 pub fn tls_server_end_point(&self) -> Option<&[u8]> {
73 match self {
74 Self::Plain(_) => None,
75 Self::ClientTls(stream) => Some(stream.tls_server_end_point()),
76 Self::ServerTls(stream) => Some(stream.tls_server_end_point()),
77 }
78 }
79}
80
81impl<S> TlsServerEndPoint for NetworkStream<S> {
82 fn tls_server_end_point(&self) -> &[u8] {
83 match self {
84 Self::Plain(_) => &[],
85 Self::ClientTls(stream) => stream.tls_server_end_point(),
86 Self::ServerTls(stream) => stream.tls_server_end_point(),
87 }
88 }
89}
90
91impl<S: AsyncRead + AsyncWrite + Unpin> NetworkStream<S> {
92 pub fn split(self) -> (tokio::io::ReadHalf<Self>, tokio::io::WriteHalf<Self>) {
94 tokio::io::split(self)
95 }
96}
97
98#[derive(Clone, Copy, Debug, Eq, PartialEq)]
100pub struct AlreadyTls;
101
102impl fmt::Display for AlreadyTls {
103 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
104 formatter.write_str("transport is already using TLS")
105 }
106}
107
108impl std::error::Error for AlreadyTls {}
109
110impl<S: AsyncRead + AsyncWrite + Unpin> AsyncRead for NetworkStream<S> {
111 fn poll_read(
112 mut self: Pin<&mut Self>,
113 context: &mut Context<'_>,
114 buffer: &mut ReadBuf<'_>,
115 ) -> Poll<io::Result<()>> {
116 match &mut *self {
117 Self::Plain(stream) => Pin::new(stream).poll_read(context, buffer),
118 Self::ClientTls(stream) => Pin::new(stream).poll_read(context, buffer),
119 Self::ServerTls(stream) => Pin::new(stream).poll_read(context, buffer),
120 }
121 }
122}
123
124impl<S: AsyncRead + AsyncWrite + Unpin> AsyncWrite for NetworkStream<S> {
125 fn poll_write(
126 mut self: Pin<&mut Self>,
127 context: &mut Context<'_>,
128 buffer: &[u8],
129 ) -> Poll<io::Result<usize>> {
130 match &mut *self {
131 Self::Plain(stream) => Pin::new(stream).poll_write(context, buffer),
132 Self::ClientTls(stream) => Pin::new(stream).poll_write(context, buffer),
133 Self::ServerTls(stream) => Pin::new(stream).poll_write(context, buffer),
134 }
135 }
136
137 fn poll_flush(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<io::Result<()>> {
138 match &mut *self {
139 Self::Plain(stream) => Pin::new(stream).poll_flush(context),
140 Self::ClientTls(stream) => Pin::new(stream).poll_flush(context),
141 Self::ServerTls(stream) => Pin::new(stream).poll_flush(context),
142 }
143 }
144
145 fn poll_shutdown(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<io::Result<()>> {
146 match &mut *self {
147 Self::Plain(stream) => Pin::new(stream).poll_shutdown(context),
148 Self::ClientTls(stream) => Pin::new(stream).poll_shutdown(context),
149 Self::ServerTls(stream) => Pin::new(stream).poll_shutdown(context),
150 }
151 }
152}
153
154#[derive(Clone, Copy, Debug, Eq, PartialEq)]
156pub struct ConnectRetry {
157 pub max_retries: u32,
159 pub initial_delay: Duration,
161 pub max_delay: Duration,
163}
164
165impl Default for ConnectRetry {
166 fn default() -> Self {
167 Self {
168 max_retries: 3,
169 initial_delay: Duration::from_millis(100),
170 max_delay: Duration::from_secs(2),
171 }
172 }
173}
174
175pub async fn connect_with_retry(address: &str, retry: ConnectRetry) -> io::Result<TcpStream> {
181 let mut retries = 0_u32;
182 loop {
183 match TcpStream::connect(address).await {
184 Ok(stream) => return Ok(stream),
185 Err(error) if retries == retry.max_retries => return Err(error),
186 Err(_) => {
187 let exponent = retries.min(63);
188 let multiplier = 1_u64 << exponent;
189 let delay = retry
190 .initial_delay
191 .saturating_mul(u32::try_from(multiplier).unwrap_or(u32::MAX))
192 .min(retry.max_delay);
193 sleep(delay).await;
194 retries = retries.saturating_add(1);
195 }
196 }
197 }
198}
199
200#[derive(Clone, Copy, Debug, Eq, PartialEq)]
202pub struct TcpSettings {
203 pub no_delay: bool,
205 pub user_timeout: Option<Duration>,
207 pub keepalive_time: Option<Duration>,
209 pub keepalive_interval: Option<Duration>,
211 pub keepalive_retries: Option<u32>,
213}
214
215impl Default for TcpSettings {
216 fn default() -> Self {
217 Self {
218 no_delay: true,
219 user_timeout: None,
220 keepalive_time: None,
221 keepalive_interval: None,
222 keepalive_retries: None,
223 }
224 }
225}
226
227#[derive(Debug)]
229pub struct TcpConfigurationError {
230 pub option: &'static str,
232 pub source: io::Error,
234}
235
236impl fmt::Display for TcpConfigurationError {
237 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
238 write!(
239 formatter,
240 "failed to configure {}: {}",
241 self.option, self.source
242 )
243 }
244}
245
246impl std::error::Error for TcpConfigurationError {
247 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
248 Some(&self.source)
249 }
250}
251
252#[must_use]
254pub fn configure_tcp(stream: &TcpStream, settings: TcpSettings) -> Vec<TcpConfigurationError> {
255 let mut errors = Vec::new();
256 if let Err(source) = stream.set_nodelay(settings.no_delay) {
257 errors.push(TcpConfigurationError {
258 option: "TCP_NODELAY",
259 source,
260 });
261 }
262
263 let socket = SockRef::from(stream);
264 #[cfg(target_os = "linux")]
265 if let Some(timeout) = settings.user_timeout
266 && let Err(source) = socket.set_tcp_user_timeout(Some(timeout))
267 {
268 errors.push(TcpConfigurationError {
269 option: "TCP_USER_TIMEOUT",
270 source,
271 });
272 }
273
274 if settings.keepalive_time.is_some()
275 || settings.keepalive_interval.is_some()
276 || settings.keepalive_retries.is_some()
277 {
278 if let Err(source) = socket.set_keepalive(true) {
279 errors.push(TcpConfigurationError {
280 option: "SO_KEEPALIVE",
281 source,
282 });
283 return errors;
284 }
285 let mut keepalive = TcpKeepalive::new();
286 if let Some(time) = settings.keepalive_time {
287 keepalive = keepalive.with_time(time);
288 }
289 if let Some(interval) = settings.keepalive_interval {
290 keepalive = keepalive.with_interval(interval);
291 }
292 if let Some(retries) = settings.keepalive_retries {
293 keepalive = keepalive.with_retries(retries);
294 }
295 if let Err(source) = socket.set_tcp_keepalive(&keepalive) {
296 errors.push(TcpConfigurationError {
297 option: "TCP_KEEPALIVE",
298 source,
299 });
300 }
301 }
302 errors
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use tokio::net::TcpListener;
309
310 #[tokio::test]
311 async fn network_stream_delegates_plain_io() {
312 let (client, server) = tokio::io::duplex(16);
313 let mut client = NetworkStream::plain(client);
314 let mut server = NetworkStream::plain(server);
315 tokio::io::AsyncWriteExt::write_all(&mut client, b"ping")
316 .await
317 .unwrap();
318 let mut bytes = [0; 4];
319 tokio::io::AsyncReadExt::read_exact(&mut server, &mut bytes)
320 .await
321 .unwrap();
322 assert_eq!(&bytes, b"ping");
323 assert!(client.is_plain());
324 assert_eq!(client.tls_server_end_point(), None);
325 }
326
327 #[tokio::test]
328 async fn connects_with_configurable_retry() {
329 let listener = TcpListener::bind(("127.0.0.1", 0)).await.unwrap();
330 let address = listener.local_addr().unwrap().to_string();
331 let client = connect_with_retry(&address, ConnectRetry::default())
332 .await
333 .unwrap();
334 let (_server, _) = listener.accept().await.unwrap();
335 assert!(configure_tcp(&client, TcpSettings::default()).is_empty());
336 }
337}