1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
//! Simple HTTPS echo service based on hyper_util and 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.
use std::net::{Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::{env, fs, io};
use http::{Method, Request, Response, StatusCode};
use http_body_util::{BodyExt, Full};
use hyper::body::{Bytes, Incoming};
use hyper::service::service_fn;
use hyper_util::rt::{TokioExecutor, TokioIo};
use hyper_util::server::conn::auto::Builder;
use pki_types::{CertificateDer, PrivateKeyDer};
use rustls::ServerConfig;
use tokio::net::TcpListener;
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<dyn std::error::Error + Send + Sync>> {
// Set a process wide default crypto provider.
#[cfg(feature = "ring")]
let _ = rustls::crypto::ring::default_provider().install_default();
#[cfg(feature = "aws-lc-rs")]
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
// First parameter is port number (optional, defaults to 1337)
let port = match env::args().nth(1) {
Some(ref p) => p.parse()?,
None => 1337,
};
let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), port);
// Load public certificate.
let certs = load_certs("examples/sample.pem")?;
// Load private key.
let key = load_private_key("examples/sample.rsa")?;
println!("Starting to serve on https://{}", addr);
// Create a TCP listener via tokio.
let incoming = TcpListener::bind(&addr).await?;
// Build TLS configuration.
let mut server_config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.map_err(|e| error(e.to_string()))?;
server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()];
let tls_acceptor = TlsAcceptor::from(Arc::new(server_config));
let service = service_fn(echo);
loop {
let (tcp_stream, _remote_addr) = incoming.accept().await?;
let tls_acceptor = tls_acceptor.clone();
tokio::spawn(async move {
let tls_stream = match tls_acceptor.accept(tcp_stream).await {
Ok(tls_stream) => tls_stream,
Err(err) => {
eprintln!("failed to perform tls handshake: {err:#}");
return;
}
};
if let Err(err) = Builder::new(TokioExecutor::new())
.serve_connection(TokioIo::new(tls_stream), service)
.await
{
eprintln!("failed to serve connection: {err:#}");
}
});
}
}
// Custom echo service, handling two different routes and a
// catch-all 404 responder.
async fn echo(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, hyper::Error> {
let mut response = Response::new(Full::default());
match (req.method(), req.uri().path()) {
// Help route.
(&Method::GET, "/") => {
*response.body_mut() = Full::from("Try POST /echo\n");
}
// Echo service route.
(&Method::POST, "/echo") => {
*response.body_mut() = Full::from(
req.into_body()
.collect()
.await?
.to_bytes(),
);
}
// Catch-all 404.
_ => {
*response.status_mut() = StatusCode::NOT_FOUND;
}
};
Ok(response)
}
// Load public certificate from file.
fn load_certs(filename: &str) -> io::Result<Vec<CertificateDer<'static>>> {
// 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.
rustls_pemfile::certs(&mut reader).collect()
}
// Load private key from file.
fn load_private_key(filename: &str) -> io::Result<PrivateKeyDer<'static>> {
// 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.
rustls_pemfile::private_key(&mut reader).map(|key| key.unwrap())
}
|