plane-dynamic-proxy 0.5.5

Dynamic proxy crate for Plane
Documentation
use bytes::Bytes;
use common::cert::StaticCertificateResolver;
use hyper::StatusCode;
use plane_dynamic_proxy::body::to_simple_body;
use plane_dynamic_proxy::server::HttpsConfig;
use plane_dynamic_proxy::server::SimpleHttpServer;
use std::convert::Infallible;
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tokio::time::Duration;

mod common;

// Ref: https://github.com/hyperium/hyper-util/blob/master/examples/server_graceful.rs

#[tokio::test]
async fn test_graceful_shutdown_https() {
    // Set up HTTPS configuration
    let resolver = StaticCertificateResolver::new();
    let cert = resolver.certificate();
    let hostname = resolver.hostname();

    // Start the server
    let addr = SocketAddr::from(([127, 0, 0, 1], 0));
    let listener = TcpListener::bind(addr).await.unwrap();
    let addr = listener.local_addr().unwrap();
    let server = SimpleHttpServer::new(
        hyper::service::service_fn(|_| async move {
            tokio::time::sleep(Duration::from_secs(1)).await; // emulate slow request
            let body = http_body_util::Full::<Bytes>::from("Hello, world!".to_owned());
            let body = to_simple_body(body);
            Ok::<_, Infallible>(hyper::Response::new(body))
        }),
        listener,
        HttpsConfig::from_resolver(resolver),
    )
    .unwrap();

    let url = format!("https://{}:{}", hostname, addr.port());

    // Create a client with HTTPS configuration
    let client = reqwest::Client::builder()
        .https_only(true)
        .add_root_certificate(cert)
        .resolve(&hostname, addr /* port is ignored */)
        .build()
        .unwrap();

    let response_handle = {
        let client = client.clone();
        let url = url.clone();
        tokio::spawn(async move { client.get(&url).send().await.unwrap() })
    };

    tokio::time::sleep(Duration::from_millis(600)).await;

    // Call server.graceful_shutdown()
    let shutdown_task = tokio::spawn(async move { server.graceful_shutdown().await });

    tokio::time::sleep(Duration::from_millis(200)).await;

    let response = response_handle.await.unwrap();

    // Wait for the shutdown task to complete.
    shutdown_task.await.unwrap();

    // Ensure that the result is as expected
    assert_eq!(response.status(), StatusCode::OK);
    assert_eq!(response.text().await.unwrap(), "Hello, world!");

    // Attempt to make another request, which should fail due to the server shutting down
    let result = client.get(&url).send().await;
    assert!(result.is_err());
}