aboutsummaryrefslogblamecommitdiff
path: root/src/http.rs
blob: 385456aa072521552b7b9a0b4a4838c6fa55cf1e (plain) (tree)










































































                                                                                                 
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(())
}