Skip to main content

datum_net/
tls.rs

1//! TLS-wrapped TCP sources and sinks.
2//!
3//! [`TokioTls`] mirrors the shape of `datum::TokioTcp`, but wraps each TCP
4//! byte stream in a `tokio-rustls` TLS client or server session. Callers supply
5//! their own rustls client/server configs so certificate policy stays explicit.
6
7pub use tokio_rustls::rustls;
8
9use crate::async_carrier::{self, AsyncCommandSender, DemandBatcher};
10use datum::{Flow, Keep, NotUsed, Sink, Source, StreamCompletion, StreamError, StreamResult};
11use std::net::SocketAddr;
12use std::sync::{Arc, Mutex, atomic::AtomicUsize, mpsc as std_mpsc};
13use std::time::Duration;
14use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
15use tokio::net::{TcpListener, TcpStream, ToSocketAddrs};
16use tokio::runtime::Handle;
17use tokio::sync::{mpsc, watch};
18use tokio::task::JoinHandle;
19use tokio_rustls::rustls::pki_types::ServerName;
20use tokio_rustls::{TlsAcceptor, TlsConnector};
21
22const DEFAULT_CHUNK_SIZE: usize = 8192;
23const DEFAULT_RECEIVE_BUFFER: usize = 64;
24const TRANSIENT_ACCEPT_BACKOFF: Duration = Duration::from_millis(10);
25
26static ACTIVE_TLS_CONNECTIONS: AtomicUsize = AtomicUsize::new(0);
27
28/// Snapshot of TLS listener resilience counters.
29#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
30pub struct TlsCarrierDiagnosticCounts {
31    pub transient_accept_errors: u64,
32    pub handshake_failures: u64,
33}
34
35/// Shared collector for TLS listener resilience counters.
36#[derive(Clone, Default)]
37pub struct TlsCarrierDiagnostics {
38    counts: Arc<Mutex<TlsCarrierDiagnosticCounts>>,
39}
40
41impl TlsCarrierDiagnostics {
42    #[must_use]
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    #[must_use]
48    pub fn snapshot(&self) -> TlsCarrierDiagnosticCounts {
49        *self
50            .counts
51            .lock()
52            .unwrap_or_else(|poison| poison.into_inner())
53    }
54
55    fn record_transient_accept_error(&self) {
56        let mut counts = self
57            .counts
58            .lock()
59            .unwrap_or_else(|poison| poison.into_inner());
60        counts.transient_accept_errors = counts.transient_accept_errors.saturating_add(1);
61    }
62
63    fn record_handshake_failure(&self) {
64        let mut counts = self
65            .counts
66            .lock()
67            .unwrap_or_else(|poison| poison.into_inner());
68        counts.handshake_failures = counts.handshake_failures.saturating_add(1);
69    }
70}
71
72/// TLS byte source used by accepted and outgoing TLS connections.
73///
74/// The source emits `Vec<u8>` chunks and backpressures the Tokio owner task with
75/// a bounded demand window. The public `datum-core` extension surface does not
76/// expose the private `TokioByteSource` materialized `IoResult` constructor, so
77/// `datum-net` exposes TLS-specific byte-half aliases.
78pub type TlsByteSource = Source<Vec<u8>, NotUsed>;
79
80/// TLS byte sink used by accepted and outgoing TLS connections.
81///
82/// The sink writes one upstream chunk at a time and sends TLS/TCP shutdown from
83/// its resource close hook when upstream completes.
84pub type TlsByteSink = Sink<Vec<u8>, StreamCompletion<NotUsed>>;
85
86enum DemandResponse<T> {
87    Item(T),
88    Complete,
89    Error(StreamError),
90}
91
92struct ReadResource {
93    receiver: std_mpsc::Receiver<DemandResponse<Vec<u8>>>,
94    carrier: TlsCarrier,
95    demand: DemandBatcher,
96    pending: Option<DemandResponse<Vec<u8>>>,
97}
98
99impl Drop for ReadResource {
100    fn drop(&mut self) {
101        self.carrier.close_read();
102    }
103}
104
105enum TlsCarrierCommand {
106    Demand(usize),
107    SendOne(Vec<u8>),
108    SendBatch(Vec<Vec<u8>>),
109    CloseRead,
110    CloseWrite {
111        ack: std_mpsc::Sender<StreamResult<()>>,
112    },
113}
114
115#[derive(Clone)]
116struct TlsCarrier {
117    inner: Arc<TlsCarrierInner>,
118}
119
120struct TlsCarrierInner {
121    commands: AsyncCommandSender<TlsCarrierCommand>,
122    send_errors: Mutex<std_mpsc::Receiver<StreamError>>,
123    task: Mutex<Option<JoinHandle<()>>>,
124    _execution: async_carrier::ShardedTokioCarrierExecution,
125}
126
127impl Drop for TlsCarrierInner {
128    fn drop(&mut self) {
129        if let Some(task) = self
130            .task
131            .lock()
132            .unwrap_or_else(|poison| poison.into_inner())
133            .take()
134        {
135            task.abort();
136        }
137    }
138}
139
140impl TlsCarrier {
141    fn close_read(&self) {
142        let _ = self.inner.commands.try_send(TlsCarrierCommand::CloseRead);
143    }
144
145    fn request_demand(&self, demand: usize) -> StreamResult<()> {
146        self.inner
147            .commands
148            .send_or_blocking(TlsCarrierCommand::Demand(demand))
149    }
150
151    fn send_items(&self, items: Vec<Vec<u8>>) -> StreamResult<()> {
152        self.check_send_error()?;
153        self.inner
154            .commands
155            .send_or_blocking(TlsCarrierCommand::SendBatch(items))
156            .map_err(|error| StreamError::Failed(format!("TLS send batch failed: {error:?}")))
157    }
158
159    fn send_one(&self, item: Vec<u8>) -> StreamResult<()> {
160        self.check_send_error()?;
161        self.inner
162            .commands
163            .send_or_blocking(TlsCarrierCommand::SendOne(item))
164            .map_err(|error| StreamError::Failed(format!("TLS send failed: {error:?}")))
165    }
166
167    fn close_write(&self) -> StreamResult<()> {
168        self.check_send_error()?;
169        let (ack_sender, ack_receiver) = std_mpsc::channel();
170        if self
171            .inner
172            .commands
173            .send_or_blocking(TlsCarrierCommand::CloseWrite { ack: ack_sender })
174            .is_err()
175        {
176            return Ok(());
177        }
178        match ack_receiver.recv() {
179            Ok(result) => result,
180            Err(_) => Err(abrupt_termination()),
181        }?;
182        self.check_send_error()
183    }
184
185    fn check_send_error(&self) -> StreamResult<()> {
186        match self
187            .inner
188            .send_errors
189            .lock()
190            .expect("TLS carrier send error receiver poisoned")
191            .try_recv()
192        {
193            Ok(error) => Err(error),
194            Err(std_mpsc::TryRecvError::Empty) | Err(std_mpsc::TryRecvError::Disconnected) => {
195                Ok(())
196            }
197        }
198    }
199}
200
201struct SendResource {
202    carrier: TlsCarrier,
203    pending: Vec<Vec<u8>>,
204    batch_size: usize,
205}
206
207struct BindResource {
208    demands: mpsc::Sender<std_mpsc::Sender<DemandResponse<TlsIncomingConnection>>>,
209    cancel: watch::Sender<bool>,
210    task: JoinHandle<()>,
211}
212
213struct TlsBindTask {
214    listener: TcpListener,
215    acceptor: TlsAcceptor,
216    local_addr: SocketAddr,
217    chunk_size: usize,
218    handle: Handle,
219    diagnostics: Option<TlsCarrierDiagnostics>,
220    demands: mpsc::Receiver<std_mpsc::Sender<DemandResponse<TlsIncomingConnection>>>,
221    cancel: watch::Receiver<bool>,
222}
223
224impl Drop for BindResource {
225    fn drop(&mut self) {
226        let _ = self.cancel.send(true);
227        self.task.abort();
228    }
229}
230
231fn io_error(error: std::io::Error) -> StreamError {
232    StreamError::Failed(error.to_string())
233}
234
235fn abrupt_termination() -> StreamError {
236    StreamError::AbruptTermination
237}
238
239/// A materialized TLS connection.
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
241pub struct TlsConnection {
242    pub local_addr: SocketAddr,
243    pub remote_addr: SocketAddr,
244}
245
246impl TlsConnection {
247    #[must_use]
248    pub fn local_addr(&self) -> SocketAddr {
249        self.local_addr
250    }
251
252    #[must_use]
253    pub fn remote_addr(&self) -> SocketAddr {
254        self.remote_addr
255    }
256}
257
258/// A materialized TLS listener binding.
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260pub struct TlsBinding {
261    pub local_addr: SocketAddr,
262}
263
264impl TlsBinding {
265    #[must_use]
266    pub fn local_addr(&self) -> SocketAddr {
267        self.local_addr
268    }
269}
270
271/// A TLS connection accepted by [`TokioTls::bind`].
272pub struct TlsIncomingConnection {
273    connection: TlsConnection,
274    source: TlsByteSource,
275    sink: TlsByteSink,
276}
277
278impl TlsIncomingConnection {
279    #[must_use]
280    pub fn local_addr(&self) -> SocketAddr {
281        self.connection.local_addr
282    }
283
284    #[must_use]
285    pub fn remote_addr(&self) -> SocketAddr {
286        self.connection.remote_addr
287    }
288
289    #[must_use]
290    pub fn connection(&self) -> TlsConnection {
291        self.connection
292    }
293
294    #[must_use]
295    pub fn into_parts(self) -> (TlsByteSource, TlsByteSink) {
296        (self.source, self.sink)
297    }
298
299    #[must_use]
300    pub fn into_flow(self) -> Flow<Vec<u8>, Vec<u8>, NotUsed> {
301        Flow::from_sink_and_source_coupled(self.sink, self.source)
302            .map_materialized_value(|_| NotUsed)
303    }
304}
305
306/// TLS-over-TCP stream entry points.
307pub struct TokioTls;
308
309/// Alias for [`TokioTls`].
310pub type Tls = TokioTls;
311
312impl TokioTls {
313    /// Opens a TLS client connection as a coupled byte flow.
314    ///
315    /// TCP connect and the TLS client handshake run when the flow is
316    /// materialized. The caller-provided [`rustls::ClientConfig`] controls root
317    /// trust, protocol versions, ALPN, and certificate verification policy.
318    #[must_use]
319    pub fn outgoing_connection<A>(
320        addr: A,
321        server_name: ServerName<'static>,
322        client_config: Arc<rustls::ClientConfig>,
323        chunk_size: usize,
324    ) -> Flow<Vec<u8>, Vec<u8>, StreamCompletion<TlsConnection>>
325    where
326        A: ToSocketAddrs + Clone + Send + Sync + 'static,
327    {
328        assert!(chunk_size > 0, "chunk size must be greater than zero");
329        Flow::future_flow(move || {
330            let addr = addr.clone();
331            let server_name = server_name.clone();
332            let client_config = Arc::clone(&client_config);
333            async move {
334                let handle = Handle::current();
335                tls_client_connect(addr, server_name, client_config, handle, chunk_size).await
336            }
337        })
338    }
339
340    /// Opens a TLS client connection using the default 8 KiB chunk size.
341    #[must_use]
342    pub fn outgoing_connection_default<A>(
343        addr: A,
344        server_name: ServerName<'static>,
345        client_config: Arc<rustls::ClientConfig>,
346    ) -> Flow<Vec<u8>, Vec<u8>, StreamCompletion<TlsConnection>>
347    where
348        A: ToSocketAddrs + Clone + Send + Sync + 'static,
349    {
350        Self::outgoing_connection(addr, server_name, client_config, DEFAULT_CHUNK_SIZE)
351    }
352
353    /// Binds a TLS server listener and emits accepted TLS connections.
354    ///
355    /// The TCP listener binds when the source is materialized. Each downstream
356    /// pull permits TCP accepts plus TLS server handshakes until a connection
357    /// succeeds. Per-connection handshake failures are logged and skipped.
358    #[must_use]
359    pub fn bind<A>(
360        addr: A,
361        server_config: Arc<rustls::ServerConfig>,
362        chunk_size: usize,
363    ) -> Source<TlsIncomingConnection, StreamCompletion<TlsBinding>>
364    where
365        A: ToSocketAddrs + Clone + Send + Sync + 'static,
366    {
367        Self::bind_optional_diagnostics(addr, server_config, chunk_size, None)
368    }
369
370    /// Binds a TLS server listener and records skipped accept/handshake failures.
371    #[must_use]
372    pub fn bind_with_diagnostics<A>(
373        addr: A,
374        server_config: Arc<rustls::ServerConfig>,
375        chunk_size: usize,
376        diagnostics: TlsCarrierDiagnostics,
377    ) -> Source<TlsIncomingConnection, StreamCompletion<TlsBinding>>
378    where
379        A: ToSocketAddrs + Clone + Send + Sync + 'static,
380    {
381        Self::bind_optional_diagnostics(addr, server_config, chunk_size, Some(diagnostics))
382    }
383
384    fn bind_optional_diagnostics<A>(
385        addr: A,
386        server_config: Arc<rustls::ServerConfig>,
387        chunk_size: usize,
388        diagnostics: Option<TlsCarrierDiagnostics>,
389    ) -> Source<TlsIncomingConnection, StreamCompletion<TlsBinding>>
390    where
391        A: ToSocketAddrs + Clone + Send + Sync + 'static,
392    {
393        assert!(chunk_size > 0, "chunk size must be greater than zero");
394        Source::lazy_future_source(move || {
395            let addr = addr.clone();
396            let server_config = Arc::clone(&server_config);
397            let diagnostics = diagnostics.clone();
398            async move {
399                let handle = Handle::current();
400                let listener = TcpListener::bind(addr).await.map_err(io_error)?;
401                let local_addr = listener.local_addr().map_err(io_error)?;
402                Ok(tls_bind_source(
403                    listener,
404                    server_config,
405                    local_addr,
406                    handle,
407                    chunk_size,
408                    diagnostics,
409                ))
410            }
411        })
412    }
413
414    /// Binds a TLS server listener using the default 8 KiB chunk size.
415    #[must_use]
416    pub fn bind_default<A>(
417        addr: A,
418        server_config: Arc<rustls::ServerConfig>,
419    ) -> Source<TlsIncomingConnection, StreamCompletion<TlsBinding>>
420    where
421        A: ToSocketAddrs + Clone + Send + Sync + 'static,
422    {
423        Self::bind(addr, server_config, DEFAULT_CHUNK_SIZE)
424    }
425}
426
427pub(crate) fn tls_flow_from_stream_with_execution<S>(
428    stream: S,
429    connection: TlsConnection,
430    execution: async_carrier::ShardedTokioCarrierExecution,
431    chunk_size: usize,
432) -> Flow<Vec<u8>, Vec<u8>, TlsConnection>
433where
434    S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
435{
436    let (source, sink) = single_use_tls_halves(stream, execution, chunk_size);
437    Flow::from_sink_and_source(sink, source).map_materialized_value(move |_| connection)
438}
439
440fn tls_incoming_connection<S>(
441    stream: S,
442    connection: TlsConnection,
443    execution: async_carrier::ShardedTokioCarrierExecution,
444    chunk_size: usize,
445) -> TlsIncomingConnection
446where
447    S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
448{
449    let (source, sink) = single_use_tls_halves(stream, execution, chunk_size);
450    TlsIncomingConnection {
451        connection,
452        source,
453        sink,
454    }
455}
456
457fn single_use_tls_halves<S>(
458    stream: S,
459    execution: async_carrier::ShardedTokioCarrierExecution,
460    chunk_size: usize,
461) -> (TlsByteSource, TlsByteSink)
462where
463    S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
464{
465    let (carrier, receiver) =
466        start_tls_carrier(stream, execution, chunk_size, DEFAULT_RECEIVE_BUFFER);
467    let source =
468        single_use_tls_source_from_carrier(carrier.clone(), receiver, DEFAULT_RECEIVE_BUFFER);
469    let sink = single_use_tls_sink_from_carrier(carrier, 1);
470    (source, sink)
471}
472
473fn single_use_tls_source_from_carrier(
474    carrier: TlsCarrier,
475    receiver: std_mpsc::Receiver<DemandResponse<Vec<u8>>>,
476    receive_buffer: usize,
477) -> TlsByteSource {
478    let receiver = Arc::new(Mutex::new(Some(receiver)));
479    Source::unfold_resource(
480        {
481            let receiver = Arc::clone(&receiver);
482            move || {
483                let receiver = receiver
484                    .lock()
485                    .expect("single-use TLS receiver poisoned")
486                    .take()
487                    .ok_or_else(|| StreamError::Failed("TLS source already materialized".into()))?;
488                let demand = DemandBatcher::new(receive_buffer);
489                let pending = match carrier.request_demand(demand.initial()) {
490                    Ok(()) => None,
491                    Err(error) => match receiver.try_recv() {
492                        Ok(response) => Some(response),
493                        Err(std_mpsc::TryRecvError::Empty) => return Err(error),
494                        Err(std_mpsc::TryRecvError::Disconnected) => {
495                            return Err(abrupt_termination());
496                        }
497                    },
498                };
499                Ok(ReadResource {
500                    receiver,
501                    carrier: carrier.clone(),
502                    demand,
503                    pending,
504                })
505            }
506        },
507        read_next_chunk,
508        close_read_resource,
509    )
510}
511
512fn read_next_chunk(resource: &mut ReadResource) -> StreamResult<Option<Vec<u8>>> {
513    let response = match resource.pending.take() {
514        Some(response) => response,
515        None => resource.receiver.recv().map_err(|_| abrupt_termination())?,
516    };
517    match response {
518        DemandResponse::Item(chunk) => {
519            if let Some(demand) = resource.demand.record_consumed() {
520                let _ = resource.carrier.request_demand(demand);
521            }
522            Ok(Some(chunk))
523        }
524        DemandResponse::Complete => Ok(None),
525        DemandResponse::Error(error) => Err(error),
526    }
527}
528
529fn close_read_resource(resource: ReadResource) -> StreamResult<()> {
530    resource.carrier.close_read();
531    Ok(())
532}
533
534fn start_tls_carrier<S>(
535    stream: S,
536    execution: async_carrier::ShardedTokioCarrierExecution,
537    chunk_size: usize,
538    receive_buffer: usize,
539) -> (TlsCarrier, std_mpsc::Receiver<DemandResponse<Vec<u8>>>)
540where
541    S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
542{
543    let command_capacity = async_carrier::DEFAULT_COMMAND_BUFFER.max(receive_buffer);
544    let (commands, command_receiver) = async_carrier::command_channel(command_capacity, "TLS");
545    let (send_error_sender, send_error_receiver) = std_mpsc::channel();
546    let (receive_sender, receive_receiver) =
547        std_mpsc::sync_channel(receive_buffer.saturating_add(1));
548    let (reader, writer) = tokio::io::split(stream);
549    let command_keepalive = commands.clone();
550    let task = execution.handle().spawn(run_tls_carrier_task(
551        reader,
552        writer,
553        chunk_size,
554        receive_sender,
555        send_error_sender,
556        command_keepalive,
557        command_receiver,
558    ));
559    (
560        TlsCarrier {
561            inner: Arc::new(TlsCarrierInner {
562                commands,
563                send_errors: Mutex::new(send_error_receiver),
564                task: Mutex::new(Some(task)),
565                _execution: execution,
566            }),
567        },
568        receive_receiver,
569    )
570}
571
572async fn run_tls_carrier_task<R, W>(
573    mut reader: R,
574    mut writer: W,
575    chunk_size: usize,
576    receive_sender: std_mpsc::SyncSender<DemandResponse<Vec<u8>>>,
577    send_error_sender: std_mpsc::Sender<StreamError>,
578    _command_keepalive: AsyncCommandSender<TlsCarrierCommand>,
579    mut commands: mpsc::Receiver<TlsCarrierCommand>,
580) where
581    R: AsyncRead + Unpin + Send + 'static,
582    W: AsyncWrite + Unpin + Send + 'static,
583{
584    let mut buffer = vec![0_u8; chunk_size];
585    let mut pending_tail = Vec::with_capacity(chunk_size);
586    let mut requested = 0_usize;
587    let mut read_open = true;
588    let mut write_open = true;
589
590    loop {
591        if !read_open && !write_open {
592            return;
593        }
594
595        if read_open && requested > 0 {
596            tokio::select! {
597                biased;
598                command = commands.recv() => {
599                    let Some(command) = command else {
600                        return;
601                    };
602                    if !handle_tls_carrier_command(
603                        &mut writer,
604                        command,
605                        &send_error_sender,
606                        &mut read_open,
607                        &mut write_open,
608                        &mut requested,
609                    ).await {
610                        return;
611                    }
612                }
613                read = reader.read(&mut buffer) => {
614                    match read {
615                        Ok(0) => {
616                            if !pending_tail.is_empty() {
617                                match try_send_tls_read_response(
618                                    &receive_sender,
619                                    DemandResponse::Item(std::mem::take(&mut pending_tail)),
620                                ) {
621                                    TlsQueueOutcome::Queued => {
622                                        requested = requested.saturating_sub(1);
623                                    }
624                                    TlsQueueOutcome::Closed => {
625                                        read_open = false;
626                                        continue;
627                                    }
628                                    TlsQueueOutcome::Full => {
629                                        report_tls_read_error(
630                                            &receive_sender,
631                                            &send_error_sender,
632                                            tls_receive_buffer_overflow(),
633                                        );
634                                        return;
635                                    }
636                                }
637                            }
638                            match try_send_tls_read_response(
639                                &receive_sender,
640                                DemandResponse::Complete,
641                            ) {
642                                TlsQueueOutcome::Queued | TlsQueueOutcome::Closed => {
643                                    read_open = false;
644                                }
645                                TlsQueueOutcome::Full => {
646                                    report_tls_read_error(
647                                        &receive_sender,
648                                        &send_error_sender,
649                                        tls_receive_buffer_overflow(),
650                                    );
651                                    return;
652                                }
653                            }
654                        }
655                        Ok(read) => {
656                            match queue_tls_read_chunks(
657                                &receive_sender,
658                                &send_error_sender,
659                                chunk_size,
660                                &mut pending_tail,
661                                &buffer[..read],
662                            ) {
663                                TlsReadQueueResult::Queued(queued) => {
664                                    requested = requested.saturating_sub(queued);
665                                }
666                                TlsReadQueueResult::Closed => {
667                                    read_open = false;
668                                }
669                                TlsReadQueueResult::Failed => {
670                                    return;
671                                }
672                            }
673                        }
674                        Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
675                        Err(error) => {
676                            report_tls_read_error(
677                                &receive_sender,
678                                &send_error_sender,
679                                io_error(error),
680                            );
681                            return;
682                        }
683                    }
684                }
685            }
686        } else {
687            let Some(command) = commands.recv().await else {
688                return;
689            };
690            if !handle_tls_carrier_command(
691                &mut writer,
692                command,
693                &send_error_sender,
694                &mut read_open,
695                &mut write_open,
696                &mut requested,
697            )
698            .await
699            {
700                return;
701            }
702        }
703    }
704}
705
706async fn handle_tls_carrier_command<W>(
707    writer: &mut W,
708    command: TlsCarrierCommand,
709    send_error_sender: &std_mpsc::Sender<StreamError>,
710    read_open: &mut bool,
711    write_open: &mut bool,
712    requested: &mut usize,
713) -> bool
714where
715    W: AsyncWrite + Unpin,
716{
717    match command {
718        TlsCarrierCommand::Demand(demand) => {
719            *requested = requested.saturating_add(demand);
720            true
721        }
722        TlsCarrierCommand::SendOne(chunk) => {
723            if !*write_open {
724                report_tls_write_error(
725                    send_error_sender,
726                    StreamError::Failed("TLS write side is closed".to_owned()),
727                );
728                return *read_open;
729            }
730            if write_one_tls_chunk(writer, send_error_sender, &chunk).await {
731                true
732            } else {
733                *write_open = false;
734                *read_open
735            }
736        }
737        TlsCarrierCommand::SendBatch(chunks) => {
738            if !*write_open {
739                report_tls_write_error(
740                    send_error_sender,
741                    StreamError::Failed("TLS write side is closed".to_owned()),
742                );
743                return *read_open;
744            }
745            for chunk in &chunks {
746                if let Err(error) = writer.write_all(chunk).await.map_err(io_error) {
747                    report_tls_write_error(send_error_sender, error);
748                    *write_open = false;
749                    return *read_open;
750                }
751            }
752            if let Err(error) = writer.flush().await.map_err(io_error) {
753                report_tls_write_error(send_error_sender, error);
754                *write_open = false;
755                return *read_open;
756            }
757            true
758        }
759        TlsCarrierCommand::CloseRead => {
760            *read_open = false;
761            true
762        }
763        TlsCarrierCommand::CloseWrite { ack } => {
764            *write_open = false;
765            let result = close_tls_writer(writer).await;
766            match result {
767                Ok(()) => {
768                    let _ = ack.send(Ok(()));
769                    true
770                }
771                Err(error) => {
772                    report_tls_write_error(send_error_sender, error.clone());
773                    let _ = ack.send(Err(error));
774                    *read_open
775                }
776            }
777        }
778    }
779}
780
781async fn write_one_tls_chunk<W>(
782    writer: &mut W,
783    send_error_sender: &std_mpsc::Sender<StreamError>,
784    chunk: &[u8],
785) -> bool
786where
787    W: AsyncWrite + Unpin,
788{
789    if let Err(error) = writer.write_all(chunk).await.map_err(io_error) {
790        report_tls_write_error(send_error_sender, error);
791        return false;
792    }
793    if let Err(error) = writer.flush().await.map_err(io_error) {
794        report_tls_write_error(send_error_sender, error);
795        return false;
796    }
797    true
798}
799
800async fn close_tls_writer<W>(writer: &mut W) -> StreamResult<()>
801where
802    W: AsyncWrite + Unpin,
803{
804    writer.flush().await.map_err(io_error)?;
805    writer.shutdown().await.map_err(io_error)
806}
807
808enum TlsReadQueueResult {
809    Queued(usize),
810    Closed,
811    Failed,
812}
813
814enum TlsQueueOutcome {
815    Queued,
816    Full,
817    Closed,
818}
819
820fn queue_tls_read_chunks(
821    sender: &std_mpsc::SyncSender<DemandResponse<Vec<u8>>>,
822    send_error_sender: &std_mpsc::Sender<StreamError>,
823    chunk_size: usize,
824    pending_tail: &mut Vec<u8>,
825    read_buffer: &[u8],
826) -> TlsReadQueueResult {
827    let mut offset = 0;
828    let mut queued = 0_usize;
829    if !pending_tail.is_empty() {
830        let needed = chunk_size - pending_tail.len();
831        let take = needed.min(read_buffer.len());
832        pending_tail.extend_from_slice(&read_buffer[..take]);
833        offset += take;
834        if pending_tail.len() == chunk_size {
835            match try_send_tls_read_response(
836                sender,
837                DemandResponse::Item(std::mem::take(pending_tail)),
838            ) {
839                TlsQueueOutcome::Queued => queued += 1,
840                TlsQueueOutcome::Closed => return TlsReadQueueResult::Closed,
841                TlsQueueOutcome::Full => {
842                    report_tls_read_error(sender, send_error_sender, tls_receive_buffer_overflow());
843                    return TlsReadQueueResult::Failed;
844                }
845            }
846        }
847    }
848
849    while offset + chunk_size <= read_buffer.len() {
850        let next = offset + chunk_size;
851        match try_send_tls_read_response(
852            sender,
853            DemandResponse::Item(read_buffer[offset..next].to_vec()),
854        ) {
855            TlsQueueOutcome::Queued => queued += 1,
856            TlsQueueOutcome::Closed => return TlsReadQueueResult::Closed,
857            TlsQueueOutcome::Full => {
858                report_tls_read_error(sender, send_error_sender, tls_receive_buffer_overflow());
859                return TlsReadQueueResult::Failed;
860            }
861        }
862        offset = next;
863    }
864
865    if offset < read_buffer.len() {
866        pending_tail.extend_from_slice(&read_buffer[offset..]);
867    }
868    TlsReadQueueResult::Queued(queued)
869}
870
871fn try_send_tls_read_response(
872    sender: &std_mpsc::SyncSender<DemandResponse<Vec<u8>>>,
873    item: DemandResponse<Vec<u8>>,
874) -> TlsQueueOutcome {
875    match sender.try_send(item) {
876        Ok(()) => TlsQueueOutcome::Queued,
877        Err(std_mpsc::TrySendError::Full(_)) => TlsQueueOutcome::Full,
878        Err(std_mpsc::TrySendError::Disconnected(_)) => TlsQueueOutcome::Closed,
879    }
880}
881
882fn report_tls_read_error(
883    receive_sender: &std_mpsc::SyncSender<DemandResponse<Vec<u8>>>,
884    send_error_sender: &std_mpsc::Sender<StreamError>,
885    error: StreamError,
886) {
887    let _ = send_error_sender.send(error.clone());
888    let _ = receive_sender.try_send(DemandResponse::Error(error));
889}
890
891fn report_tls_write_error(send_error_sender: &std_mpsc::Sender<StreamError>, error: StreamError) {
892    let _ = send_error_sender.send(error);
893}
894
895fn tls_receive_buffer_overflow() -> StreamError {
896    StreamError::Failed("TLS receive buffer filled without downstream demand".to_owned())
897}
898
899fn single_use_tls_sink_from_carrier(carrier: TlsCarrier, batch_size: usize) -> TlsByteSink {
900    let carrier = Arc::new(Mutex::new(Some(carrier)));
901    Flow::<Vec<u8>, Vec<u8>>::identity()
902        .map_with_resource(
903            {
904                let carrier = Arc::clone(&carrier);
905                move || {
906                    let carrier = carrier
907                        .lock()
908                        .expect("single-use TLS carrier poisoned")
909                        .take()
910                        .ok_or_else(|| {
911                            StreamError::Failed("TLS sink already materialized".into())
912                        })?;
913                    Ok(SendResource {
914                        carrier,
915                        pending: Vec::with_capacity(batch_size),
916                        batch_size,
917                    })
918                }
919            },
920            |resource, chunk| {
921                send_tls_chunk(resource, chunk)?;
922                Ok(NotUsed)
923            },
924            close_tls_send_resource,
925        )
926        .to_mat(Sink::ignore(), Keep::right)
927}
928
929fn close_tls_send_resource(mut resource: SendResource) -> StreamResult<Option<NotUsed>> {
930    flush_tls_send_resource(&mut resource)?;
931    resource.carrier.close_write()?;
932    Ok(None)
933}
934
935fn send_tls_chunk(resource: &mut SendResource, chunk: Vec<u8>) -> StreamResult<()> {
936    if resource.batch_size <= 1 {
937        return resource.carrier.send_one(chunk);
938    }
939    resource.pending.push(chunk);
940    if resource.pending.len() >= resource.batch_size {
941        flush_tls_send_resource(resource)?;
942    }
943    Ok(())
944}
945
946fn flush_tls_send_resource(resource: &mut SendResource) -> StreamResult<()> {
947    if resource.pending.is_empty() {
948        return resource.carrier.check_send_error();
949    }
950    let pending = std::mem::take(&mut resource.pending);
951    resource.carrier.send_items(pending)
952}
953
954fn tls_bind_source(
955    listener: TcpListener,
956    server_config: Arc<rustls::ServerConfig>,
957    local_addr: SocketAddr,
958    handle: Handle,
959    chunk_size: usize,
960    diagnostics: Option<TlsCarrierDiagnostics>,
961) -> Source<TlsIncomingConnection, TlsBinding> {
962    let listener = Arc::new(Mutex::new(Some(listener)));
963    Source::unfold_resource(
964        {
965            let listener = Arc::clone(&listener);
966            let handle = handle.clone();
967            move || {
968                let listener = listener
969                    .lock()
970                    .expect("single-use TLS listener poisoned")
971                    .take()
972                    .ok_or_else(|| {
973                        StreamError::Failed("TLS listener already materialized".into())
974                    })?;
975                let (demand_sender, demand_receiver) = mpsc::channel(1);
976                let (cancel_sender, cancel_receiver) = watch::channel(false);
977                let task = handle.spawn(run_tls_bind_task(TlsBindTask {
978                    listener,
979                    acceptor: TlsAcceptor::from(Arc::clone(&server_config)),
980                    local_addr,
981                    chunk_size,
982                    handle: handle.clone(),
983                    diagnostics: diagnostics.clone(),
984                    demands: demand_receiver,
985                    cancel: cancel_receiver,
986                }));
987                Ok(BindResource {
988                    demands: demand_sender,
989                    cancel: cancel_sender,
990                    task,
991                })
992            }
993        },
994        |resource| {
995            let (reply_sender, reply_receiver) = std_mpsc::channel();
996            resource
997                .demands
998                .blocking_send(reply_sender)
999                .map_err(|_| abrupt_termination())?;
1000            match reply_receiver.recv() {
1001                Ok(DemandResponse::Item(connection)) => Ok(Some(connection)),
1002                Ok(DemandResponse::Complete) => Ok(None),
1003                Ok(DemandResponse::Error(error)) => Err(error),
1004                Err(_) => Err(abrupt_termination()),
1005            }
1006        },
1007        close_bind_resource,
1008    )
1009    .map_materialized_value(move |_| TlsBinding { local_addr })
1010}
1011
1012fn close_bind_resource(resource: BindResource) -> StreamResult<()> {
1013    let _ = resource.cancel.send(true);
1014    resource.task.abort();
1015    Ok(())
1016}
1017
1018async fn run_tls_bind_task(task: TlsBindTask) {
1019    let TlsBindTask {
1020        listener,
1021        acceptor,
1022        local_addr,
1023        chunk_size,
1024        handle,
1025        diagnostics,
1026        mut demands,
1027        mut cancel,
1028    } = task;
1029    loop {
1030        let reply = tokio::select! {
1031            demand = demands.recv() => match demand {
1032                Some(reply) => reply,
1033                None => return,
1034            },
1035            changed = cancel.changed() => {
1036                let _ = changed;
1037                return;
1038            }
1039        };
1040
1041        loop {
1042            let (tcp, remote_addr) = loop {
1043                let accepted = tokio::select! {
1044                    accepted = listener.accept() => accepted,
1045                    changed = cancel.changed() => {
1046                        let _ = changed;
1047                        return;
1048                    }
1049                };
1050
1051                match accepted {
1052                    Ok(accepted) => break accepted,
1053                    Err(error) => match classify_accept_error(&error) {
1054                        AcceptErrorDecision::Retry => {
1055                            if let Some(diagnostics) = &diagnostics {
1056                                diagnostics.record_transient_accept_error();
1057                            }
1058                            tracing::warn!(
1059                                local_addr = %local_addr,
1060                                error = %error,
1061                                "transient TLS accept error; continuing listener"
1062                            );
1063                            continue;
1064                        }
1065                        AcceptErrorDecision::Backoff(delay) => {
1066                            if let Some(diagnostics) = &diagnostics {
1067                                diagnostics.record_transient_accept_error();
1068                            }
1069                            tracing::warn!(
1070                                local_addr = %local_addr,
1071                                error = %error,
1072                                backoff_ms = delay.as_millis(),
1073                                "resource-pressure TLS accept error; backing off"
1074                            );
1075                            tokio::select! {
1076                                _ = tokio::time::sleep(delay) => {},
1077                                changed = cancel.changed() => {
1078                                    let _ = changed;
1079                                    return;
1080                                }
1081                            }
1082                            continue;
1083                        }
1084                        AcceptErrorDecision::Fatal => {
1085                            let _ = reply.send(DemandResponse::Error(io_error(error)));
1086                            return;
1087                        }
1088                    },
1089                }
1090            };
1091
1092            let connection = TlsConnection {
1093                local_addr: tcp.local_addr().unwrap_or(local_addr),
1094                remote_addr,
1095            };
1096            let execution = tls_connection_execution(handle.clone());
1097            let accepted = tokio::select! {
1098                accepted = accept_tls_on_execution(tcp, acceptor.clone(), &execution) => accepted,
1099                changed = cancel.changed() => {
1100                    let _ = changed;
1101                    return;
1102                }
1103            };
1104
1105            match accepted {
1106                Ok(stream) => {
1107                    let incoming =
1108                        tls_incoming_connection(stream, connection, execution, chunk_size);
1109                    if reply.send(DemandResponse::Item(incoming)).is_err() {
1110                        return;
1111                    }
1112                    break;
1113                }
1114                Err(error) => {
1115                    if let Some(diagnostics) = &diagnostics {
1116                        diagnostics.record_handshake_failure();
1117                    }
1118                    tracing::warn!(
1119                        local_addr = %local_addr,
1120                        remote_addr = %remote_addr,
1121                        error = ?error,
1122                        "TLS server handshake failed; continuing listener"
1123                    );
1124                    continue;
1125                }
1126            };
1127        }
1128    }
1129}
1130
1131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1132enum AcceptErrorDecision {
1133    Retry,
1134    Backoff(Duration),
1135    Fatal,
1136}
1137
1138fn classify_accept_error(error: &std::io::Error) -> AcceptErrorDecision {
1139    if matches!(
1140        error.kind(),
1141        std::io::ErrorKind::Interrupted
1142            | std::io::ErrorKind::ConnectionAborted
1143            | std::io::ErrorKind::ConnectionReset
1144    ) || error.raw_os_error().is_some_and(is_retry_accept_errno)
1145    {
1146        return AcceptErrorDecision::Retry;
1147    }
1148    if error
1149        .raw_os_error()
1150        .is_some_and(is_resource_pressure_accept_errno)
1151    {
1152        return AcceptErrorDecision::Backoff(TRANSIENT_ACCEPT_BACKOFF);
1153    }
1154    AcceptErrorDecision::Fatal
1155}
1156
1157#[cfg(target_os = "linux")]
1158fn is_retry_accept_errno(code: i32) -> bool {
1159    matches!(code, 4 | 103 | 104)
1160}
1161
1162#[cfg(not(target_os = "linux"))]
1163fn is_retry_accept_errno(_code: i32) -> bool {
1164    false
1165}
1166
1167#[cfg(target_os = "linux")]
1168fn is_resource_pressure_accept_errno(code: i32) -> bool {
1169    matches!(code, 12 | 23 | 24 | 105)
1170}
1171
1172#[cfg(not(target_os = "linux"))]
1173fn is_resource_pressure_accept_errno(_code: i32) -> bool {
1174    false
1175}
1176
1177pub(crate) fn tls_connection_execution(
1178    fallback: Handle,
1179) -> async_carrier::ShardedTokioCarrierExecution {
1180    async_carrier::sharded_tokio_carrier_execution(fallback, &ACTIVE_TLS_CONNECTIONS)
1181}
1182
1183pub(crate) async fn tls_client_connect<A>(
1184    addr: A,
1185    server_name: ServerName<'static>,
1186    client_config: Arc<rustls::ClientConfig>,
1187    fallback: Handle,
1188    chunk_size: usize,
1189) -> StreamResult<Flow<Vec<u8>, Vec<u8>, TlsConnection>>
1190where
1191    A: ToSocketAddrs + Send + 'static,
1192{
1193    let execution = tls_connection_execution(fallback);
1194    let (tls, connection) = execution
1195        .run(async move {
1196            let tcp = TcpStream::connect(addr).await.map_err(io_error)?;
1197            let connection = TlsConnection {
1198                local_addr: tcp.local_addr().map_err(io_error)?,
1199                remote_addr: tcp.peer_addr().map_err(io_error)?,
1200            };
1201            let tls = TlsConnector::from(client_config)
1202                .connect(server_name, tcp)
1203                .await
1204                .map_err(io_error)?;
1205            Ok((tls, connection))
1206        })
1207        .await?;
1208    Ok(tls_flow_from_stream_with_execution(
1209        tls, connection, execution, chunk_size,
1210    ))
1211}
1212
1213async fn accept_tls_on_execution(
1214    tcp: TcpStream,
1215    acceptor: TlsAcceptor,
1216    execution: &async_carrier::ShardedTokioCarrierExecution,
1217) -> StreamResult<tokio_rustls::server::TlsStream<TcpStream>> {
1218    enum AcceptedTcp {
1219        Tokio(TcpStream),
1220        Std(std::net::TcpStream),
1221    }
1222
1223    let tcp = if execution.is_sharded() {
1224        AcceptedTcp::Std(tcp.into_std().map_err(io_error)?)
1225    } else {
1226        AcceptedTcp::Tokio(tcp)
1227    };
1228    execution
1229        .run(async move {
1230            let tcp = match tcp {
1231                AcceptedTcp::Std(std_tcp) => TcpStream::from_std(std_tcp).map_err(io_error)?,
1232                AcceptedTcp::Tokio(tcp) => tcp,
1233            };
1234            acceptor.accept(tcp).await.map_err(io_error)
1235        })
1236        .await
1237}
1238
1239#[cfg(test)]
1240mod tests {
1241    use super::*;
1242
1243    #[test]
1244    fn accept_error_classifier_retries_interrupt_and_peer_abort() {
1245        for kind in [
1246            std::io::ErrorKind::Interrupted,
1247            std::io::ErrorKind::ConnectionAborted,
1248            std::io::ErrorKind::ConnectionReset,
1249        ] {
1250            let error = std::io::Error::from(kind);
1251            assert_eq!(classify_accept_error(&error), AcceptErrorDecision::Retry);
1252        }
1253    }
1254
1255    #[test]
1256    fn accept_error_classifier_rejects_fatal_errors() {
1257        let error = std::io::Error::from(std::io::ErrorKind::AddrInUse);
1258        assert_eq!(classify_accept_error(&error), AcceptErrorDecision::Fatal);
1259    }
1260
1261    #[cfg(target_os = "linux")]
1262    #[test]
1263    fn accept_error_classifier_backs_off_resource_pressure() {
1264        for errno in [12, 23, 24, 105] {
1265            let error = std::io::Error::from_raw_os_error(errno);
1266            assert_eq!(
1267                classify_accept_error(&error),
1268                AcceptErrorDecision::Backoff(TRANSIENT_ACCEPT_BACKOFF),
1269                "errno {errno}"
1270            );
1271        }
1272    }
1273}