diff options
author | Quentin <quentin@dufour.io> | 2023-11-29 16:09:56 +0000 |
---|---|---|
committer | Quentin <quentin@dufour.io> | 2023-11-29 16:09:56 +0000 |
commit | b04c2bfb0a5776ddcf8c0bab23dd1f800e3a0df3 (patch) | |
tree | 8b0eda30cba5b2c62810a2cd71b7a93095e20205 /src/https.rs | |
parent | 14325395f6b059a317df738657edec47599c291f (diff) | |
parent | b76b6dcbcc47ebc61848389a6b0d5d4e8d8cde48 (diff) | |
download | tricot-b04c2bfb0a5776ddcf8c0bab23dd1f800e3a0df3.tar.gz tricot-b04c2bfb0a5776ddcf8c0bab23dd1f800e3a0df3.zip |
Merge pull request 'New directive `tricot-add-redirect <match-prefix> <redirect-prefix> [301|302|303|307]`' (#10) from redirect into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/tricot/pulls/10
Diffstat (limited to 'src/https.rs')
-rw-r--r-- | src/https.rs | 72 |
1 files changed, 63 insertions, 9 deletions
diff --git a/src/https.rs b/src/https.rs index ed98ae1..9d92470 100644 --- a/src/https.rs +++ b/src/https.rs @@ -24,7 +24,7 @@ use tokio_util::io::{ReaderStream, StreamReader}; use opentelemetry::{metrics, KeyValue}; use crate::cert_store::{CertStore, StoreResolver}; -use crate::proxy_config::{ProxyConfig, ProxyEntry}; +use crate::proxy_config::{HostDescription, ProxyConfig, ProxyEntry}; use crate::reverse_proxy; const MAX_CONNECTION_LIFETIME: Duration = Duration::from_secs(24 * 3600); @@ -234,8 +234,9 @@ async fn select_target_and_proxy( .iter() .filter(|ent| { ent.flags.healthy - && ent.host.matches(host) + && ent.url_prefix.host.matches(host) && ent + .url_prefix .path_prefix .as_ref() .map(|prefix| path.starts_with(prefix)) @@ -244,7 +245,8 @@ async fn select_target_and_proxy( .max_by_key(|ent| { ( ent.priority, - ent.path_prefix + ent.url_prefix + .path_prefix .as_ref() .map(|x| x.len() as i32) .unwrap_or(0), @@ -270,15 +272,22 @@ async fn select_target_and_proxy( ); proxy_to.calls_in_progress.fetch_add(1, Ordering::SeqCst); + // Forward to backend debug!("{}{} -> {}", host, path, proxy_to); trace!("Request: {:?}", req); - let response = match do_proxy(https_config, remote_addr, req, proxy_to).await { - Ok(resp) => resp, - Err(e) => Response::builder() - .status(StatusCode::BAD_GATEWAY) - .body(Body::from(format!("Proxy error: {}", e))) - .unwrap(), + let response = if let Some(http_res) = try_redirect(host, path, proxy_to) { + // redirection middleware + http_res + } else { + // proxying to backend + match do_proxy(https_config, remote_addr, req, proxy_to).await { + Ok(resp) => resp, + Err(e) => Response::builder() + .status(StatusCode::BAD_GATEWAY) + .body(Body::from(format!("Proxy error: {}", e))) + .unwrap(), + } }; proxy_to.calls_in_progress.fetch_sub(1, Ordering::SeqCst); @@ -300,6 +309,51 @@ async fn select_target_and_proxy( } } +fn try_redirect(req_host: &str, req_path: &str, proxy_to: &ProxyEntry) -> Option<Response<Body>> { + let maybe_redirect = proxy_to.redirects.iter().find(|(src, _, _)| { + let mut matched: bool = src.host.matches(req_host); + + if let Some(path) = &src.path_prefix { + matched &= req_path.starts_with(path); + } + + matched + }); + + let (src_prefix, dst_prefix, code) = match maybe_redirect { + None => return None, + Some(redirect) => redirect, + }; + + let new_host = match &dst_prefix.host { + HostDescription::Hostname(h) => h, + _ => unreachable!(), // checked when ProxyEntry is created + }; + + let new_prefix = dst_prefix.path_prefix.as_deref().unwrap_or(""); + let original_prefix = src_prefix.path_prefix.as_deref().unwrap_or(""); + let suffix = &req_path[original_prefix.len()..]; + + let uri = format!("https://{}{}{}", new_host, new_prefix, suffix); + + let status = match StatusCode::from_u16(*code) { + Err(e) => { + warn!( + "Couldn't redirect {}{} to {} as code {} in invalid: {}", + req_host, req_path, uri, code, e + ); + return None; + } + Ok(sc) => sc, + }; + + Response::builder() + .header("Location", uri.clone()) + .status(status) + .body(Body::from(uri)) + .ok() +} + async fn do_proxy( https_config: &HttpsConfig, remote_addr: SocketAddr, |