2021-12-07 14:20:45 +00:00
|
|
|
use std::net::SocketAddr;
|
2021-12-07 15:37:22 +00:00
|
|
|
use std::sync::{atomic::Ordering, Arc};
|
2021-12-07 14:20:45 +00:00
|
|
|
|
|
|
|
use anyhow::Result;
|
|
|
|
use log::*;
|
|
|
|
|
|
|
|
use futures::FutureExt;
|
|
|
|
use hyper::server::conn::Http;
|
|
|
|
use hyper::service::service_fn;
|
|
|
|
use hyper::{Body, Request, Response, StatusCode};
|
|
|
|
use tokio::net::TcpListener;
|
|
|
|
use tokio::sync::watch;
|
|
|
|
use tokio_rustls::TlsAcceptor;
|
|
|
|
|
|
|
|
use crate::cert_store::{CertStore, StoreResolver};
|
|
|
|
use crate::proxy_config::ProxyConfig;
|
|
|
|
use crate::reverse_proxy;
|
|
|
|
|
|
|
|
pub async fn serve_https(
|
|
|
|
cert_store: Arc<CertStore>,
|
|
|
|
proxy_config: watch::Receiver<Arc<ProxyConfig>>,
|
|
|
|
) -> Result<()> {
|
|
|
|
let addr = format!("0.0.0.0:1443");
|
|
|
|
|
|
|
|
let mut cfg = rustls::ServerConfig::builder()
|
|
|
|
.with_safe_defaults()
|
|
|
|
.with_no_client_auth()
|
|
|
|
.with_cert_resolver(Arc::new(StoreResolver(cert_store)));
|
|
|
|
|
|
|
|
cfg.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
|
|
|
let tls_cfg = Arc::new(cfg);
|
|
|
|
let tls_acceptor = Arc::new(TlsAcceptor::from(tls_cfg));
|
|
|
|
|
|
|
|
println!("Starting to serve on https://{}.", addr);
|
|
|
|
|
|
|
|
let tcp = TcpListener::bind(&addr).await?;
|
|
|
|
loop {
|
|
|
|
let (socket, remote_addr) = tcp.accept().await?;
|
|
|
|
|
|
|
|
let proxy_config = proxy_config.clone();
|
|
|
|
let tls_acceptor = tls_acceptor.clone();
|
|
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
match tls_acceptor.accept(socket).await {
|
|
|
|
Ok(stream) => {
|
|
|
|
debug!("TLS handshake was successfull");
|
|
|
|
let http_result = Http::new()
|
|
|
|
.serve_connection(
|
|
|
|
stream,
|
|
|
|
service_fn(move |req: Request<Body>| {
|
|
|
|
let proxy_config: Arc<ProxyConfig> = proxy_config.borrow().clone();
|
|
|
|
handle(remote_addr, req, proxy_config).map(|res| match res {
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Handler error: {}", e);
|
|
|
|
Response::builder()
|
|
|
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
.body(Body::from(format!("{}", e)))
|
|
|
|
.map_err(Into::into)
|
|
|
|
}
|
|
|
|
x => x,
|
|
|
|
})
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
if let Err(http_err) = http_result {
|
|
|
|
debug!("HTTP error: {}", http_err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => debug!("Error in TLS connection: {}", e),
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Custom echo service, handling two different routes and a
|
|
|
|
// catch-all 404 responder.
|
|
|
|
async fn handle(
|
|
|
|
remote_addr: SocketAddr,
|
|
|
|
req: Request<Body>,
|
|
|
|
proxy_config: Arc<ProxyConfig>,
|
|
|
|
) -> Result<Response<Body>, anyhow::Error> {
|
|
|
|
let host = if let Some(auth) = req.uri().authority() {
|
|
|
|
auth.as_str()
|
|
|
|
} else {
|
|
|
|
req.headers()
|
|
|
|
.get("host")
|
|
|
|
.ok_or_else(|| anyhow!("Missing host header"))?
|
|
|
|
.to_str()?
|
|
|
|
};
|
|
|
|
let path = req.uri().path();
|
|
|
|
|
|
|
|
let ent = proxy_config
|
|
|
|
.entries
|
|
|
|
.iter()
|
|
|
|
.filter(|ent| {
|
|
|
|
ent.host == host
|
|
|
|
&& ent
|
|
|
|
.path_prefix
|
|
|
|
.as_ref()
|
|
|
|
.map(|prefix| path.starts_with(prefix))
|
|
|
|
.unwrap_or(true)
|
|
|
|
})
|
|
|
|
.min_by_key(|ent| {
|
|
|
|
(
|
|
|
|
ent.priority,
|
|
|
|
-(ent
|
|
|
|
.path_prefix
|
|
|
|
.as_ref()
|
|
|
|
.map(|x| x.len() as i32)
|
|
|
|
.unwrap_or(0)),
|
2021-12-07 15:37:22 +00:00
|
|
|
ent.calls.load(Ordering::SeqCst),
|
2021-12-07 14:20:45 +00:00
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
if let Some(proxy_to) = ent {
|
2021-12-07 15:37:22 +00:00
|
|
|
proxy_to.calls.fetch_add(1, Ordering::SeqCst);
|
|
|
|
|
2021-12-07 14:20:45 +00:00
|
|
|
let to_addr = format!("http://{}", proxy_to.target_addr);
|
|
|
|
info!("Proxying {} {} -> {}", host, path, to_addr);
|
|
|
|
|
|
|
|
reverse_proxy::call(remote_addr.ip(), &to_addr, req).await
|
|
|
|
} else {
|
|
|
|
info!("Proxying {} {} -> NOT FOUND", host, path);
|
|
|
|
|
|
|
|
Ok(Response::builder()
|
|
|
|
.status(StatusCode::NOT_FOUND)
|
|
|
|
.body(Body::from("No matching proxy entry"))?)
|
|
|
|
}
|
|
|
|
}
|