ferro-lumberjack 1.0.0

Logstash Lumberjack v2 (Beats) protocol primitives: frame codec, async client, async server, TLS via rustls. Extracted from the Ferro ecosystem.
Documentation
// SPDX-License-Identifier: Apache-2.0
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]

// ---------------------------------------------------------------------------
// API stability — semver commitment (effective `v0.2.0`)
// ---------------------------------------------------------------------------
//
// From `v0.2.0` onward the public API surface re-exported below is a
// stable contract: breaking changes (renames, removals, signature
// changes that aren't strict additions) require a major-version bump
// to `1.0.0`. Minor releases (`0.2.x`) may add new items and may
// `#[deprecate]` existing ones, but will not remove them.
//
// Items NOT covered by this commitment:
//
// - Anything reachable only via `#[doc(hidden)]`.
// - Behavioural details documented as "implementation-defined"
//   (e.g. exact compaction thresholds in `FrameDecoder`).
// - Future feature-gated additions: a new optional feature may be
//   added without bumping major.
//
// See `CHANGELOG.md` for the canonical history.

mod error;
pub mod frame;
mod sequence;

#[cfg(feature = "client")]
#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
pub mod client;

#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
pub mod server;

#[cfg(feature = "tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "tls")))]
pub mod tls;

pub use error::{FrameError, ProtocolError};
pub use frame::{
    Frame, FrameDecoder, FrameType, encode_ack, encode_compressed, encode_json_frame, encode_window,
};
pub use sequence::Sequence;

/// Lumberjack v2 protocol version byte (`b'2'`).
pub const PROTOCOL_VERSION: u8 = b'2';

/// Default maximum decoded frame payload size (64 MiB).
///
/// Caps both raw frame payloads and the *decompressed* size of `C` frames,
/// to make zlib-bomb attacks O(memory-bounded) instead of unbounded. Used
/// by [`FrameDecoder::new`].
pub const DEFAULT_MAX_FRAME_PAYLOAD: usize = 64 * 1024 * 1024;

/// Default maximum number of data events the server will accumulate for a
/// single window (100 000).
///
/// A window's declared `count` is peer-supplied; without an aggregate cap a
/// malicious peer can declare a huge count and stream many small frames,
/// forcing the receiver's per-window `Vec` (and memory) to grow unboundedly
/// before the window completes. The server rejects a window whose declared
/// count, or whose observed event count mid-stream, exceeds this value with
/// [`ProtocolError::WindowTooLarge`]. Used by
/// [`server::ServerBuilder::max_window_events`].
pub const DEFAULT_MAX_WINDOW_EVENTS: usize = 100_000;

/// Default maximum total accumulated payload bytes across all events in a
/// single window (256 MiB).
///
/// Complements [`DEFAULT_MAX_WINDOW_EVENTS`]: even within the event-count
/// cap, the sum of per-event payloads is bounded so a window of moderately
/// sized events cannot exhaust memory. The server rejects a window once the
/// accumulated payload bytes exceed this value with
/// [`ProtocolError::WindowTooLarge`]. Used by
/// [`server::ServerBuilder::max_window_bytes`].
pub const DEFAULT_MAX_WINDOW_BYTES: usize = 256 * 1024 * 1024;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn default_max_frame_payload_is_exactly_64_mib() {
        // Pins the `64 * 1024 * 1024` arithmetic (lib.rs:57). Both `*`
        // operators must be multiplication:
        //   - real:            64 * 1024 * 1024 = 67_108_864
        //   - `64 + 1024*1024` =  1_048_640 (col-49 `*`→`+` mutant)
        //   - `64*1024 + 1024` =     66_560 (col-56 `*`→`+` mutant)
        // The exact-equality assertion below distinguishes all three.
        assert_eq!(DEFAULT_MAX_FRAME_PAYLOAD, 67_108_864);
        assert_eq!(DEFAULT_MAX_FRAME_PAYLOAD, 64 * 1024 * 1024);
        // Sanity: the two `+`-mutant values are NOT equal to the real one.
        assert_ne!(DEFAULT_MAX_FRAME_PAYLOAD, 64 + 1024 * 1024);
        assert_ne!(DEFAULT_MAX_FRAME_PAYLOAD, 64 * 1024 + 1024);
    }

    #[test]
    fn protocol_version_byte_is_ascii_two() {
        assert_eq!(PROTOCOL_VERSION, b'2');
    }
}