comprehensive_http 0.1.1

A harness for creating consistently-shaped servers will less boilerplate
Documentation
use std::net::{IpAddr, Ipv6Addr, SocketAddr, TcpListener};
use tokio::net::TcpStream;

pub(crate) fn localhost(port: u16) -> SocketAddr {
    SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), port)
}

pub(crate) fn pick_unused_port(not_this_one: Option<u16>) -> u16 {
    let start = rand::random::<u16>() & 0x7fff;
    for low_bits in start..start + 50 {
        let port = (low_bits & 0x7fff) | 0x8000;
        if let Some(banned) = not_this_one {
            if port == banned {
                continue;
            }
        }
        if TcpListener::bind(localhost(port)).is_ok() {
            return port;
        }
    }
    panic!("Cannot find a free TCP port");
}

pub(crate) async fn wait_until_serving(addr: &SocketAddr) {
    for _ in 0..50 {
        if TcpStream::connect(addr).await.is_ok() {
            break;
        }
        tokio::time::sleep(std::time::Duration::from_millis(50)).await;
    }
}

#[cfg(feature = "tls")]
pub(crate) mod tls {
    use std::sync::Arc;
    use tokio_rustls::rustls::crypto::CryptoProvider;
    use tokio_rustls::rustls::pki_types::pem::PemObject;
    use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
    use tokio_rustls::rustls::server::ClientHello;
    use tokio_rustls::rustls::sign::CertifiedKey;

    use crate::tls_testdata;

    #[derive(Debug)]
    pub(crate) struct Resolver;

    impl tokio_rustls::rustls::server::ResolvesServerCert for Resolver {
        fn resolve(&self, _client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
            let cert = CertificateDer::from_pem_slice(tls_testdata::USER1_CERT).unwrap();
            let provider = CryptoProvider::get_default().unwrap();
            let key = PrivateKeyDer::from_pem_slice(tls_testdata::USER1_KEY).unwrap();
            let key = provider.key_provider.load_private_key(key).unwrap();
            Some(Arc::new(CertifiedKey::new(vec![cert], key)))
        }
    }

    pub(crate) struct MockTlsConfig;

    impl comprehensive::Resource for MockTlsConfig {
        type Args = comprehensive::NoArgs;
        type Dependencies = comprehensive::NoDependencies;
        const NAME: &str = "Mock TlsConfig for tests";

        fn new(
            _: comprehensive::NoDependencies,
            _: comprehensive::NoArgs,
        ) -> Result<Self, Box<dyn std::error::Error>> {
            Ok(Self)
        }
    }

    impl MockTlsConfig {
        pub fn cert_resolver(&self) -> Result<Arc<Resolver>, comprehensive::ComprehensiveError> {
            Ok(Arc::new(Resolver))
        }
    }
}