xocomil 0.3.0

A lightweight, zero-allocation HTTP/1.1 request parser and response writer
Documentation
#![deny(unsafe_op_in_unsafe_fn)]

//! # xocomil
//!
//! A lightweight, zero-allocation HTTP/1.1 request parser and response writer.
//!
//! Supports the standard HTTP methods: GET, HEAD, POST, PUT, DELETE, PATCH,
//! and OPTIONS. Request parsing borrows directly from the caller's read
//! buffer, and response headers are serialized to a stack buffer — no heap
//! allocations on the hot path.
//!
//! ## Unsupported methods
//!
//! CONNECT and TRACE are intentionally omitted:
//!
//! - **TRACE** enables cross-site tracing (XST) attacks, allowing an
//!   attacker to steal credentials from `Authorization` or `Cookie`
//!   headers reflected in the response body. Most production servers
//!   disable it.
//! - **CONNECT** requests proxy tunneling, which is out of scope for a
//!   request parser — it changes the connection semantics entirely
//!   (the server becomes a TCP relay).
//!
//! If you need either method, parse the method bytes directly from the
//! raw request line.
//!
//! ## Parsing requests
//!
//! ```
//! use xocomil::request::Request;
//! use xocomil::headers::RequestHeader;
//!
//! let raw = b"GET /index.html HTTP/1.1\r\nHost: example.com\r\n\r\n";
//! let req: Request = Request::parse(raw).unwrap();
//!
//! assert_eq!(req.path(), b"/index.html");
//!
//! // Type-safe header lookup (zero-cost, static dispatch)
//! assert_eq!(req.header(RequestHeader::Host), Some(&b"example.com"[..]));
//!
//! // Convert to &str on demand
//! assert_eq!(req.header_str("Host").unwrap(), Some("example.com"));
//! ```
//!
//! ## Writing responses
//!
//! ```
//! use xocomil::headers::{StatusCode, ResponseHeader};
//! use xocomil::response::Response;
//!
//! let mut output = Vec::new();
//!
//! Response::<16>::new(StatusCode::Ok)
//!     .header(ResponseHeader::ContentType, b"text/plain").unwrap()
//!     .write(&mut output, b"hello")
//!     .unwrap();
//!
//! let response = String::from_utf8(output).unwrap();
//! assert!(response.starts_with("HTTP/1.1 200 OK\r\n"));
//! assert!(response.ends_with("hello"));
//! ```

pub mod ascii;
pub mod body;
pub(crate) mod bytes;
pub mod error;
pub mod headers;
pub mod media;
pub mod pct;
pub mod query;
pub mod request;
pub mod response;
pub(crate) mod scan;
pub(crate) mod simd;
pub(crate) mod tchar;
pub(crate) mod validate;

pub use error::{
    BodyErrorKind, Error, MediaErrorKind, ParseErrorKind, PctErrorKind, ReadError,
    ResponseErrorKind,
};

// Static assertions: all public types are Send + Sync.
const _: () = {
    const fn assert_send_sync<T: Send + Sync>() {}
    #[allow(unused)]
    const fn assertions() {
        assert_send_sync::<Error>();
        assert_send_sync::<ParseErrorKind>();
        assert_send_sync::<BodyErrorKind>();
        assert_send_sync::<PctErrorKind>();
        assert_send_sync::<MediaErrorKind>();
        assert_send_sync::<media::MediaType<'_>>();
        assert_send_sync::<ReadError>();
        assert_send_sync::<request::Request<'_>>();
        assert_send_sync::<request::ReadRequest<'_>>();
        assert_send_sync::<response::Response<'_>>();
        assert_send_sync::<body::RequestBody<'_>>();
        assert_send_sync::<body::BodyKind>();
        assert_send_sync::<headers::Method>();
        assert_send_sync::<headers::HttpVersion>();
        assert_send_sync::<headers::Header<'_>>();
        assert_send_sync::<headers::StatusCode>();
    }
};