webio 0.9.6-alpha

A minimalist, high-performance Rust web framework built with a zero-dependency philosophy for maximum speed and low memory footprint.
Documentation
//! # WebIO Core Engine
//! 
//! This module defines the foundational primitives for the WebIO high-performance 
//! web framework. It is engineered to provide a zero-dependency, minimalist 
//! alternative to heavy async runtimes, focusing on **Computational Integrity** 
//! and **Memory Predictability**.
//! 
//! ### Key Architectural Pillars:
//! * **Deterministic Concurrency:** Utilizes a "Thread-per-Request" model with 
//!   OS-level pre-emption to prevent event-loop starvation during heavy math.
//! * **Zero-RAM Ingestion:** Provides O(1) memory complexity for multi-gigabyte 
//!   data streams via direct `TcpStream` hijacking.
//! * **Zero-Dependency Philosophy:** Built strictly on the Rust Standard Library (`std`) 
//!   to ensure rapid compilation and a transparent security audit trail.

use std::collections::HashMap;
use std::io::{Read, Write, BufWriter};
use std::net::TcpStream;
use std::pin::{Pin, pin};
use std::future::Future;
use std::task::{Context, Poll, Waker};
use std::hint;
use std::thread;

// --- STREAMING ARCHITECTURE ---

/// A specialized trait for asynchronous data delivery, optimized for zero-copy 
/// and low-memory footprints in High-Integrity Computing environments.
/// 
/// `BodyStream` is the core mechanism that allows WebIO to serve multi-gigabyte 
/// datasets (e.g., large CSVs, models, or video streams) while maintaining a 
/// constant, predictable RAM overhead. By breaking payloads into discrete 
/// chunks, the engine avoids the "RAM Spike" common in traditional frameworks 
/// that buffer entire responses before transmission.
pub trait BodyStream: Send {
    /// Retrieves the next available data fragment for the HTTP/1.1 Chunked 
    /// Transfer Encoding lifecycle. 
    /// 
    /// * **Returns `Some(Vec<u8>)`:** Indicates a valid data chunk is ready to 
    ///   be written to the socket.
    /// * **Returns `None`:** Indicates the stream is exhausted, signaling 
    ///   the engine to send the final 0-length termination chunk.
    fn next_chunk(&mut self) -> Option<Vec<u8>>; 
}

/// A standard implementation for static, in-memory payloads.
/// 
/// This implementation allows standard byte vectors to be treated as streams. 
/// Utilizing `std::mem::take`, it ensures that the data is moved efficiently 
/// to the network writer without redundant heap allocations.
impl BodyStream for Vec<u8> {
    fn next_chunk(&mut self) -> Option<Vec<u8>> {
        if self.is_empty() { 
            None 
        } else {
            // Memory Optimization: Swap the internal vector with an empty one 
            // to transfer ownership to the caller in O(1) time.
            Some(std::mem::take(self)) 
        }
    }
}

// --- DATA TRANSFORMATION TRAITS --

/// A polymorphic conversion trait designed to normalize heterogeneous data 
/// sources into a unified byte representation. 
/// 
/// `IntoBytes` serves as the foundational abstraction layer for the WebIO 
/// response engine. It enables the builder-pattern `.body()` method to 
/// accept a variety of common Rust types—such as heap-allocated strings, 
/// static string slices, or raw byte vectors—without requiring the 
/// developer to perform manual transformations. 
/// 
/// By abstracting these conversions, WebIO maintains a clean, high-level API 
/// while ensuring that the underlying **Computing Science** logic receives 
/// a consistent binary stream for transmission.
pub trait IntoBytes {
    /// Consumes the source value and transforms it into a standard byte vector.
    fn into_bytes(self) -> Vec<u8>; 
}

/// Specialized implementation for heap-allocated strings.
/// This utilizes the standard library's zero-copy conversion where possible.
impl IntoBytes for String { 
    fn into_bytes(self) -> Vec<u8> { self.into_bytes() } 
}

/// Implementation for static or borrowed string slices.
/// Performs a deterministic allocation to create an owned byte representation 
/// suitable for the asynchronous response lifecycle.
impl IntoBytes for &str { 
    fn into_bytes(self) -> Vec<u8> { self.as_bytes().to_vec() } 
}

/// Identity implementation for pre-formatted binary data.
/// This allows direct, zero-overhead passing of binary datasets, 
/// such as serialized models or image fragments.
impl IntoBytes for Vec<u8> { 
    fn into_bytes(self) -> Vec<u8> { self } 
}

/// Represents an incoming HTTP/WebSocket request.
/// 
/// Request metadata (method, path, headers) and a 10MB safety-limited body 
/// are pre-parsed into memory. For large datasets exceeding the RAM limit, 
/// the raw `stream` field provides direct access to the socket.
pub struct Req {
    /// HTTP method (e.g., "GET", "POST").
    pub method: String,
    /// Full request path (e.g., "/user/123?query=true").
    pub path: String,
    /// Parsed request body (limited to 10MB to prevent RAM exhaustion).
    pub body: String,
    /// Metadata headers mapped from the raw HTTP text.
    pub headers: HashMap<String, String>,
    /// Raw socket access for Zero-RAM Big Data streaming.
    /// Enables handling multi-gigabyte uploads without RAM bloat.
    pub stream: TcpStream,
}

// --- REQUEST IMPLEMENTATION ---

impl Req {
    /// Parses the request body as a URL-encoded form (application/x-www-form-urlencoded).
    /// 
    /// This utility provides rapid, deterministic access to key-value pairs submitted via 
    /// HTML POST forms or serialized metadata strings. By utilizing an internal 
    /// parsing logic with `replace` for standard '+' space decoding, it maintains 
    /// high efficiency for typical metadata ingestion tasks.
    pub fn form(&self) -> HashMap<String, String> {
        let mut map = HashMap::new();
        for pair in self.body.split('&') {
            let mut kv = pair.split('=');
            if let (Some(k), Some(v)) = (kv.next(), kv.next()) {
                // Space decoding per HTTP standards
                let decoded_v = v.replace('+', " "); 
                map.insert(k.to_string(), decoded_v);
            }
        }
        map
    }

    /// Performs Zero-RAM Big Data ingestion by streaming the raw socket data directly 
    /// to the filesystem.
    /// 
    /// This method is the primary mechanism for handling massive datasets (e.g., multi-gigabyte 
    /// CSVs, models, or video files) without triggering the 10MB RAM safety guard. 
    /// Utilizing a high-throughput 64KB buffer and a `BufWriter`, it ensures maximum 
    /// I/O performance while maintaining a constant, negligible memory footprint. 
    /// 
    /// ### Reliability:
    /// It returns the total number of bytes successfully written, providing 
    /// a precise integrity check for high-concurrency data science pipelines.
    pub fn save_to_file(&mut self, path: &str) -> std::io::Result<u64> {
        let file = std::fs::File::create(path)?;
        let mut writer = BufWriter::new(file);
        let mut buffer = [0; 65536]; // Optimized 64KB fragment
        let mut total = 0;

        while let Ok(n) = self.stream.read(&mut buffer) {
            if n == 0 { break; }
            writer.write_all(&buffer[..n])?;
            total += n as u64;
        }
        writer.flush()?;
        Ok(total)
    }

    /// Upgrades the active HTTP connection to a persistent WebSocket.
    /// 
    /// This method facilitates the transition from the standard request-response 
    /// lifecycle to a full-duplex, persistent communication state (RFC 6455). 
    /// By hijacking the existing `TcpStream`, it allows handlers to establish 
    /// low-latency, real-time data feeds essential for live computational 
    /// monitoring or interactive collaborative tools.
    /// 
    /// ### Lifecycle:
    /// Upon a successful handshake (101 Switching Protocols), the handler 
    /// gains exclusive ownership of the socket, effectively isolating the 
    /// WebSocket thread from the general HTTP routing engine.
    pub fn upgrade_websocket(&self) -> Option<TcpStream> {
        let key = self.headers.get("sec-websocket-key")?;
        
        // RFC 6455 Cryptographic Handshake Logic
        let accept_key = crate::utils::derive_websocket_accept(key); 

        let response = format!(
            "HTTP/1.1 101 Switching Protocols\r\n\
             Upgrade: websocket\r\n\
             Connection: Upgrade\r\n\
             Sec-WebSocket-Accept: {}\r\n\r\n",
            accept_key
        );

        let mut s = self.stream.try_clone().ok()?;
        let _ = s.write_all(response.as_bytes());
        Some(s)
    }
}

//// A collection of dynamic URL segments extracted from the request path.
/// 
/// If a route is defined as `/user/<id>`, and the request is `/user/42`, 
/// the Params will contain `{"id": "42"}`.
pub struct Params(pub HashMap<String, String>);

/// Standard HTTP Status Codes. Using a `u16` representation ensures
/// compatibility with the HTTP protocol while providing type-safe common codes.
#[derive(Copy, Clone)]
#[repr(u16)]
pub enum StatusCode {
    // 1xx: Informational
    Continue = 100,
    SwitchingProtocols = 101,
    Processing = 102,
    EarlyHints = 103,
    // 2xx: Success
    Ok = 200,
    Created = 201,
    Accepted = 202,
    NonAuthoritativeInformation = 203,
    NoContent = 204,
    ResetContent = 205,
    PartialContent = 206,
    MultiStatus = 207,
    AlreadyReported = 208,
    IMUsed = 226,
    // 3xx: Redirection
    MultipleChoices = 300,
    MovedPermanently = 301,
    Found = 302,
    SeeOther = 303,
    NotModified = 304,
    UseProxy = 305,
    TemporaryRedirect = 307,
    PermanentRedirect = 308,
    // 4xx: Client Error
    BadRequest = 400,
    Unauthorized = 401,
    PaymentRequired = 402,
    Forbidden = 403, 
    NotFound = 404,
    MethodNotAllowed = 405,
    NotAcceptable = 406,
    ProxyAuthenticationRequired = 407,
    RequestTimeout = 408,
    Conflict = 409,
    Gone = 410,
    LengthRequired = 411,
    PreconditionFailed = 412,
    PayloadTooLarge = 413,
    URITooLong = 414,
    UnsupportedMediaType = 415,
    RangeNotSatisfiable = 416,
    ExpectationFailed = 417,
    ImATeapot = 418,
    MisdirectedRequest = 421,
    UnprocessableEntity = 422,
    Locked = 423,
    FailedDependency = 424,
    UpgradeRequired = 426,
    PreconditionRequired = 428,
    TooManyRequests = 429,
    RequestHeaderFieldsTooLarge = 431,
    UnavailableForLegalReasons = 451,
    // 5xx: Server Error
    InternalServerError = 500,
    NotImplemented = 501,
    BadGateway = 502,
    ServiceUnavailable = 503,
    GatewayTimeout = 504,
    HTTPVersionNotSupported = 505,
    VariantAlsoNegotiates = 506,
    InsufficientStorage = 507,
    LoopDetected = 508,
    NotExtended = 510,
    NetworkAuthenticationRequired = 511,
}

/// The outgoing HTTP response. 
/// Uses a `Box<dyn BodyStream>` to allow for flexible, memory-efficient body types.
pub struct Reply {
    pub status: u16,
    pub headers: HashMap<String, String>,
    pub body: Box<dyn BodyStream>,
}

impl Reply {
    /// Creates a new response with a specific status code.
    pub fn new(status: StatusCode) -> Self {
        Self { status: status as u16, headers: HashMap::new(), body: Box::new(Vec::new()) }
    }

    /// Builder pattern method to add headers to the response.
    pub fn header(mut self, key: &str, value: &str) -> Self {
        self.headers.insert(key.to_string(), value.to_string());
        self
    }

    /// Sets the response body. Accepts any type implementing [`IntoBytes`].
    pub fn body<T: IntoBytes>(mut self, data: T) -> Self {
        self.body = Box::new(data.into_bytes());
        self
    }
}

/// Type alias for a pinned, thread-safe future. 
/// Necessary for handling async route logic without an external runtime.
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

/// Represents a route handler. 
/// Receives the Request and Path Params, returning an async Response.
pub type Handler = Box<dyn Fn(Req, Params) -> BoxFuture<'static, Reply> + Send + Sync>;

/// Middleware signature for early-exit logic (e.g., Auth or Logging).
pub type Middleware = Box<dyn Fn(&str) -> Option<Reply> + Send + Sync>;

// --- THE SAFE-TURBO EXECUTOR ---

/// Drives a [`Future`] to completion by blocking the current OS thread.
///
/// As a high-performance, zero-dependency blocking executor, `block_on` (formerly `launch`)
/// serves as the primary entry point for bridging the synchronous world of OS threads
/// into the asynchronous logic of WebIO. It is engineered to maintain ultra-low-latency 
/// performance, frequently achieving response times in the **70µs - 400µs** range.
///
/// ### Hybrid Spin-Wait Strategy:
/// To maintain **sub-100µs** response times essential for Data Science and Big Data 
/// throughput, the executor employs a two-phase polling strategy:
/// 
/// 1. **Spin-Phase (150,000 cycles):** Utilizes [`std::hint::spin_loop()`] to stay 
///    "on-core." This bypasses OS scheduler latency by catching I/O ready states in 
///    nanoseconds, ensuring the CPU remains dedicated to the task during brief stalls.
/// 2. **Yield-Phase:** If the future remains `Pending` after the spin budget, the 
///    executor calls [`std::thread::yield_now()`]. This prevents 100% CPU starvation 
///    during long-running stalls, allowing the Kernel to rebalance thread priorities.
///
/// ### Evolution: From Unsafe to Safe-Turbo:
/// Originally utilizing `unsafe` blocks for stack pinning and `RawWakerVTable` 
/// construction, the current implementation leverages modern, zero-cost abstractions 
/// from the Rust Standard Library:
/// - **Modern Wakers:** Employs [`Waker::noop()`] (Rust 1.77+), the most efficient 
///   possible "do-nothing" waker available.
/// - **Pinned Stability:** Uses [`std::pin::pin!`] to satisfy the pinning contract 
///   entirely within the safe-type system, eliminating Undefined Behavior (UB).
///
/// ### Zero-Dependency Philosophy:
/// By strictly utilizing `std` primitives, WebIO avoids the heavy binary footprint 
/// and complex task-stealing overhead of external runtimes like Tokio, making it 
/// ideal for high-integrity **Computational Science** microservices.
pub fn block_on<F: Future>(future: F) -> F::Output {
    // Pin the future to the stack safely using the standard macro.
    let mut future = pin!(future);
    
    // Create a zero-overhead waker for the polling context.
    let waker = Waker::noop(); 
    let mut cx = Context::from_waker(waker);
    
    let mut spins = 0u64;
    loop {
        match future.as_mut().poll(&mut cx) {
            Poll::Ready(v) => return v,
            Poll::Pending => {
                // Processor-level hint to keep the CPU core active.
                if spins < 150_000 { 
                    hint::spin_loop(); 
                    spins += 1;
                } else {
                    // Fallback to the OS scheduler to maintain system fairness.
                    thread::yield_now();
                    spins = 0;
                }
            }
        }
    }
}