diff options
author | Alex Auvolat <alex@adnab.me> | 2021-12-07 13:50:44 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2021-12-07 13:50:44 +0100 |
commit | 5535c4951a832d65755afa53822a36e96681320f (patch) | |
tree | f5b46340c16dbf97f69711f519824512b8d0db80 /src/http.rs | |
parent | 61e6df6209b3c55e4c07c6baf2fabfba23a474f1 (diff) | |
download | tricot-5535c4951a832d65755afa53822a36e96681320f.tar.gz tricot-5535c4951a832d65755afa53822a36e96681320f.zip |
Retrieve let's encrypt certificates
Diffstat (limited to 'src/http.rs')
-rw-r--r-- | src/http.rs | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/src/http.rs b/src/http.rs new file mode 100644 index 0000000..385456a --- /dev/null +++ b/src/http.rs @@ -0,0 +1,75 @@ +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/"; + +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(""))?) + } +} + +pub async fn serve_http(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) + })) + } + }); + + let addr = ([0, 0, 0, 0], 1080).into(); + + let server = Server::bind(&addr).serve(make_svc); + + println!("Listening on http://{}", addr); + + server.await?; + + Ok(()) +} |