use async_web_server::{parse_pem, HttpRequest, TcpIncoming, TlsStream};
use clap::Parser;
use futures::prelude::*;
use rcgen::{date_time_ymd, CertificateParams};
use rustls_acme::futures_rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
use smol::future::block_on;
use smol::spawn;
use std::fs;
use std::net::Ipv6Addr;
use std::path::PathBuf;
#[derive(Parser, Debug)]
struct Args {
#[clap(short, required = true)]
domains: Vec<String>,
#[clap(short, long, number_of_values = 2, default_values = &["80", "443"])]
ports: Vec<u16>,
#[clap(short, long)]
cert: Option<PathBuf>,
}
fn main() -> anyhow::Result<()> {
simple_logger::init_with_level(log::Level::Info).unwrap();
let args = Args::parse();
let redirect_http = TcpIncoming::bind((Ipv6Addr::UNSPECIFIED, args.ports[0]))?
.http()
.redirect_https();
spawn(redirect_http).detach();
let (cert_chain, private_key) = match &args.cert {
None => generate_certificate(args.domains.clone()),
Some(path) => parse_pem(fs::read(path)?)?,
};
let mut incoming = TcpIncoming::bind((Ipv6Addr::UNSPECIFIED, args.ports[1]))?
.tls(cert_chain, private_key)?
.http();
block_on(async {
while let Some(req) = incoming.next().await {
spawn(async {
if let Err(err) = handle_http(req).await {
log::error!("error handling request: {:?}", err);
}
})
.detach();
}
unreachable!()
})
}
async fn handle_http(req: HttpRequest<TlsStream>) -> anyhow::Result<()> {
log::info!("received {:?} request head", req.method());
let resp = req.response().await?;
log::info!("read request body",);
resp.send("Hello HTTPS!").await?;
log::info!("sent response");
Ok(())
}
fn generate_certificate(
domains: impl Into<Vec<String>>,
) -> (Vec<CertificateDer<'static>>, PrivateKeyDer<'static>) {
let mut params = CertificateParams::new(domains);
params.not_before = date_time_ymd(2000, 1, 1);
params.not_after = date_time_ymd(3000, 1, 1);
let cert_and_key = rcgen::Certificate::from_params(params).unwrap();
(
vec![CertificateDer::from(cert_and_key.serialize_der().unwrap())],
PrivatePkcs8KeyDer::from(cert_and_key.serialize_private_key_der()).into(),
)
}