| //! Simple HTTPS echo service based on hyper-rustls |
| //! |
| //! First parameter is the mandatory port to use. |
| //! Certificate and private key are hardcoded to sample files. |
| //! hyper will automatically use HTTP/2 if a client starts talking HTTP/2, |
| //! otherwise HTTP/1.1 will be used. |
| |
| #![cfg(feature = "acceptor")] |
| |
| use std::vec::Vec; |
| use std::{env, fs, io}; |
| |
| use hyper::server::conn::AddrIncoming; |
| use hyper::service::{make_service_fn, service_fn}; |
| use hyper::{Body, Method, Request, Response, Server, StatusCode}; |
| use hyper_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<dyn std::error::Error + Send + Sync>> { |
| // 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).parse()?; |
| |
| // Load public certificate. |
| let certs = load_certs("examples/sample.pem")?; |
| // Load private key. |
| let key = load_private_key("examples/sample.rsa")?; |
| // Build TLS configuration. |
| |
| // Create a TCP listener via tokio. |
| let incoming = AddrIncoming::bind(&addr)?; |
| let acceptor = TlsAcceptor::builder() |
| .with_single_cert(certs, key) |
| .map_err(|e| error(format!("{}", e)))? |
| .with_all_versions_alpn() |
| .with_incoming(incoming); |
| let service = make_service_fn(|_| async { Ok::<_, io::Error>(service_fn(echo)) }); |
| let server = Server::builder(acceptor).serve(service); |
| |
| // Run the future, keep going until an error occurs. |
| println!("Starting to serve on https://{}.", addr); |
| server.await?; |
| Ok(()) |
| } |
| |
| // Custom echo service, handling two different routes and a |
| // catch-all 404 responder. |
| async fn echo(req: Request<Body>) -> Result<Response<Body>, 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<Vec<rustls::Certificate>> { |
| // 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. |
| let certs = rustls_pemfile::certs(&mut reader) |
| .map_err(|_| error("failed to load certificate".into()))?; |
| Ok(certs |
| .into_iter() |
| .map(rustls::Certificate) |
| .collect()) |
| } |
| |
| // Load private key from file. |
| fn load_private_key(filename: &str) -> io::Result<rustls::PrivateKey> { |
| // 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 = rustls_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(rustls::PrivateKey(keys[0].clone())) |
| } |