nexus_net/maybe_tls.rs
1//! Stream that may or may not be wrapped in TLS (sync only).
2//!
3//! Implements `Read + Write` by delegating to either the plain
4//! stream or the `TlsStream` wrapper (requires `tls` feature).
5//!
6//! Protocol clients use `MaybeTls<S>` as their stream type when the
7//! TLS decision happens at runtime (`ws://` vs `wss://`). For async
8//! TLS, see `nexus-async-web::maybe_tls`.
9
10use std::io::{self, Read, Write};
11
12#[cfg(feature = "tls")]
13use crate::tls::TlsStream;
14
15/// A stream that may or may not be wrapped in TLS.
16///
17/// The `Tls` variant is boxed because `TlsStream` includes rustls's
18/// ~1KB connection state. TLS connections are established once at
19/// startup — the box indirection is not on the hot path.
20pub enum MaybeTls<S> {
21 /// Plaintext stream.
22 Plain(S),
23 /// TLS-wrapped stream.
24 #[cfg(feature = "tls")]
25 Tls(Box<TlsStream<S>>),
26}
27
28impl<S> MaybeTls<S> {
29 /// Whether this is a TLS-wrapped stream.
30 pub fn is_tls(&self) -> bool {
31 #[cfg(feature = "tls")]
32 if matches!(self, Self::Tls(_)) {
33 return true;
34 }
35 false
36 }
37}
38
39// =============================================================================
40// Read + Write (blocking)
41// =============================================================================
42
43impl<S: Read + Write> Read for MaybeTls<S> {
44 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
45 match self {
46 Self::Plain(s) => s.read(buf),
47 #[cfg(feature = "tls")]
48 Self::Tls(s) => s.read(buf),
49 }
50 }
51}
52
53impl<S: Read + Write> Write for MaybeTls<S> {
54 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
55 match self {
56 Self::Plain(s) => s.write(buf),
57 #[cfg(feature = "tls")]
58 Self::Tls(s) => s.write(buf),
59 }
60 }
61
62 fn flush(&mut self) -> io::Result<()> {
63 match self {
64 Self::Plain(s) => s.flush(),
65 #[cfg(feature = "tls")]
66 Self::Tls(s) => s.flush(),
67 }
68 }
69}