rama 0.3.0

modular service framework
Documentation
use std::time::Duration;

use rama::{
    Layer, Service,
    http::{
        Body, BodyExtractExt, Request, Version, client::EasyHttpWebClient,
        layer::trace::TraceLayer, server::HttpServer, service::web::WebService,
    },
    net::address::SocketAddress,
    rt::Executor,
    telemetry::tracing::subscriber::{
        self, EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt,
    },
};

use turmoil::{Builder, ToSocketAddrs};

use crate::{stream::TcpStream, types::TurmoilTcpConnector};

const ADDRESS: SocketAddress = SocketAddress::default_ipv4(62004);

fn setup_tracing() {
    subscriber::registry()
        .with(fmt::layer().with_test_writer())
        .with(
            EnvFilter::try_from_env("RUST_LOG")
                .unwrap_or_else(|_| EnvFilter::new("turmoil=trace,info")),
        )
        .init();
}

async fn start_server(
    address: impl ToSocketAddrs,
) -> Result<(), Box<dyn std::error::Error + 'static>> {
    let listener = turmoil::net::TcpListener::bind(address).await?;

    let conn_result = tokio::time::timeout(Duration::from_secs(1), listener.accept()).await?;

    let (conn, _) = conn_result?;
    let conn = TcpStream::new(conn);

    let server = HttpServer::new_http1(Executor::default());
    server
        .serve(
            conn,
            TraceLayer::new_for_http()
                .into_layer(WebService::default().with_get("/", "Hello, World")),
        )
        .await
        .expect("serving endpoint");
    Ok(())
}

async fn run_client(address: impl Into<SocketAddress>) -> Result<(), Box<dyn std::error::Error>> {
    let client = TraceLayer::new_for_http().into_layer(
        EasyHttpWebClient::connector_builder()
            .with_custom_transport_connector(TurmoilTcpConnector)
            .without_dns_connector()
            .without_tls_proxy_support()
            .without_proxy_support()
            .without_tls_support()
            .with_default_http_connector(Executor::default())
            .build_client(),
    );

    let resp = client
        .serve(
            Request::builder()
                .uri(format!("http://{address}/", address = address.into()))
                .method("GET")
                .body(Body::empty())
                .unwrap(),
        )
        .await
        .map_err(|err| err.to_string())?;
    assert!(resp.status().is_success());
    assert_eq!(resp.version(), Version::HTTP_11);
    let body = resp.try_into_string().await.unwrap();
    assert_eq!(body, "Hello, World");
    Ok(())
}

#[test]
fn http_1_client_server_it() {
    setup_tracing();

    let mut sim = Builder::new().enable_tokio_io().build();

    sim.host(ADDRESS.ip_addr, || start_server(ADDRESS.to_string()));

    sim.client("client", run_client(ADDRESS));

    sim.run().expect("Error during simulation");
}