forked from Deuxfleurs/tricot
76 lines
2 KiB
Rust
76 lines
2 KiB
Rust
use std::net::SocketAddr;
|
|
use std::sync::Arc;
|
|
|
|
use anyhow::Result;
|
|
use log::*;
|
|
|
|
use http::uri::Authority;
|
|
use hyper::service::{make_service_fn, service_fn};
|
|
use hyper::{Body, Request, Response, Server, StatusCode, Uri};
|
|
|
|
use crate::consul::Consul;
|
|
|
|
const CHALLENGE_PREFIX: &str = "/.well-known/acme-challenge/";
|
|
|
|
pub async fn serve_http(
|
|
bind_addr: SocketAddr,
|
|
consul: Consul,
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
let consul = Arc::new(consul);
|
|
// For every connection, we must make a `Service` to handle all
|
|
// incoming HTTP requests on said connection.
|
|
let make_svc = make_service_fn(|_conn| {
|
|
let consul = consul.clone();
|
|
// This is the `Service` that will handle the connection.
|
|
// `service_fn` is a helper to convert a function that
|
|
// returns a Response into a `Service`.
|
|
async move {
|
|
Ok::<_, anyhow::Error>(service_fn(move |req: Request<Body>| {
|
|
let consul = consul.clone();
|
|
handle(req, consul)
|
|
}))
|
|
}
|
|
});
|
|
|
|
info!("Listening on http://{}", bind_addr);
|
|
let server = Server::bind(&bind_addr).serve(make_svc);
|
|
|
|
server.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn handle(req: Request<Body>, consul: Arc<Consul>) -> Result<Response<Body>> {
|
|
let path = req.uri().path();
|
|
info!("HTTP request {}", path);
|
|
|
|
if let Some(token) = path.strip_prefix(CHALLENGE_PREFIX) {
|
|
let response = consul.kv_get(&format!("challenge/{}", token)).await?;
|
|
match response {
|
|
Some(r) => Ok(Response::new(Body::from(r))),
|
|
None => Ok(Response::builder()
|
|
.status(StatusCode::NOT_FOUND)
|
|
.body(Body::from(""))?),
|
|
}
|
|
} else {
|
|
// Redirect to HTTPS
|
|
let uri2 = req.uri().clone();
|
|
let mut parts = uri2.into_parts();
|
|
|
|
let host = req
|
|
.headers()
|
|
.get("Host")
|
|
.map(|h| h.to_str())
|
|
.ok_or_else(|| anyhow!("Missing host header"))??
|
|
.to_string();
|
|
|
|
parts.authority = Some(Authority::from_maybe_shared(host)?);
|
|
parts.scheme = Some("https".parse().unwrap());
|
|
let uri2 = Uri::from_parts(parts)?;
|
|
|
|
Ok(Response::builder()
|
|
.status(StatusCode::MOVED_PERMANENTLY)
|
|
.header("Location", uri2.to_string())
|
|
.body(Body::from(""))?)
|
|
}
|
|
}
|