tricot/src/https.rs

144 lines
3.7 KiB
Rust
Raw Normal View History

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 http::header::{HeaderName, HeaderValue};
2021-12-07 14:20:45 +00:00
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(
2021-12-07 17:19:51 +00:00
bind_addr: SocketAddr,
2021-12-07 14:20:45 +00:00
cert_store: Arc<CertStore>,
proxy_config: watch::Receiver<Arc<ProxyConfig>>,
) -> Result<()> {
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));
2021-12-07 17:19:51 +00:00
info!("Starting to serve on https://{}.", bind_addr);
2021-12-07 14:20:45 +00:00
2021-12-07 17:19:51 +00:00
let tcp = TcpListener::bind(bind_addr).await?;
2021-12-07 14:20:45 +00:00
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 {
2021-12-08 10:24:25 +00:00
warn!("HTTP error: {}", http_err);
2021-12-07 14:20:45 +00:00
}
}
2021-12-08 10:24:25 +00:00
Err(e) => warn!("Error in TLS connection: {}", e),
2021-12-07 14:20:45 +00:00
}
});
}
}
// 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.matches(host)
2021-12-07 14:20:45 +00:00
&& ent
.path_prefix
.as_ref()
.map(|prefix| path.starts_with(prefix))
.unwrap_or(true)
})
2021-12-08 21:29:08 +00:00
.max_by_key(|ent| {
2021-12-07 14:20:45 +00:00
(
ent.priority,
2021-12-08 21:29:08 +00:00
ent.path_prefix
2021-12-07 14:20:45 +00:00
.as_ref()
.map(|x| x.len() as i32)
2021-12-08 21:29:08 +00:00
.unwrap_or(0),
-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);
2021-12-08 21:24:58 +00:00
let method = req.method().clone();
let uri = req.uri().to_string();
debug!("{}{} -> {}", host, path, proxy_to);
2021-12-08 12:28:07 +00:00
trace!("Request: {:?}", req);
2021-12-07 14:20:45 +00:00
2021-12-07 17:19:51 +00:00
let mut response = reverse_proxy::call(remote_addr.ip(), &to_addr, req).await?;
for (header, value) in proxy_to.add_headers.iter() {
response.headers_mut().insert(
HeaderName::from_bytes(header.as_bytes())?,
HeaderValue::from_str(value)?,
);
2021-12-07 17:19:51 +00:00
}
2021-12-08 12:28:07 +00:00
trace!("Response: {:?}", response);
2021-12-08 21:24:58 +00:00
info!("{} {} {}", method, response.status().as_u16(), uri);
2021-12-07 17:19:51 +00:00
Ok(response)
2021-12-07 14:20:45 +00:00
} else {
info!("Proxying {} {} -> NOT FOUND", host, path);
Ok(Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Body::from("No matching proxy entry"))?)
}
}