hyper-openssl 0.7.1

Hyper TLS support via OpenSSL
Documentation
use futures::stream::Stream;
use hyper::client::HttpConnector;
use hyper::{Body, Client};
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
use std::thread;
use tokio::runtime::current_thread::Runtime;

use super::*;

#[test]
#[cfg(feature = "runtime")]
fn google() {
    let ssl = HttpsConnector::new(4).unwrap();
    let client = Client::builder().keep_alive(false).build::<_, Body>(ssl);

    let mut runtime = Runtime::new().unwrap();

    let f = client
        .get("https://www.google.com".parse().unwrap())
        .and_then(|resp| {
            assert!(resp.status().is_success(), "{}", resp.status());
            resp.into_body().for_each(|_| Ok(()))
        });
    runtime.block_on(f).unwrap();

    let f = client
        .get("https://www.google.com".parse().unwrap())
        .and_then(|resp| {
            assert!(resp.status().is_success(), "{}", resp.status());
            resp.into_body().for_each(|_| Ok(()))
        });
    runtime.block_on(f).unwrap();

    let f = client
        .get("https://www.google.com".parse().unwrap())
        .and_then(|resp| {
            assert!(resp.status().is_success(), "{}", resp.status());
            resp.into_body().for_each(|_| Ok(()))
        });
    runtime.block_on(f).unwrap();
}

#[test]
fn localhost() {
    let listener = ::std::net::TcpListener::bind("127.0.0.1:15410").unwrap();
    let port = listener.local_addr().unwrap().port();

    let mut ctx = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap();
    #[cfg(ossl111)]
    {
        ctx.clear_options(openssl::ssl::SslOptions::NO_TLSV1_3);
    }

    ctx.set_session_id_context(b"test").unwrap();
    ctx.set_certificate_chain_file("test/cert.pem").unwrap();
    ctx.set_private_key_file("test/key.pem", SslFiletype::PEM)
        .unwrap();
    let ctx = ctx.build();

    let thread = thread::spawn(move || {
        for _ in 0..3 {
            let stream = listener.accept().unwrap().0;
            let mut stream = ctx.accept(stream).unwrap();
            stream.read_exact(&mut [0]).unwrap();
            stream
                .write_all(b"HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n")
                .unwrap();
            stream.shutdown().unwrap();
        }
    });

    let mut connector = HttpConnector::new(1);
    connector.enforce_http(false);
    let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap();
    ssl.set_ca_file("test/cert.pem").unwrap();

    #[cfg(ossl111)]
    {
        use std::fs::File;
        use std::io::Write;

        let file = File::create("target/keyfile.log").unwrap();
        ssl.set_keylog_callback(move |_, line| {
            let _ = writeln!(&file, "{}", line);
        });
    }

    let ssl = HttpsConnector::with_connector(connector, ssl).unwrap();
    let client = Client::builder().build::<_, Body>(ssl);

    let mut runtime = Runtime::new().unwrap();

    for _ in 0..3 {
        let f = client
            .get(format!("https://localhost:{}", port).parse().unwrap())
            .and_then(|resp| {
                assert!(resp.status().is_success(), "{}", resp.status());
                resp.into_body().for_each(|_| Ok(()))
            });
        runtime.block_on(f).unwrap();
    }

    thread.join().unwrap();
}

#[test]
#[cfg(ossl102)]
fn alpn_h2() {
    use futures::future;
    use hyper::server::conn::Http;
    use hyper::service;
    use hyper::Response;
    use openssl::ssl::{self, AlpnError};
    use tokio::net::TcpListener;
    use tokio_openssl::SslAcceptorExt;

    let mut listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
    let port = listener.local_addr().unwrap().port();

    let mut ctx = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap();
    ctx.set_certificate_chain_file("test/cert.pem").unwrap();
    ctx.set_private_key_file("test/key.pem", SslFiletype::PEM)
        .unwrap();
    ctx.set_alpn_select_callback(|_, client| {
        ssl::select_next_proto(b"\x02h2", client).ok_or(AlpnError::NOACK)
    });
    let ctx = ctx.build();

    let server = future::poll_fn(move || listener.poll_accept())
        .map_err(|e| panic!("tcp accept error: {}", e))
        .and_then(move |(stream, _)| ctx.accept_async(stream))
        .map_err(|e| panic!("tls accept error: {}", e))
        .and_then(|stream| {
            assert_eq!(
                stream.get_ref().ssl().selected_alpn_protocol().unwrap(),
                b"h2"
            );
            Http::new().http2_only(true).serve_connection(
                stream,
                service::service_fn_ok(|_| Response::new(Body::empty())),
            )
        })
        .map(|_| ())
        .map_err(|e| panic!("http error: {}", e));

    let mut runtime = Runtime::new().unwrap();
    runtime.spawn(server);

    let mut connector = HttpConnector::new(1);
    connector.enforce_http(false);
    let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap();
    ssl.set_ca_file("test/cert.pem").unwrap();
    ssl.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap();

    let ssl = HttpsConnector::with_connector(connector, ssl).unwrap();
    let client = Client::builder().build::<_, Body>(ssl);

    let f = client
        .get(format!("https://localhost:{}", port).parse().unwrap())
        .and_then(|resp| {
            assert!(resp.status().is_success(), "{}", resp.status());
            resp.into_body().for_each(|_| Ok(()))
        });
    runtime.block_on(f).unwrap();
    drop(client);

    runtime.run().unwrap();
}