feat: add hyper_rustls
This commit is contained in:
141
__web/hyper_rustls/src/main.rs
Normal file
141
__web/hyper_rustls/src/main.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
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<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);
|
||||
|
||||
// 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<Box<dyn Stream<Item=Result<TlsStream<TcpStream>, io::Error>> + 'a>>,
|
||||
}
|
||||
|
||||
impl hyper::server::accept::Accept for HyperAcceptor<'_> {
|
||||
type Conn = TlsStream<TcpStream>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll_accept(mut self: Pin<&mut Self>, cx: &mut Context)
|
||||
-> Poll<Option<Result<Self::Conn, Self::Error>>> {
|
||||
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<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.
|
||||
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<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 = 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())
|
||||
}
|
||||
Reference in New Issue
Block a user