Use hyper server with TLS

Hi,

I'm trying to update tokio-tls to be usable with latest tokio and hyper. Most of things is working , but still have some issues with hyper-server example:

pub fn main() {
    // Create our TLS context through which new connections will be
    // accepted. This is where we pass in the certificate as well to
    // send to clients.
    let der = include_bytes!("identity.p12");
    let cert = Identity::from_pkcs12(der, "mypass").unwrap();
    let tls_cx = TlsAcceptor::builder(cert).build().unwrap();

    let new_service = || service_fn_ok(|_req| Response::new(Body::from("Hello World")));

    let addr = "127.0.0.1:12345".parse().unwrap();
    let srv = TcpListener::bind(&addr).expect("Error binding local port");
    // Use lower lever hyper API to be able to intercept client connection
    let http_proto = Http::new();
    let http_server = http_proto
        .serve_incoming(
            srv.incoming().and_then(move |socket| {
                tls_cx
                    .accept_async(socket)
                    .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
            }),
            new_service,
        )
        .for_each(|conn| {
            hyper::rt::spawn(
                conn.and_then(|c| c.map_err(|e| panic!("Hyper error {}", e)))
                    .map_err(|e| eprintln!("Connection error {}", e)),
            );
            Ok(())
        });

    println!("Listening on {}", addr);

    hyper::rt::run(http_server.map_err(|e| eprintln!("Error running server: {}", e)));
}

Now I got 2 problems (maybe related) - the only way to make this compile is to use panic! in err_map. Otherwise I really cannot find appropriate mapping due to this error:

type mismatch resolving `<[closure@examples/hyper-server.rs:56:45: 56:79] as std::ops::FnOnce<(hyper::Error,)>>::Output == hyper::common::never::Never`

expected (), found enum `hyper::common::never::Never`

Above code somehow runs, but problem is when there is some error in the connection (like for instance using http:// schema in browser - then TLS handshake fails) - then severs stops responding to further connections - all connections are refused.

What could be the problem - do you see anything wrong in the code above? Any ideas?

full code for updated tokio-tls including this example is here https://github.com/izderadicka/tokio-tls/tree/new-tokio

Here is my code to make hyper 0.13.1 work with Rustls

    let addr = SocketAddr::from(([0, 0, 0, 0], port));

    // init TLS certificates
    let tls_acceptor : Option<TlsAcceptor>;

    if cert_file.len() > 0 && key_file.len() > 0 {
        // prepare SSL certificates
        let certs = load_certs(Path::new(cert_file))
            .expect(&format!("Failed to load certificate key file {}", cert_file));
        let mut keys = load_keys(Path::new(key_file))
            .expect(&format!("Failed to load SSL key file {}", key_file));

        let mut config = ServerConfig::new(NoClientAuth::new());
        config.set_single_cert(certs, keys.remove(0))
            .expect("Invalid TLS certificates"); 
        config.versions = vec![ProtocolVersion::TLSv1_3, ProtocolVersion::TLSv1_2];
        
        tls_acceptor = Some(TlsAcceptor::from(Arc::new(config)));

        info!("Starting HTTPS proxy server listening at port {} ...", port);
    } else {
        tls_acceptor = None;

        info!("Starting HTTP proxy server listening at port {} ...", port);
    }

    // listening on port
    let mut listener = TcpListener::bind(&addr).await
        .expect(&format!("HTTP proxy fails to bind at {:?}", addr));

        
    tokio::spawn(async move {
        let mut hyper_http = Http::new();
        hyper_http.http1_only(true).keep_alive(false);


        let mut incoming = listener.incoming();
        while let Some(stream) = incoming.next().await {
            match stream {
                Ok(socket) => {
                    let cloned_acceptor = tls_acceptor.clone();
                    let cloned_http = hyper_http.clone();
                    
                    tokio::spawn(async move {
                        match cloned_acceptor.as_ref() {
                            Some(acceptor) => { // https
                                match acceptor.accept(socket).await {
                                    Ok(stream) => {
                                        let conn = cloned_http.serve_connection(stream, service_fn(proxy));
                                        if let Err(e) = conn.await {
                                            warn!("Error occured when handling HTTPS connection. {:?}", e);
                                        };
                                    },
                                    Err(e) => {
                                        warn!("TLS hardshake failed. {:?}", e);
                                    }
                                }
                            },
                            None => { // http
                                let conn = cloned_http.serve_connection(socket, service_fn(proxy));
                                if let Err(e) = conn.await {
                                    warn!("Error occured when handling HTTP connection. {:?}", e);
                                };
                            }
                        };
                    });
                }
                Err(e) => { 
                    warn!("Error occured when accepting new connection. {:?}", e);
                }
            }
        }
        
    });


fn load_certs(path: &Path) -> io::Result<Vec<Certificate>> {
    certs(&mut BufReader::new(File::open(path)?))
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))
}

fn load_keys(path: &Path) -> io::Result<Vec<PrivateKey>> {
    rsa_private_keys(&mut BufReader::new(File::open(path)?))
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))
}
2 Likes

@wangjia184 Thanks! I solved it somehow in past, but now I'm thinking to move to hyper 0.13 so your code would come handy.
Do you have experiences with hyper 0.12 -> 0.13 migration? How it went?

Hey, as part of GitHub - swir-rs/swir: SWIR - Sidecar Written In Rust I migrated from hyper 0.12 to 0.13. It went pretty smooth. For TLS I copied fragments from tonic GitHub - hyperium/tonic: A native gRPC client & server implementation with async/await support. as shown here https://github.com/swir-rs/swir/blob/adding_grpc/src/main.rs