aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2023-11-29 15:50:25 +0100
committerQuentin Dufour <quentin@deuxfleurs.fr>2023-11-29 15:50:25 +0100
commit2b3f934247e0c35f53822d600ec34483b4092e5c (patch)
treeaaaa8d40ff6a7634233a0aa459bd46c84214604b
parentf11592926b9548c0797c02bd5aa4550a1f251c98 (diff)
downloadtricot-2b3f934247e0c35f53822d600ec34483b4092e5c.tar.gz
tricot-2b3f934247e0c35f53822d600ec34483b4092e5c.zip
implement redirection in https.rs
-rw-r--r--src/https.rs66
-rw-r--r--src/proxy_config.rs10
2 files changed, 65 insertions, 11 deletions
diff --git a/src/https.rs b/src/https.rs
index 5f8a17e..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);
@@ -272,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);
@@ -302,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,
diff --git a/src/proxy_config.rs b/src/proxy_config.rs
index a481058..8381de2 100644
--- a/src/proxy_config.rs
+++ b/src/proxy_config.rs
@@ -106,7 +106,7 @@ pub struct ProxyEntry {
/// Try to match all these redirection before forwarding to the backend
/// when matching this rule
- pub redirects: Vec<(UrlPrefix, UrlPrefix, u32)>,
+ pub redirects: Vec<(UrlPrefix, UrlPrefix, u16)>,
/// Number of calls in progress, used to deprioritize slow back-ends
pub calls_in_progress: atomic::AtomicI64,
@@ -246,7 +246,7 @@ enum MatchTag {
#[derive(Debug)]
enum ConfigTag<'a> {
AddHeader(&'a str, String),
- AddRedirect(UrlPrefix, UrlPrefix, u32),
+ AddRedirect(UrlPrefix, UrlPrefix, u16),
GlobalLb,
LocalLb,
}
@@ -301,14 +301,16 @@ fn parse_tricot_tags(tag: &str) -> Option<ParsedTag> {
let maybe_parsed_code = maybe_raw_code
.iter()
.next()
- .map(|c| c.parse::<u32>().ok())
+ .map(|c| c.parse::<u16>().ok())
.flatten();
let http_code = match maybe_parsed_code {
Some(301) => 301,
Some(302) => 302,
+ Some(303) => 303,
+ Some(307) => 307,
_ => {
debug!(
- "tag {} has a missing or invalid http code, set it to 302",
+ "tag {} has a missing or invalid http code, setting it to 302",
tag
);
302