hyperlane-core 22.2.0

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 http1_plaintext_keeps_the_exact_request_target_bytes() {
    let (control, port): (ServerControlHook, u16) =
        start_target_server(ServerProtocol::Http1, None)
            .await
            .expect("the http1 target server must start");
    for (name, target) in TARGET_CASES {
        let mut stream: TcpStream = connect_tcp(port).await.expect("socket must connect");
        let (head, body): (String, Vec<u8>) = http1_target_reply(&mut stream, target).await;
        assert!(
            head.starts_with("HTTP/1.1 200"),
            "{name}: the response must be framed as HTTP/1.1, got: {head}"
        );
        assert_eq!(
            response_header_value(&head, PROBE_VERSION_HEADER),
            format!("{:?}", HttpVersion::Http1_1),
            "{name}: the handler must observe the HTTP/1.1 request version"
        );
        assert_eq!(
            response_header_value(&head, TARGET_ECHO_HEADER),
            target,
            "{name}: the echoed target header must carry the exact target bytes"
        );
        assert_eq!(
            String::from_utf8_lossy(&body),
            target,
            "{name}: the echoed body must carry the exact target bytes"
        );
    }
    control.shutdown().await;
}

#[tokio::test]
async fn http1_plaintext_preserves_a_trailing_question_mark() {
    let (control, port): (ServerControlHook, u16) =
        start_target_server(ServerProtocol::Http1, None)
            .await
            .expect("the http1 target server must start");
    let mut stream: TcpStream = connect_tcp(port).await.expect("socket must connect");
    let (head, body): (String, Vec<u8>) =
        http1_target_reply(&mut stream, TRAILING_QUESTION_TARGET).await;
    assert_eq!(
        response_header_value(&head, TARGET_ECHO_HEADER),
        TRAILING_QUESTION_TARGET,
        "a trailing question mark must survive the http1 target"
    );
    assert_eq!(
        String::from_utf8_lossy(&body),
        TRAILING_QUESTION_TARGET,
        "a trailing question mark must survive the http1 target"
    );
    control.shutdown().await;
}

#[tokio::test]
async fn http1_keep_alive_next_request_replaces_the_previous_target() {
    let (control, port): (ServerControlHook, u16) =
        start_target_server(ServerProtocol::Http1, None)
            .await
            .expect("the http1 target server must start");
    let mut stream: TcpStream = connect_tcp(port).await.expect("socket must connect");
    let (first_head, first_body): (String, Vec<u8>) =
        http1_target_reply(&mut stream, TARGET_CASES[0].1).await;
    let (second_head, second_body): (String, Vec<u8>) =
        http1_target_reply(&mut stream, TARGET_CASES[3].1).await;
    assert_eq!(
        response_header_value(&first_head, TARGET_ECHO_HEADER),
        TARGET_CASES[0].1,
        "the first request must report its own target"
    );
    assert_eq!(
        response_header_value(&second_head, TARGET_ECHO_HEADER),
        TARGET_CASES[3].1,
        "the second request on the same connection must not retain the first target"
    );
    assert_eq!(String::from_utf8_lossy(&first_body), TARGET_CASES[0].1);
    assert_eq!(String::from_utf8_lossy(&second_body), TARGET_CASES[3].1);
    control.shutdown().await;
}

#[tokio::test]
async fn http2_prior_knowledge_keeps_the_exact_request_target_bytes() {
    let (control, port): (ServerControlHook, u16) =
        start_target_server(ServerProtocol::Http2, None)
            .await
            .expect("the http2 target server must start");
    let (mut sender, connection) = h2_plaintext_session(port)
        .await
        .expect("h2 prior-knowledge handshake must succeed");
    let driving: JoinHandle<()> = spawn(async move {
        let _result: Result<(), h2::Error> = connection.await;
    });
    for (name, target) in TARGET_CASES {
        let uri: String = format!("http://{TLS_SERVER_NAME}:{port}{target}");
        let (status, version, body): H2Reply = h2_request(&mut sender, "GET", &uri, &[])
            .await
            .expect("the h2 target request must complete");
        assert_eq!(status, 200, "{name}: the h2 target request must succeed");
        assert_eq!(
            version,
            http::Version::HTTP_2,
            "{name}: the response must be framed as HTTP/2"
        );
        assert_eq!(
            String::from_utf8_lossy(&body),
            target,
            "{name}: the echoed body must carry the exact target bytes"
        );
    }
    drop(sender);
    driving.abort();
    control.shutdown().await;
}

#[tokio::test]
async fn http2_prior_knowledge_preserves_a_trailing_question_mark() {
    let (control, port): (ServerControlHook, u16) =
        start_target_server(ServerProtocol::Http2, None)
            .await
            .expect("the http2 target server must start");
    let (mut sender, connection) = h2_plaintext_session(port)
        .await
        .expect("h2 prior-knowledge handshake must succeed");
    let driving: JoinHandle<()> = spawn(async move {
        let _result: Result<(), h2::Error> = connection.await;
    });
    let uri: String = format!("http://{TLS_SERVER_NAME}:{port}{TRAILING_QUESTION_TARGET}");
    let (status, headers, body): H2HeadReply =
        h2_request_with_headers(&mut sender, "GET", &uri, &[])
            .await
            .expect("the h2 target request must complete");
    assert_eq!(status, 200);
    assert_eq!(
        headers
            .get(PROBE_VERSION_HEADER)
            .and_then(|value: &http::HeaderValue| value.to_str().ok()),
        Some(format!("{:?}", HttpVersion::Http2).as_str()),
        "the handler must observe the HTTP/2 request version"
    );
    assert_eq!(
        headers
            .get(TARGET_ECHO_HEADER)
            .and_then(|value: &http::HeaderValue| value.to_str().ok()),
        Some(TRAILING_QUESTION_TARGET),
        "a trailing question mark must survive the h2 target"
    );
    assert_eq!(
        String::from_utf8_lossy(&body),
        TRAILING_QUESTION_TARGET,
        "a trailing question mark must survive the h2 target"
    );
    drop(sender);
    driving.abort();
    control.shutdown().await;
}

#[tokio::test]
async fn http2_next_stream_replaces_the_previous_target() {
    let (control, port): (ServerControlHook, u16) =
        start_target_server(ServerProtocol::Http2, None)
            .await
            .expect("the http2 target server must start");
    let (mut sender, connection) = h2_plaintext_session(port)
        .await
        .expect("h2 prior-knowledge handshake must succeed");
    let driving: JoinHandle<()> = spawn(async move {
        let _result: Result<(), h2::Error> = connection.await;
    });
    let first_uri: String = format!("http://{TLS_SERVER_NAME}:{port}{}", TARGET_CASES[0].1);
    let second_uri: String = format!("http://{TLS_SERVER_NAME}:{port}{}", TARGET_CASES[5].1);
    let (first_status, _, first_body): H2HeadReply =
        h2_request_with_headers(&mut sender, "GET", &first_uri, &[])
            .await
            .expect("the first h2 target request must complete");
    let (second_status, _, second_body): H2HeadReply =
        h2_request_with_headers(&mut sender, "GET", &second_uri, &[])
            .await
            .expect("the second h2 target request must complete");
    assert_eq!(first_status, 200);
    assert_eq!(second_status, 200);
    assert_eq!(
        String::from_utf8_lossy(&first_body),
        TARGET_CASES[0].1,
        "the first stream must report its own target"
    );
    assert_eq!(
        String::from_utf8_lossy(&second_body),
        TARGET_CASES[5].1,
        "the second stream on the same connection must not retain the first target"
    );
    drop(sender);
    driving.abort();
    control.shutdown().await;
}

#[tokio::test]
async fn http3_over_quic_keeps_the_exact_request_target_bytes() {
    let (control, port): (ServerControlHook, u16) =
        start_target_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("the http3 target server must start");
    for (name, target) in TARGET_CASES {
        let uri: String = format!("https://{TLS_SERVER_NAME}:{port}{target}");
        let (status, version, body, alpn): H3Reply = h3_request(port, "GET", &uri, &[])
            .await
            .expect("the h3 target request must complete");
        assert_eq!(status, 200, "{name}: the h3 target request must succeed");
        assert_eq!(
            version,
            http::Version::HTTP_3,
            "{name}: the response must be framed as HTTP/3"
        );
        assert_eq!(
            alpn,
            Some(ALPN_HTTP3.to_vec()),
            "{name}: the connection must carry the h3 ALPN identifier"
        );
        assert_eq!(
            String::from_utf8_lossy(&body),
            target,
            "{name}: the echoed body must carry the exact target bytes"
        );
    }
    control.shutdown().await;
}

#[tokio::test]
async fn http3_over_quic_preserves_a_trailing_question_mark() {
    let (control, port): (ServerControlHook, u16) =
        start_target_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("the http3 target server must start");
    let uri: String = format!("https://{TLS_SERVER_NAME}:{port}{TRAILING_QUESTION_TARGET}");
    let (status, headers, body, _alpn): H3HeadReply =
        h3_request_with_headers(port, "GET", &uri, &[])
            .await
            .expect("the h3 target request must complete");
    assert_eq!(status, 200);
    assert_eq!(
        headers
            .get(PROBE_VERSION_HEADER)
            .and_then(|value: &http::HeaderValue| value.to_str().ok()),
        Some(format!("{:?}", HttpVersion::Http3).as_str()),
        "the handler must observe the HTTP/3 request version"
    );
    assert_eq!(
        headers
            .get(TARGET_ECHO_HEADER)
            .and_then(|value: &http::HeaderValue| value.to_str().ok()),
        Some(TRAILING_QUESTION_TARGET),
        "a trailing question mark must survive the h3 target"
    );
    assert_eq!(
        String::from_utf8_lossy(&body),
        TRAILING_QUESTION_TARGET,
        "a trailing question mark must survive the h3 target"
    );
    control.shutdown().await;
}

#[tokio::test]
async fn http3_next_stream_on_one_connection_replaces_the_previous_target() {
    let (control, port): (ServerControlHook, u16) =
        start_target_server(ServerProtocol::Http3, Some(trusted_tls_config()))
            .await
            .expect("the http3 target server must start");
    let replies: Vec<(u16, http::Version, Vec<u8>)> =
        h3_targets_on_one_connection(port, [TARGET_CASES[0].1, TARGET_CASES[5].1])
            .await
            .expect("the h3 target requests must complete on one connection");
    assert_eq!(replies.len(), 2);
    assert_eq!(replies[0].0, 200);
    assert_eq!(replies[1].0, 200);
    assert_eq!(replies[0].1, http::Version::HTTP_3);
    assert_eq!(replies[1].1, http::Version::HTTP_3);
    assert_eq!(
        String::from_utf8_lossy(&replies[0].2),
        TARGET_CASES[0].1,
        "the first stream must report its own target"
    );
    assert_eq!(
        String::from_utf8_lossy(&replies[1].2),
        TARGET_CASES[5].1,
        "the second stream on the same connection must not retain the first target"
    );
    control.shutdown().await;
}