hyperlane-core 22.0.6

A lightweight, high-performance, and cross-platform Rust HTTP server library built on Tokio. It simplifies modern web service development by providing built-in support for middleware, WebSocket, Server-Sent Events (SSE), and raw TCP communication. With a unified and ergonomic API across Windows, Linux, and MacOS, it enables developers to build robust, scalable, and event-driven network applications with minimal overhead and maximum flexibility.
Documentation
use super::*;

#[tokio::test]
async fn http3_over_quic_reports_the_real_local_and_peer_addresses_of_the_connection() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    let (endpoint, connection): (quinn::Endpoint, quinn::Connection) =
        connect_quic(port, ALPN_HTTP3)
            .await
            .expect("a quic client must connect");
    let client_local: SocketAddr = endpoint
        .local_addr()
        .expect("the client endpoint must report its own udp address");
    let target: String = format!("https://{TLS_SERVER_NAME}:{port}{SOCKET_METADATA_ROUTE_PATH}");
    let (status, _headers, body, _alpn): H3HeadReply = h3_exchange_on(H3Exchange {
        connection,
        alpn: None,
        method: "GET",
        target: &target,
        body: &[],
    })
    .await
    .expect("h3 socket metadata request must complete");
    assert_eq!(status, 200);
    let text: String = String::from_utf8_lossy(&body).to_string();
    let (reported_local, reported_peer): (String, String) =
        match text.split_once(SOCKET_METADATA_SEPARATOR) {
            Some(pair) => (pair.0.to_string(), pair.1.to_string()),
            None => panic!("the route must report both addresses, got: {text:?}"),
        };
    let expected_local: SocketAddr = format!("{LOOPBACK_HOST}:{port}")
        .parse()
        .expect("the bind address must parse");
    assert_eq!(
        reported_local,
        expected_local.to_string(),
        "an h3 stream must carry the connection's real local address, not a fabricated one"
    );
    assert_eq!(
        reported_peer,
        client_local.to_string(),
        "an h3 stream must carry the connection's real peer address"
    );
    control.shutdown().await;
}

#[tokio::test]
async fn http3_streams_a_body_that_never_announced_a_content_length() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    let (status, headers, body, _alpn): H3HeadReply = h3_request_with_headers(
        port,
        "GET",
        &format!("https://{TLS_SERVER_NAME}:{port}{UNFRAMED_BODY_ROUTE_PATH}"),
        &[],
    )
    .await
    .expect("an unframed h3 body must be accepted");
    assert_eq!(status, 200);
    assert!(
        !headers.contains_key(CONTENT_LENGTH),
        "the head must not announce a length for a body framed by data frames"
    );
    assert_eq!(
        String::from_utf8_lossy(&body),
        UNFRAMED_BODY,
        "a body with no content-length must still reach the client in full"
    );
    control.shutdown().await;
}

#[tokio::test]
async fn http3_over_quic_negotiates_h3_alpn_and_tls13() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    let (status, version, body, alpn): H3Reply = h3_request(
        port,
        "GET",
        &format!("https://{TLS_SERVER_NAME}:{port}{VERSION_ROUTE_PATH}"),
        &[],
    )
    .await
    .expect("h3 request must complete");
    assert_eq!(status, 200);
    assert_eq!(
        version,
        http::Version::HTTP_3,
        "the response must be framed as HTTP/3, not downgraded"
    );
    assert_eq!(
        alpn,
        Some(ALPN_HTTP3.to_vec()),
        "QUIC must negotiate the h3 ALPN identifier"
    );
    assert_eq!(
        String::from_utf8_lossy(&body),
        format!("{:?}", HttpVersion::Http3)
    );
    control.shutdown().await;
}

#[tokio::test]
async fn http3_over_quic_reads_path_query_and_header() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    let target: String = format!(
        "https://{TLS_SERVER_NAME}:{port}{PROBE_PATH}?{}",
        PROBE_QUERY
    );
    let (status, version, _body, _alpn): H3Reply = h3_request(port, "GET", &target, &[])
        .await
        .expect("h3 probe request must complete");
    assert_eq!(status, 200);
    assert_eq!(version, http::Version::HTTP_3);
    control.shutdown().await;
}

#[tokio::test]
async fn http3_over_quic_binary_post_round_trips_hostile_bytes() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    let payload: Vec<u8> = hostile_binary_payload();
    let (status, version, body, _alpn): H3Reply = h3_request(
        port,
        "POST",
        &format!("https://{TLS_SERVER_NAME}:{port}{BINARY_ROUTE_PATH}"),
        &payload,
    )
    .await
    .expect("h3 binary post must complete");
    assert_eq!(status, 200);
    assert_eq!(version, http::Version::HTTP_3);
    assert_eq!(
        body, payload,
        "every hostile byte must survive the h3 body round trip"
    );
    control.shutdown().await;
}

#[tokio::test]
async fn http3_over_quic_delivers_a_response_above_the_flow_control_window() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    let expected: Vec<u8> = large_binary_payload();
    let (status, version, body, _alpn): H3Reply = h3_request(
        port,
        "GET",
        &format!("https://{TLS_SERVER_NAME}:{port}{LARGE_ROUTE_PATH}"),
        &[],
    )
    .await
    .expect("large h3 response must complete");
    assert_eq!(status, 200);
    assert_eq!(version, http::Version::HTTP_3);
    assert_eq!(body.len(), expected.len());
    assert!(
        body == expected,
        "the large response must be byte identical past the flow control window"
    );
    control.shutdown().await;
}

#[tokio::test]
async fn http3_over_quic_shutdown_releases_the_bound_udp_port() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    assert_wait_pending_then_complete(&control).await;
    let rebound: io::Result<UdpSocket> = UdpSocket::bind(format!("{LOOPBACK_HOST}:{port}"));
    assert!(
        rebound.is_ok(),
        "udp port {port} must be free again after shutdown and wait"
    );
}

#[tokio::test]
async fn http3_over_quic_rejects_a_client_offering_only_h2() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    let outcome: Result<(quinn::Endpoint, quinn::Connection), String> =
        connect_quic(port, ALPN_HTTP2).await;
    assert!(
        outcome.is_err(),
        "an http3 listener must refuse an h2-only QUIC client instead of downgrading"
    );
    control.shutdown().await;
}

#[tokio::test]
async fn h3_sse_chunk_reaches_the_client_before_the_handler_finishes() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    let _lease: GateLease = gate_lease(GateSlot::SseHttp3).await;
    let target: String = format!("https://{TLS_SERVER_NAME}:{port}{SSE_ROUTE_PATH_HTTP3}");
    let reading: JoinHandle<(Vec<u8>, bool)> =
        spawn(async move { h3_stream_until(port, &target, SSE_FIRST_CHUNK).await });
    route_gate(GateSlot::SseHttp3)
        .await_first_chunk_sent()
        .await;
    let (collected, matched): (Vec<u8>, bool) =
        timeout(Duration::from_secs(FAST_STREAM_BUDGET_MILLIS), reading)
            .await
            .expect("the first sse chunk must be readable while the handler is still blocked")
            .expect("the reader task must not panic");
    let text: String = String::from_utf8_lossy(&collected).to_string();
    assert!(
        matched,
        "the first sse chunk must be readable while the handler is still blocked, got: {text:?}"
    );
    assert!(
        !text.contains(SSE_SECOND_CHUNK),
        "the second chunk must wait for the handler to be released, got: {text:?}"
    );
    route_gate(GateSlot::SseHttp3).release_sse();
    control.shutdown().await;
}

#[tokio::test]
async fn http3_one_connection_answers_a_fast_stream_while_a_slow_stream_is_blocked() {
    let (control, port): (ServerControlHook, u16) =
        start_protocol_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("http3 quic server must start");
    let _lease: GateLease = gate_lease(GateSlot::SlowHttp3).await;
    let order: Arc<Mutex<Vec<&'static str>>> = Arc::new(Mutex::new(Vec::new()));
    let slow_order: Arc<Mutex<Vec<&'static str>>> = Arc::clone(&order);
    let slow: JoinHandle<Result<H3Reply, String>> = spawn(async move {
        let outcome: Result<H3Reply, String> = h3_request(
            port,
            "GET",
            &format!("https://{TLS_SERVER_NAME}:{port}{SLOW_ROUTE_PATH_HTTP3}"),
            &[],
        )
        .await;
        slow_order.lock().expect("order lock").push("slow");
        outcome
    });
    route_gate(GateSlot::SlowHttp3).await_slow_entered().await;
    let fast_order: Arc<Mutex<Vec<&'static str>>> = Arc::clone(&order);
    let fast: JoinHandle<Result<H3Reply, String>> = spawn(async move {
        let outcome: Result<H3Reply, String> = h3_request(
            port,
            "GET",
            &format!("https://{TLS_SERVER_NAME}:{port}{VERSION_ROUTE_PATH}"),
            &[],
        )
        .await;
        fast_order.lock().expect("order lock").push("fast");
        outcome
    });
    let budget: Duration = Duration::from_millis(FAST_STREAM_BUDGET_MILLIS);
    let fast_result: Result<H3Reply, String> = timeout(budget, fast)
        .await
        .expect("the fast stream must complete while the slow handler is still blocked")
        .expect("the fast stream task must not panic");
    assert!(
        fast_result.is_ok(),
        "the fast request must succeed while the slow handler is held"
    );
    assert_eq!(
        *order.lock().expect("order lock"),
        vec!["fast"],
        "only the fast stream may complete while the slow handler is blocked"
    );
    route_gate(GateSlot::SlowHttp3).release_slow();
    let slow_result: Result<H3Reply, String> =
        timeout(Duration::from_secs(REQUEST_TIMEOUT_SECS), slow)
            .await
            .expect("the released slow stream must finish inside its timeout")
            .expect("the slow stream task must not panic");
    assert!(slow_result.is_ok());
    assert_eq!(*order.lock().expect("order lock"), vec!["fast", "slow"]);
    control.shutdown().await;
}