Skip to content

Commit

Permalink
Add rewrite_port on frontends
Browse files Browse the repository at this point in the history
Signed-off-by: Eloi DEMOLIS <[email protected]>
  • Loading branch information
Wonshtrum committed Dec 17, 2024
1 parent 2360820 commit c9ffaae
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 105 deletions.
2 changes: 2 additions & 0 deletions bin/src/ctl/request_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ impl CommandManager {
redirect_scheme: todo!(),
rewrite_host: todo!(),
rewrite_path: todo!(),
rewrite_port: todo!(),
})
.into(),
),
Expand Down Expand Up @@ -306,6 +307,7 @@ impl CommandManager {
redirect_scheme: todo!(),
rewrite_host: todo!(),
rewrite_path: todo!(),
rewrite_port: todo!(),
})
.into(),
),
Expand Down
1 change: 1 addition & 0 deletions command/src/command.proto
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ message RequestHttpFrontend {
optional RedirectScheme redirect_scheme = 9;
optional string rewrite_host = 10;
optional string rewrite_path = 11;
optional uint32 rewrite_port = 12;
}

message RequestTcpFrontend {
Expand Down
5 changes: 5 additions & 0 deletions command/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ pub struct FileClusterFrontendConfig {
pub redirect_scheme: Option<RedirectScheme>,
pub rewrite_host: Option<String>,
pub rewrite_path: Option<String>,
pub rewrite_port: Option<u16>,
}

impl FileClusterFrontendConfig {
Expand Down Expand Up @@ -760,6 +761,7 @@ impl FileClusterFrontendConfig {
redirect_scheme: self.redirect_scheme,
rewrite_host: self.rewrite_host.clone(),
rewrite_path: self.rewrite_path.clone(),
rewrite_port: self.rewrite_port.clone(),
})
}
}
Expand Down Expand Up @@ -914,6 +916,7 @@ pub struct HttpFrontendConfig {
pub redirect_scheme: Option<RedirectScheme>,
pub rewrite_host: Option<String>,
pub rewrite_path: Option<String>,
pub rewrite_port: Option<u16>,
}

impl HttpFrontendConfig {
Expand Down Expand Up @@ -955,6 +958,7 @@ impl HttpFrontendConfig {
redirect_scheme: self.redirect_scheme.map(Into::into),
rewrite_host: self.rewrite_host.clone(),
rewrite_path: self.rewrite_path.clone(),
rewrite_port: self.rewrite_port.map(|x| x as u32),
})
.into(),
);
Expand All @@ -973,6 +977,7 @@ impl HttpFrontendConfig {
redirect_scheme: self.redirect_scheme.map(Into::into),
rewrite_host: self.rewrite_host.clone(),
rewrite_path: self.rewrite_path.clone(),
rewrite_port: self.rewrite_port.map(|x| x as u32),
})
.into(),
);
Expand Down
1 change: 1 addition & 0 deletions command/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ impl RequestHttpFrontend {
tags: Some(self.tags),
rewrite_host: self.rewrite_host,
rewrite_path: self.rewrite_path,
rewrite_port: self.rewrite_port.map(|x| x as u16),
})
}
}
Expand Down
3 changes: 3 additions & 0 deletions command/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub struct HttpFrontend {
pub rewrite_host: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rewrite_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rewrite_port: Option<u16>,
pub tags: Option<BTreeMap<String, String>>,
}

Expand All @@ -61,6 +63,7 @@ impl From<HttpFrontend> for RequestHttpFrontend {
redirect_scheme: Some(val.redirect_scheme.into()),
rewrite_host: val.rewrite_host,
rewrite_path: val.rewrite_path,
rewrite_port: val.rewrite_port.map(|x| x as u32),
}
}
}
Expand Down
55 changes: 7 additions & 48 deletions lib/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,61 +430,16 @@ impl L7ListenerHandler for HttpListener {
self.config.connect_timeout
}

// redundant, already called once in extract_route
fn frontend_from_request(
&self,
host: &str,
uri: &str,
path: &str,
method: &Method,
) -> Result<RouteResult, FrontendFromRequestError> {
let start = Instant::now();
let (remaining_input, (hostname, _)) = match hostname_and_port(host.as_bytes()) {
Ok(tuple) => tuple,
Err(parse_error) => {
// parse_error contains a slice of given_host, which should NOT escape this scope
return Err(FrontendFromRequestError::HostParse {
host: host.to_owned(),
error: parse_error.to_string(),
});
}
};
if remaining_input != &b""[..] {
return Err(FrontendFromRequestError::InvalidCharsAfterHost(
host.to_owned(),
));
}

/*if port == Some(&b"80"[..]) {
// it is alright to call from_utf8_unchecked,
// we already verified that there are only ascii
// chars in there
unsafe { from_utf8_unchecked(hostname) }
} else {
host
}
*/
let host = unsafe { from_utf8_unchecked(hostname) };

let route = self.fronts.lookup(host, uri, method).map_err(|e| {
self.fronts.lookup(host, path, method).map_err(|e| {
incr!("http.failed_backend_matching");
FrontendFromRequestError::NoClusterFound(e)
})?;

let now = Instant::now();

if let RouteResult::Flow {
direction: RouteDirection::Forward(cluster_id),
..
} = &route
{
time!(
"frontend_matching_time",
cluster_id,
(now - start).as_millis()
);
}

Ok(route)
})
}
}

Expand Down Expand Up @@ -1339,6 +1294,7 @@ mod tests {
redirect_scheme: RedirectScheme::UseSame,
rewrite_host: None,
rewrite_path: None,
rewrite_port: None,
tags: None,
})
.expect("Could not add http frontend");
Expand All @@ -1354,6 +1310,7 @@ mod tests {
redirect_scheme: RedirectScheme::UseSame,
rewrite_host: None,
rewrite_path: None,
rewrite_port: None,
tags: None,
})
.expect("Could not add http frontend");
Expand All @@ -1369,6 +1326,7 @@ mod tests {
redirect_scheme: RedirectScheme::UseSame,
rewrite_host: None,
rewrite_path: None,
rewrite_port: None,
tags: None,
})
.expect("Could not add http frontend");
Expand All @@ -1384,6 +1342,7 @@ mod tests {
redirect_scheme: RedirectScheme::UseSame,
rewrite_host: None,
rewrite_path: None,
rewrite_port: None,
tags: None,
})
.expect("Could not add http frontend");
Expand Down
45 changes: 3 additions & 42 deletions lib/src/https.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,52 +567,13 @@ impl L7ListenerHandler for HttpsListener {
fn frontend_from_request(
&self,
host: &str,
uri: &str,
path: &str,
method: &Method,
) -> Result<RouteResult, FrontendFromRequestError> {
let start = Instant::now();
let (remaining_input, (hostname, _)) = match hostname_and_port(host.as_bytes()) {
Ok(tuple) => tuple,
Err(parse_error) => {
// parse_error contains a slice of given_host, which should NOT escape this scope
return Err(FrontendFromRequestError::HostParse {
host: host.to_owned(),
error: parse_error.to_string(),
});
}
};

if remaining_input != &b""[..] {
return Err(FrontendFromRequestError::InvalidCharsAfterHost(
host.to_owned(),
));
}

// it is alright to call from_utf8_unchecked,
// we already verified that there are only ascii
// chars in there
let host = unsafe { from_utf8_unchecked(hostname) };

let route = self.fronts.lookup(host, uri, method).map_err(|e| {
self.fronts.lookup(host, path, method).map_err(|e| {
incr!("http.failed_backend_matching");
FrontendFromRequestError::NoClusterFound(e)
})?;

let now = Instant::now();

if let RouteResult::Flow {
direction: RouteDirection::Forward(cluster_id),
..
} = &route
{
time!(
"frontend_matching_time",
cluster_id,
(now - start).as_millis()
);
}

Ok(route)
})
}
}

Expand Down
79 changes: 66 additions & 13 deletions lib/src/protocol/kawa_h1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ use std::{
io::ErrorKind,
net::{Shutdown, SocketAddr},
rc::{Rc, Weak},
str::from_utf8_unchecked,
time::{Duration, Instant},
};

use mio::{net::TcpStream, Interest, Token};
use parser::hostname_and_port;
use rusty_ulid::Ulid;
use sozu_command::{
config::MAX_LOOP_ITERATIONS,
Expand Down Expand Up @@ -40,8 +42,9 @@ use crate::{
sozu_command::{logging::LogContext, ready::Ready},
timer::TimeoutContainer,
AcceptError, BackendConnectAction, BackendConnectionError, BackendConnectionStatus,
L7ListenerHandler, L7Proxy, ListenerHandler, Protocol, ProxySession, Readiness,
RetrieveClusterError, SessionIsToBeClosed, SessionMetrics, SessionResult, StateResult,
FrontendFromRequestError, L7ListenerHandler, L7Proxy, ListenerHandler, Protocol, ProxySession,
Readiness, RetrieveClusterError, SessionIsToBeClosed, SessionMetrics, SessionResult,
StateResult,
};

/// This macro is defined uniquely in this module to help the tracking of kawa h1
Expand Down Expand Up @@ -1257,7 +1260,7 @@ impl<Front: SocketHandler, L: ListenerHandler + L7ListenerHandler> Http<Front, L
Err(cluster_error) => {
self.set_answer(DefaultAnswer::Answer400 {
message: "Could not extract the route after connection started, this should not happen.".into(),
phase: self.request_stream.parsing_phase.marker(),
phase: kawa::ParsingPhaseMarker::StatusLine,
successfully_parsed: "null".into(),
partially_parsed: "null".into(),
invalid: "null".into(),
Expand All @@ -1266,6 +1269,38 @@ impl<Front: SocketHandler, L: ListenerHandler + L7ListenerHandler> Http<Front, L
}
};

let (host, port) = match hostname_and_port(host.as_bytes()) {
Ok((b"", (hostname, port))) => (unsafe { from_utf8_unchecked(hostname) }, port),
Ok(_) => {
let host = host.to_owned();
self.set_answer(DefaultAnswer::Answer400 {
message: "Invalid characters after hostname, this should not happen.".into(),
phase: kawa::ParsingPhaseMarker::StatusLine,
successfully_parsed: "null".into(),
partially_parsed: "null".into(),
invalid: "null".into(),
});
return Err(RetrieveClusterError::RetrieveFrontend(
FrontendFromRequestError::InvalidCharsAfterHost(host),
));
}
Err(parse_error) => {
let host = host.to_owned();
let error = parse_error.to_string();
self.set_answer(DefaultAnswer::Answer400 {
message: "Could not parse port from hostname, this should not happen.".into(),
phase: kawa::ParsingPhaseMarker::StatusLine,
successfully_parsed: "null".into(),
partially_parsed: "null".into(),
invalid: "null".into(),
});
return Err(RetrieveClusterError::RetrieveFrontend(
FrontendFromRequestError::HostParse { host, error },
));
}
};

let start = Instant::now();
let route_result = self
.listener
.borrow()
Expand All @@ -1288,25 +1323,43 @@ impl<Front: SocketHandler, L: ListenerHandler + L7ListenerHandler> Http<Front, L
direction: flow,
rewritten_host,
rewritten_path,
rewritten_port,
} => {
let is_https = matches!(proxy.borrow().kind(), ListenerType::Https);
if let RouteDirection::Forward(cluster_id) = &flow {
if !is_https
&& proxy
.borrow()
.clusters()
.get(cluster_id)
.map(|cluster| cluster.https_redirect)
.unwrap_or(false)
{
time!(
"frontend_matching_time",
cluster_id,
start.elapsed().as_millis()
);
let (https_redirect, https_redirect_port, authentication) = proxy
.borrow()
.clusters()
.get(cluster_id)
.map(|cluster| (cluster.https_redirect, Some(8443), None::<()>))
.unwrap_or((false, None, None));
if !is_https && https_redirect {
let port =
https_redirect_port.map_or(String::new(), |port| format!(":{port}"));
self.set_answer(DefaultAnswer::Answer301 {
location: format!("https://{host}{path}"),
location: format!("https://{host}{port}{path}"),
});
return Err(RetrieveClusterError::Redirected);
}
if let Some(authentication) = authentication {
return Err(RetrieveClusterError::UnauthorizedRoute);
}
}
let host = rewritten_host.as_deref().unwrap_or(host);
let path = rewritten_path.as_deref().unwrap_or(path);
let port = rewritten_port.map_or_else(
|| {
port.map_or(String::new(), |port| {
format!(":{}", unsafe { from_utf8_unchecked(port) })
})
},
|port| format!(":{port}"),
);
match flow {
RouteDirection::Forward(cluster_id) => Ok(cluster_id),
RouteDirection::Permanent(redirect_scheme) => {
Expand All @@ -1319,7 +1372,7 @@ impl<Front: SocketHandler, L: ListenerHandler + L7ListenerHandler> Http<Front, L
}
};
self.set_answer(DefaultAnswer::Answer301 {
location: format!("{proto}://{host}{path}"),
location: format!("{proto}://{host}{port}{path}"),
});
Err(RetrieveClusterError::Redirected)
}
Expand Down
Loading

0 comments on commit c9ffaae

Please sign in to comment.