use async_stream::stream; use core::task::{Context, Poll}; use futures_util::{future::TryFutureExt, stream::Stream}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, Server, StatusCode}; use rustls::internal::pemfile; use std::pin::Pin; use std::vec::Vec; use std::{env, fs, io, sync}; use tokio::net::{TcpListener, TcpStream}; use tokio_rustls::server::TlsStream; use tokio_rustls::TlsAcceptor; fn main() { // Serve an echo service over HTTPS, with proper error handling. if let Err(e) = run_server() { eprintln!("FAILED: {}", e); std::process::exit(1); } } fn error(err: String) -> io::Error { io::Error::new(io::ErrorKind::Other, err) } #[tokio::main] async fn run_server() -> Result<(), Box> { // First parameter is port number (optional, defaults to 1337) let port = match env::args().nth(1) { Some(ref p) => p.to_owned(), None => "1337".to_owned(), }; let addr = format!("127.0.0.1:{}", port); // Build TLS configuration. let tls_cfg = { // Load public certificate. let certs = load_certs("cert.pem")?; // Load private key. let key = load_private_key("cert.key")?; // Do not use client certificate authentication. let mut cfg = rustls::ServerConfig::new(rustls::NoClientAuth::new()); // Select a certificate to use. cfg.set_single_cert(certs, key) .map_err(|e| error(format!("{}", e)))?; // Configure ALPN to accept HTTP/2, HTTP/1.1 in that order. cfg.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]); sync::Arc::new(cfg) }; // Create a TCP listener via tokio. let tcp = TcpListener::bind(&addr).await?; let tls_acceptor = TlsAcceptor::from(tls_cfg); // Prepare a long-running future stream to accept and serve clients. let incoming_tls_stream = stream! { loop { let (socket, _) = tcp.accept().await?; let stream = tls_acceptor.accept(socket).map_err(|e| { println!("[!] Voluntary server halt due to client-connection error..."); // Errors could be handled here, instead of server aborting. // Ok(None) error(format!("TLS Error: {:?}", e)) // ?? }); yield stream.await; } }; let service = make_service_fn(|_| async { Ok::<_, io::Error>(service_fn(echo)) }); let server = Server::builder(HyperAcceptor { acceptor: Box::pin(incoming_tls_stream), }).serve(service); // Run the future, keep going until an error occurs. println!("Starting to serve on https://{}.", addr); server.await?; Ok(()) } struct HyperAcceptor<'a> { acceptor: Pin, io::Error>> + 'a>>, } impl hyper::server::accept::Accept for HyperAcceptor<'_> { type Conn = TlsStream; type Error = io::Error; fn poll_accept(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll>> { Pin::new(&mut self.acceptor).poll_next(cx) } } // Custom echo service, handling two different routes and a // catch-all 404 responder. async fn echo(req: Request) -> Result, hyper::Error> { let mut response = Response::new(Body::empty()); match (req.method(), req.uri().path()) { // Help route. (&Method::GET, "/") => { *response.body_mut() = Body::from("Try POST /echo\n"); } // Echo service route. (&Method::POST, "/echo") => { *response.body_mut() = req.into_body(); } // Catch-all 404. _ => { *response.status_mut() = StatusCode::NOT_FOUND; } }; Ok(response) } // Load public certificate from file. fn load_certs(filename: &str) -> io::Result> { // Open certificate file. let certfile = fs::File::open(filename).map_err(|e| error(format!("failed to open {}: {}", filename, e)) )?; let mut reader = io::BufReader::new(certfile); // Load and return certificate. pemfile::certs(&mut reader).map_err(|_| error("failed to load certificate".into())) } // Load private key from file. fn load_private_key(filename: &str) -> io::Result { // Open keyfile. let keyfile = fs::File::open(filename).map_err(|e| error(format!("failed to open {}: {}", filename, e)) )?; let mut reader = io::BufReader::new(keyfile); // Load and return a single private key. let keys = pemfile::rsa_private_keys(&mut reader).map_err(|_| error("failed to load private key".into()) )?; if keys.len() != 1 { return Err(error("expected a single private key".into())); } Ok(keys[0].clone()) }