doido-controller 0.1.0

Action Controller + routing + Tower middleware for Doido: handlers, Context, responses, filters, routes! DSL, sessions, and middleware stacks.
Documentation
use axum::{routing::get, Router};
use doido_controller::MiddlewareStack;
use http::{Request, StatusCode};
use tower::ServiceExt;

fn app(hosts: &[&str]) -> Router {
    let stack =
        MiddlewareStack::new().with_allowed_hosts(hosts.iter().map(|h| h.to_string()).collect());
    stack.apply(Router::new().route("/", get(|| async { "ok" })))
}

async fn status_for(hosts: &[&str], host_header: Option<&str>) -> StatusCode {
    let mut b = Request::builder().uri("/");
    if let Some(h) = host_header {
        b = b.header("host", h);
    }
    let req = b.body(axum::body::Body::empty()).unwrap();
    app(hosts).oneshot(req).await.unwrap().status()
}

#[tokio::test]
async fn allowed_host_passes() {
    assert_eq!(
        status_for(&["example.com"], Some("example.com")).await,
        StatusCode::OK
    );
}

#[tokio::test]
async fn allowed_host_ignores_port() {
    assert_eq!(
        status_for(&["example.com"], Some("example.com:3000")).await,
        StatusCode::OK
    );
}

#[tokio::test]
async fn disallowed_host_is_forbidden() {
    assert_eq!(
        status_for(&["example.com"], Some("evil.test")).await,
        StatusCode::FORBIDDEN
    );
}

#[tokio::test]
async fn empty_allowlist_permits_any_host() {
    assert_eq!(status_for(&[], Some("anything.test")).await, StatusCode::OK);
}