Skip to content

Commit c9ffaae

Browse files
committed
Add rewrite_port on frontends
Signed-off-by: Eloi DEMOLIS <eloi.demolis@clever-cloud.com>
1 parent 2360820 commit c9ffaae

File tree

9 files changed

+97
-105
lines changed

9 files changed

+97
-105
lines changed

bin/src/ctl/request_builder.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ impl CommandManager {
254254
redirect_scheme: todo!(),
255255
rewrite_host: todo!(),
256256
rewrite_path: todo!(),
257+
rewrite_port: todo!(),
257258
})
258259
.into(),
259260
),
@@ -306,6 +307,7 @@ impl CommandManager {
306307
redirect_scheme: todo!(),
307308
rewrite_host: todo!(),
308309
rewrite_path: todo!(),
310+
rewrite_port: todo!(),
309311
})
310312
.into(),
311313
),

command/src/command.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ message RequestHttpFrontend {
265265
optional RedirectScheme redirect_scheme = 9;
266266
optional string rewrite_host = 10;
267267
optional string rewrite_path = 11;
268+
optional uint32 rewrite_port = 12;
268269
}
269270

270271
message RequestTcpFrontend {

command/src/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ pub struct FileClusterFrontendConfig {
671671
pub redirect_scheme: Option<RedirectScheme>,
672672
pub rewrite_host: Option<String>,
673673
pub rewrite_path: Option<String>,
674+
pub rewrite_port: Option<u16>,
674675
}
675676

676677
impl FileClusterFrontendConfig {
@@ -760,6 +761,7 @@ impl FileClusterFrontendConfig {
760761
redirect_scheme: self.redirect_scheme,
761762
rewrite_host: self.rewrite_host.clone(),
762763
rewrite_path: self.rewrite_path.clone(),
764+
rewrite_port: self.rewrite_port.clone(),
763765
})
764766
}
765767
}
@@ -914,6 +916,7 @@ pub struct HttpFrontendConfig {
914916
pub redirect_scheme: Option<RedirectScheme>,
915917
pub rewrite_host: Option<String>,
916918
pub rewrite_path: Option<String>,
919+
pub rewrite_port: Option<u16>,
917920
}
918921

919922
impl HttpFrontendConfig {
@@ -955,6 +958,7 @@ impl HttpFrontendConfig {
955958
redirect_scheme: self.redirect_scheme.map(Into::into),
956959
rewrite_host: self.rewrite_host.clone(),
957960
rewrite_path: self.rewrite_path.clone(),
961+
rewrite_port: self.rewrite_port.map(|x| x as u32),
958962
})
959963
.into(),
960964
);
@@ -973,6 +977,7 @@ impl HttpFrontendConfig {
973977
redirect_scheme: self.redirect_scheme.map(Into::into),
974978
rewrite_host: self.rewrite_host.clone(),
975979
rewrite_path: self.rewrite_path.clone(),
980+
rewrite_port: self.rewrite_port.map(|x| x as u32),
976981
})
977982
.into(),
978983
);

command/src/request.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ impl RequestHttpFrontend {
171171
tags: Some(self.tags),
172172
rewrite_host: self.rewrite_host,
173173
rewrite_path: self.rewrite_path,
174+
rewrite_port: self.rewrite_port.map(|x| x as u16),
174175
})
175176
}
176177
}

command/src/response.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pub struct HttpFrontend {
4444
pub rewrite_host: Option<String>,
4545
#[serde(skip_serializing_if = "Option::is_none")]
4646
pub rewrite_path: Option<String>,
47+
#[serde(skip_serializing_if = "Option::is_none")]
48+
pub rewrite_port: Option<u16>,
4749
pub tags: Option<BTreeMap<String, String>>,
4850
}
4951

@@ -61,6 +63,7 @@ impl From<HttpFrontend> for RequestHttpFrontend {
6163
redirect_scheme: Some(val.redirect_scheme.into()),
6264
rewrite_host: val.rewrite_host,
6365
rewrite_path: val.rewrite_path,
66+
rewrite_port: val.rewrite_port.map(|x| x as u32),
6467
}
6568
}
6669
}

lib/src/http.rs

Lines changed: 7 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -430,61 +430,16 @@ impl L7ListenerHandler for HttpListener {
430430
self.config.connect_timeout
431431
}
432432

433-
// redundant, already called once in extract_route
434433
fn frontend_from_request(
435434
&self,
436435
host: &str,
437-
uri: &str,
436+
path: &str,
438437
method: &Method,
439438
) -> Result<RouteResult, FrontendFromRequestError> {
440-
let start = Instant::now();
441-
let (remaining_input, (hostname, _)) = match hostname_and_port(host.as_bytes()) {
442-
Ok(tuple) => tuple,
443-
Err(parse_error) => {
444-
// parse_error contains a slice of given_host, which should NOT escape this scope
445-
return Err(FrontendFromRequestError::HostParse {
446-
host: host.to_owned(),
447-
error: parse_error.to_string(),
448-
});
449-
}
450-
};
451-
if remaining_input != &b""[..] {
452-
return Err(FrontendFromRequestError::InvalidCharsAfterHost(
453-
host.to_owned(),
454-
));
455-
}
456-
457-
/*if port == Some(&b"80"[..]) {
458-
// it is alright to call from_utf8_unchecked,
459-
// we already verified that there are only ascii
460-
// chars in there
461-
unsafe { from_utf8_unchecked(hostname) }
462-
} else {
463-
host
464-
}
465-
*/
466-
let host = unsafe { from_utf8_unchecked(hostname) };
467-
468-
let route = self.fronts.lookup(host, uri, method).map_err(|e| {
439+
self.fronts.lookup(host, path, method).map_err(|e| {
469440
incr!("http.failed_backend_matching");
470441
FrontendFromRequestError::NoClusterFound(e)
471-
})?;
472-
473-
let now = Instant::now();
474-
475-
if let RouteResult::Flow {
476-
direction: RouteDirection::Forward(cluster_id),
477-
..
478-
} = &route
479-
{
480-
time!(
481-
"frontend_matching_time",
482-
cluster_id,
483-
(now - start).as_millis()
484-
);
485-
}
486-
487-
Ok(route)
442+
})
488443
}
489444
}
490445

@@ -1339,6 +1294,7 @@ mod tests {
13391294
redirect_scheme: RedirectScheme::UseSame,
13401295
rewrite_host: None,
13411296
rewrite_path: None,
1297+
rewrite_port: None,
13421298
tags: None,
13431299
})
13441300
.expect("Could not add http frontend");
@@ -1354,6 +1310,7 @@ mod tests {
13541310
redirect_scheme: RedirectScheme::UseSame,
13551311
rewrite_host: None,
13561312
rewrite_path: None,
1313+
rewrite_port: None,
13571314
tags: None,
13581315
})
13591316
.expect("Could not add http frontend");
@@ -1369,6 +1326,7 @@ mod tests {
13691326
redirect_scheme: RedirectScheme::UseSame,
13701327
rewrite_host: None,
13711328
rewrite_path: None,
1329+
rewrite_port: None,
13721330
tags: None,
13731331
})
13741332
.expect("Could not add http frontend");
@@ -1384,6 +1342,7 @@ mod tests {
13841342
redirect_scheme: RedirectScheme::UseSame,
13851343
rewrite_host: None,
13861344
rewrite_path: None,
1345+
rewrite_port: None,
13871346
tags: None,
13881347
})
13891348
.expect("Could not add http frontend");

lib/src/https.rs

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -567,52 +567,13 @@ impl L7ListenerHandler for HttpsListener {
567567
fn frontend_from_request(
568568
&self,
569569
host: &str,
570-
uri: &str,
570+
path: &str,
571571
method: &Method,
572572
) -> Result<RouteResult, FrontendFromRequestError> {
573-
let start = Instant::now();
574-
let (remaining_input, (hostname, _)) = match hostname_and_port(host.as_bytes()) {
575-
Ok(tuple) => tuple,
576-
Err(parse_error) => {
577-
// parse_error contains a slice of given_host, which should NOT escape this scope
578-
return Err(FrontendFromRequestError::HostParse {
579-
host: host.to_owned(),
580-
error: parse_error.to_string(),
581-
});
582-
}
583-
};
584-
585-
if remaining_input != &b""[..] {
586-
return Err(FrontendFromRequestError::InvalidCharsAfterHost(
587-
host.to_owned(),
588-
));
589-
}
590-
591-
// it is alright to call from_utf8_unchecked,
592-
// we already verified that there are only ascii
593-
// chars in there
594-
let host = unsafe { from_utf8_unchecked(hostname) };
595-
596-
let route = self.fronts.lookup(host, uri, method).map_err(|e| {
573+
self.fronts.lookup(host, path, method).map_err(|e| {
597574
incr!("http.failed_backend_matching");
598575
FrontendFromRequestError::NoClusterFound(e)
599-
})?;
600-
601-
let now = Instant::now();
602-
603-
if let RouteResult::Flow {
604-
direction: RouteDirection::Forward(cluster_id),
605-
..
606-
} = &route
607-
{
608-
time!(
609-
"frontend_matching_time",
610-
cluster_id,
611-
(now - start).as_millis()
612-
);
613-
}
614-
615-
Ok(route)
576+
})
616577
}
617578
}
618579

lib/src/protocol/kawa_h1/mod.rs

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ use std::{
88
io::ErrorKind,
99
net::{Shutdown, SocketAddr},
1010
rc::{Rc, Weak},
11+
str::from_utf8_unchecked,
1112
time::{Duration, Instant},
1213
};
1314

1415
use mio::{net::TcpStream, Interest, Token};
16+
use parser::hostname_and_port;
1517
use rusty_ulid::Ulid;
1618
use sozu_command::{
1719
config::MAX_LOOP_ITERATIONS,
@@ -40,8 +42,9 @@ use crate::{
4042
sozu_command::{logging::LogContext, ready::Ready},
4143
timer::TimeoutContainer,
4244
AcceptError, BackendConnectAction, BackendConnectionError, BackendConnectionStatus,
43-
L7ListenerHandler, L7Proxy, ListenerHandler, Protocol, ProxySession, Readiness,
44-
RetrieveClusterError, SessionIsToBeClosed, SessionMetrics, SessionResult, StateResult,
45+
FrontendFromRequestError, L7ListenerHandler, L7Proxy, ListenerHandler, Protocol, ProxySession,
46+
Readiness, RetrieveClusterError, SessionIsToBeClosed, SessionMetrics, SessionResult,
47+
StateResult,
4548
};
4649

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

1272+
let (host, port) = match hostname_and_port(host.as_bytes()) {
1273+
Ok((b"", (hostname, port))) => (unsafe { from_utf8_unchecked(hostname) }, port),
1274+
Ok(_) => {
1275+
let host = host.to_owned();
1276+
self.set_answer(DefaultAnswer::Answer400 {
1277+
message: "Invalid characters after hostname, this should not happen.".into(),
1278+
phase: kawa::ParsingPhaseMarker::StatusLine,
1279+
successfully_parsed: "null".into(),
1280+
partially_parsed: "null".into(),
1281+
invalid: "null".into(),
1282+
});
1283+
return Err(RetrieveClusterError::RetrieveFrontend(
1284+
FrontendFromRequestError::InvalidCharsAfterHost(host),
1285+
));
1286+
}
1287+
Err(parse_error) => {
1288+
let host = host.to_owned();
1289+
let error = parse_error.to_string();
1290+
self.set_answer(DefaultAnswer::Answer400 {
1291+
message: "Could not parse port from hostname, this should not happen.".into(),
1292+
phase: kawa::ParsingPhaseMarker::StatusLine,
1293+
successfully_parsed: "null".into(),
1294+
partially_parsed: "null".into(),
1295+
invalid: "null".into(),
1296+
});
1297+
return Err(RetrieveClusterError::RetrieveFrontend(
1298+
FrontendFromRequestError::HostParse { host, error },
1299+
));
1300+
}
1301+
};
1302+
1303+
let start = Instant::now();
12691304
let route_result = self
12701305
.listener
12711306
.borrow()
@@ -1288,25 +1323,43 @@ impl<Front: SocketHandler, L: ListenerHandler + L7ListenerHandler> Http<Front, L
12881323
direction: flow,
12891324
rewritten_host,
12901325
rewritten_path,
1326+
rewritten_port,
12911327
} => {
12921328
let is_https = matches!(proxy.borrow().kind(), ListenerType::Https);
12931329
if let RouteDirection::Forward(cluster_id) = &flow {
1294-
if !is_https
1295-
&& proxy
1296-
.borrow()
1297-
.clusters()
1298-
.get(cluster_id)
1299-
.map(|cluster| cluster.https_redirect)
1300-
.unwrap_or(false)
1301-
{
1330+
time!(
1331+
"frontend_matching_time",
1332+
cluster_id,
1333+
start.elapsed().as_millis()
1334+
);
1335+
let (https_redirect, https_redirect_port, authentication) = proxy
1336+
.borrow()
1337+
.clusters()
1338+
.get(cluster_id)
1339+
.map(|cluster| (cluster.https_redirect, Some(8443), None::<()>))
1340+
.unwrap_or((false, None, None));
1341+
if !is_https && https_redirect {
1342+
let port =
1343+
https_redirect_port.map_or(String::new(), |port| format!(":{port}"));
13021344
self.set_answer(DefaultAnswer::Answer301 {
1303-
location: format!("https://{host}{path}"),
1345+
location: format!("https://{host}{port}{path}"),
13041346
});
13051347
return Err(RetrieveClusterError::Redirected);
13061348
}
1349+
if let Some(authentication) = authentication {
1350+
return Err(RetrieveClusterError::UnauthorizedRoute);
1351+
}
13071352
}
13081353
let host = rewritten_host.as_deref().unwrap_or(host);
13091354
let path = rewritten_path.as_deref().unwrap_or(path);
1355+
let port = rewritten_port.map_or_else(
1356+
|| {
1357+
port.map_or(String::new(), |port| {
1358+
format!(":{}", unsafe { from_utf8_unchecked(port) })
1359+
})
1360+
},
1361+
|port| format!(":{port}"),
1362+
);
13101363
match flow {
13111364
RouteDirection::Forward(cluster_id) => Ok(cluster_id),
13121365
RouteDirection::Permanent(redirect_scheme) => {
@@ -1319,7 +1372,7 @@ impl<Front: SocketHandler, L: ListenerHandler + L7ListenerHandler> Http<Front, L
13191372
}
13201373
};
13211374
self.set_answer(DefaultAnswer::Answer301 {
1322-
location: format!("{proto}://{host}{path}"),
1375+
location: format!("{proto}://{host}{port}{path}"),
13231376
});
13241377
Err(RetrieveClusterError::Redirected)
13251378
}

0 commit comments

Comments
 (0)