aboutsummaryrefslogtreecommitdiff
path: root/src/http.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/http.rs')
-rw-r--r--src/http.rs75
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(())
+}