AkurAI Build
Menu

Public repository / master

AkurAI-Framework / crates/http/tests/middleware.rs

Latest change 556d41481651d98d322f87107309ae80f6f96580 - feat(http): middleware chain + built-ins by Ólafur Búi Ólafsson

//! End-to-end: a real `Server` driven with `run_with`, exercising the
//! middleware chain over a real `TcpStream` client (mirrors `server.rs` tests).

use akurai_http::{MiddlewareStack, Reply, Request, Response, SecurityHeaders, Server, Timing};
use std::io::{Read, Write};
use std::net::TcpStream;

/// Send one request and read the full response as a string.
fn roundtrip(addr: std::net::SocketAddr, raw: &str) -> String {
    let mut conn = TcpStream::connect(addr).unwrap();
    conn.write_all(raw.as_bytes()).unwrap();
    let mut resp = String::new();
    conn.read_to_string(&mut resp).unwrap();
    resp
}

#[test]
fn stack_adds_headers_and_can_short_circuit() {
    let server = Server::bind("127.0.0.1:0").unwrap();
    let addr = server.local_addr().unwrap();

    // SecurityHeaders outermost (so it stamps even short-circuited replies),
    // then a block gate, then timing innermost — all wrapping the handler.
    let block = |req: &Request, next: &dyn Fn(&Request) -> Reply| {
        if req.path == "/blocked" {
            return Reply::Response(Response::new(403).with_text("blocked"));
        }
        next(req)
    };
    let stack = MiddlewareStack::new()
        .push(SecurityHeaders)
        .push(block)
        .push(Timing);

    std::thread::spawn(move || {
        server
            .run_with(stack, |req: &Request| {
                Response::ok().with_text(&format!("path={}", req.path))
            })
            .unwrap();
    });

    // A normal request: handler runs, post-processed by SecurityHeaders + Timing.
    let ok = roundtrip(addr, "GET /hello HTTP/1.1\r\nHost: x\r\n\r\n");
    assert!(ok.starts_with("HTTP/1.1 200 OK\r\n"), "got: {ok:?}");
    assert!(
        ok.contains("X-Content-Type-Options: nosniff\r\n"),
        "got: {ok:?}"
    );
    assert!(ok.contains("X-Frame-Options: DENY\r\n"));
    assert!(ok.contains("Referrer-Policy: no-referrer\r\n"));
    assert!(ok.contains("X-Response-Time-Us: "), "got: {ok:?}");
    assert!(ok.ends_with("path=/hello"));

    // A blocked request: the block middleware short-circuits before the handler,
    // but the still-outer SecurityHeaders post-processing applies on the way out.
    let blocked = roundtrip(addr, "GET /blocked HTTP/1.1\r\nHost: x\r\n\r\n");
    assert!(
        blocked.starts_with("HTTP/1.1 403 Forbidden\r\n"),
        "got: {blocked:?}"
    );
    assert!(
        blocked.contains("X-Frame-Options: DENY\r\n"),
        "got: {blocked:?}"
    );
    assert!(blocked.ends_with("blocked"));
}