aboutsummaryrefslogblamecommitdiff
path: root/src/http.rs
blob: 47316451250106a071aaf52ab1fb6002b204f24e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
                   
                         











                                                              



























                                                                                     

































                                                                                     
use std::sync::Arc;
use std::net::SocketAddr;

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