Skip to main content

TlsCodec

Struct TlsCodec 

Source
pub struct TlsCodec { /* private fields */ }
Expand description

Sans-IO TLS codec. Decrypts inbound bytes, encrypts outbound bytes.

Wraps a rustls ClientConnection with an API shaped for nexus-net. The codec is a pure state machine: callers drive IO and buffering; the codec only transforms bytes.

§API at a glance

Implementations§

Source§

impl TlsCodec

Source

pub fn new(config: &TlsConfig, hostname: &str) -> Result<TlsCodec, TlsError>

Create a new TLS codec for the given hostname.

The hostname is used for SNI (Server Name Indication) and certificate verification.

Source

pub fn read_tls(&mut self, src: &[u8]) -> Result<usize, TlsError>

Advance the codec by a single TLS packet step: one read + one process_new_packets pair.

Returns the number of ciphertext bytes consumed from src. The caller drains any plaintext between calls (via read_plaintext or drain_plaintext_into) — feeding more ciphertext while plaintext is queued can overflow rustls’s internal plaintext buffer. This is the canonical primitive for streaming app-data adapters (poll socket → step codec → drain plaintext → repeat).

For bounded input that fits in rustls’s plaintext queue (handshake bytes, in-memory tests), use the drain-loop helper read_and_process_tls.

§Returns

Ok(0) if src is empty, or if rustls’s deframer cannot progress on the input alone (matches Read::read idiom — the caller’s loop is responsible for detecting stuck state). Otherwise Ok(n) where n > 0 is bytes consumed (always <= src.len(); rustls’s deframer caps each call at its internal READ_SIZE).

§Errors

Any rustls error from the read or process step (alerts, decryption failures, plaintext-buffer overflow, protocol violations).

Source

pub fn read_and_process_tls(&mut self, src: &[u8]) -> Result<usize, TlsError>

Feed buffered TLS bytes through rustls in a loop until the entire slice is consumed.

Use only for bounded input that fits in rustls’s plaintext queue — in-memory tests, custom adapters that pre-buffer a known-bounded byte sequence. Do not use for streaming app data: large ciphertext slices fed without intervening plaintext drains overflow rustls’s internal plaintext buffer (received plaintext buffer full). For streaming adapters, drive read_tls step-by-step yourself.

No production callers in this crate — kept as a user-facing safety helper. The async TlsInner::connect (nexus-async-web) and sync TlsStream::connect drive their own loops over read_tls / read_tls_from. External adapter authors who pre-buffer ciphertext can reach for this helper to avoid reimplementing the consume-loop.

§Why this exists

rustls::Connection::read_tls is not guaranteed to consume the full provided slice on a single call. The naive pattern codec.read_tls(&buf)? silently drops the unconsumed tail (issue #200 — a TLS handshake against a server that splits its response into multiple records inside one TCP segment fails because the unconsumed bytes vanish). This helper encodes the correct loop so naive callers don’t reintroduce the bug.

§Returns

Ok(src.len()) when the entire slice has been consumed.

§Errors
  • TlsError::Io(InvalidData) if rustls’s deframer can’t make progress (returned 0 bytes consumed) — malformed input.
  • Any rustls error from the underlying read/process steps.
Source

pub fn read_tls_from<R>(&mut self, src: &mut R) -> Result<usize, TlsError>
where R: Read,

Drive a sync Read source: read up to rustls’s internal READ_SIZE from src, then process the records.

Equivalent to one read_tls step but pulls bytes from a Read source instead of a buffer. Returns the bytes read from src, or 0 on EOF / no bytes available. The caller’s loop handles the rest.

Source

pub fn drain_plaintext_into<P>( &mut self, sink: &mut P, ) -> Result<usize, TlsError>
where P: ParserSink,

Drain decrypted plaintext into a ParserSink.

Direct-feed path: uses BufRead::fill_buf to borrow rustls’s internal plaintext queue and copy directly into sink.spare(), skipping the intermediate &mut [u8] that the read_plaintext shape requires. Returns the number of plaintext bytes delivered.

Implements the zero-copy seam between rustls and parsers (FrameReader for WebSocket framing, ResponseReader for HTTP). Used by adapters’ WireStream::poll_fill_into to fold plaintext draining into the same call that drives ciphertext reads.

Source

pub fn read_plaintext(&mut self, dst: &mut [u8]) -> Result<usize, TlsError>

Read decrypted plaintext into a buffer (sans-IO path).

For users who want to feed bytes into FrameReader manually or use a different parser.

Source

pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<usize, TlsError>

Encrypt up to plaintext.len() bytes, returning the number of bytes actually accepted by rustls’s outbound plaintext queue.

Chunked semantics — the caller’s write_all (or equivalent) handles re-driving on partial acceptance. This is the AsyncWrite::poll_write contract: surface backpressure as a partial count, not a hard error.

§Returns

Ok(0) if rustls’s queue is full and cannot accept any bytes (caller should drain ciphertext to the socket and retry). Otherwise Ok(n) where n > 0 is plaintext bytes queued for encryption. n may be less than plaintext.len().

§Errors

Any rustls writer error other than WriteZero (which is translated to Ok(0) so callers treat queue-full as backpressure rather than a hard failure).

Source

pub fn set_buffer_limit(&mut self, limit: Option<usize>)

Set rustls’s outbound plaintext queue limit. None for unlimited (rustls accepts as much plaintext as memory allows; pair with a caller-side bound).

Default is rustls’s DEFAULT_BUFFER_LIMIT = 64 KiB. Trading workloads with small messages typically don’t need to change this. Bulk-transfer workloads (large snapshots, file uploads over TLS) may benefit from raising it to reduce drain/refill cycles in encrypt.

Source

pub fn send_close_notify(&mut self)

Queue a TLS close_notify alert.

Subsequent calls to wants_write will return true until the alert ciphertext has been written via write_tls_to.

Idempotent: rustls tracks whether close_notify has been sent and no-ops on duplicate calls.

Use in AsyncWrite::poll_shutdown (or equivalent) before closing the underlying transport. Without close_notify, the peer sees TCP FIN as a potential truncation and may error its read loop mid-stream.

Source

pub fn write_tls_to<W>(&mut self, dst: &mut W) -> Result<usize, Error>
where W: Write,

Flush encrypted bytes to a socket.

Returns the number of bytes written. Call in a loop or when wants_write returns true.

Source

pub fn is_handshaking(&self) -> bool

Whether the TLS handshake is still in progress.

Source

pub fn wants_read(&self) -> bool

Whether the codec has buffered TLS data to read.

Source

pub fn wants_write(&self) -> bool

Whether the codec has encrypted data to write.

Trait Implementations§

Source§

impl Debug for TlsCodec

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V