noxtls-io 0.2.12

Internal implementation crate for noxtls: transport traits and blocking/async TLS record I/O adapters.
Documentation
// Copyright (c) 2019-2026, Argenox Technologies LLC
// All rights reserved.
//
// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
//
// This file is part of the NoxTLS Library.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by the
// Free Software Foundation; version 2 of the License.
//
// Alternatively, this file may be used under the terms of a commercial
// license from Argenox Technologies LLC.
//
// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
// CONTACT: info@argenox.com

//! TLS record length-prefix framing helpers (transport layer, no cipher semantics).

use crate::internal_alloc::Vec;
use noxtls_core::{Error, Result};

/// Length of a TLS 1.x `TLSPlaintext` outer header (`type || version || length`).
pub const TLS_RECORD_HEADER_LEN: usize = 5;

/// Maximum TLS record payload length permitted by RFC 5246 / RFC 8446 outer length field.
pub const TLS_MAX_RECORD_PAYLOAD_LEN: usize = 1 << 14;

/// Buffers incoming TLS octets and yields complete record packets (`header + payload`).
///
/// Supports partial reads from blocking or async transports.
#[derive(Debug, Default, Clone)]
pub struct TlsRecordDeframer {
    buf: Vec<u8>,
}

impl TlsRecordDeframer {
    /// Creates an empty deframer buffer.
    #[must_use]
    pub fn noxtls_new() -> Self {
        Self { buf: Vec::new() }
    }

    /// Appends newly read transport bytes to the internal buffer.
    pub fn push(&mut self, chunk: &[u8]) {
        self.buf.extend_from_slice(chunk);
    }

    /// Returns one complete TLS record packet when `5 + length` bytes are buffered.
    ///
    /// # Errors
    ///
    /// Returns [`Error::InvalidLength`] when the length field exceeds [`TLS_MAX_RECORD_PAYLOAD_LEN`].
    pub fn pop_packet(&mut self) -> Result<Option<Vec<u8>>> {
        if self.buf.len() < TLS_RECORD_HEADER_LEN {
            return Ok(None);
        }
        let payload_len = u16::from_be_bytes([self.buf[3], self.buf[4]]) as usize;
        if payload_len > TLS_MAX_RECORD_PAYLOAD_LEN {
            return Err(Error::InvalidLength(
                "tls record payload exceeds maximum allowed length",
            ));
        }
        let total = TLS_RECORD_HEADER_LEN.saturating_add(payload_len);
        if self.buf.len() < total {
            return Ok(None);
        }
        let packet = self.buf[..total].to_vec();
        self.buf.drain(..total);
        Ok(Some(packet))
    }

    /// Returns the number of bytes currently buffered.
    #[must_use]
    pub fn buffered_len(&self) -> usize {
        self.buf.len()
    }
}