Skip to content

Commit 0722e78

Browse files
committed
Refactor Route, RouteResult and redirection flow
Signed-off-by: Eloi DEMOLIS <eloi.demolis@clever-cloud.com>
1 parent 663b67f commit 0722e78

File tree

8 files changed

+249
-220
lines changed

8 files changed

+249
-220
lines changed

command/assets/custom_200.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
HTTP/1.1 200 OK
2+
%Content-Length: %CONTENT_LENGTH
3+
Sozu-Id: %REQUEST_ID
4+
5+
<h1>%CLUSTER_ID Custom 200</h1>
6+
<p>original url: %ROUTE</p>
7+
<p>rewritten url: %REDIRECT_LOCATION</p>

command/assets/custom_404.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
HTTP/1.1 404 Not Found
22
Cache-Control: no-cache
33
Connection: close
4-
Sozu-Id: %SOZU_ID
4+
Sozu-Id: %REQUEST_ID
55

66
<h1>My own 404 error page</h1>
7-
<p>Your request %SOZU_ID found no frontend and cannot be redirected.</p>
7+
<p>Your request %REQUEST_ID found no frontend and cannot be redirected.</p>

command/assets/custom_503.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
Cache-Control: no-cache
33
Connection: close
44
%Content-Length: %CONTENT_LENGTH
5-
Sozu-Id: %SOZU_ID
5+
Sozu-Id: %REQUEST_ID
66

77
<h1>MyCluster: 503 Service Unavailable</h1>
8-
<p>No server seems to be alive, could not redirect request %SOZU_ID.</p>
8+
<p>No server seems to be alive, could not redirect request %REQUEST_ID.</p>
99
<pre>
10-
%DETAILS
10+
%MESSAGE
1111
<pre>

lib/src/http.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ use std::{
22
cell::RefCell,
33
collections::{hash_map::Entry, BTreeMap, HashMap},
44
io::ErrorKind,
5-
mem,
65
net::{Shutdown, SocketAddr},
76
os::unix::io::AsRawFd,
87
rc::{Rc, Weak},
9-
str::from_utf8_unchecked,
108
time::{Duration, Instant},
119
};
1210

@@ -32,15 +30,11 @@ use crate::{
3230
backends::BackendMap,
3331
pool::Pool,
3432
protocol::{
35-
http::{
36-
answers::HttpAnswers,
37-
parser::{hostname_and_port, Method},
38-
ResponseStream,
39-
},
33+
http::{answers::HttpAnswers, parser::Method, ResponseStream},
4034
proxy_protocol::expect::ExpectProxyProtocol,
4135
Http, Pipe, SessionState,
4236
},
43-
router::{RouteDirection, RouteResult, Router},
37+
router::{RouteResult, Router},
4438
server::{ListenToken, SessionManager},
4539
socket::server_bind,
4640
timer::TimeoutContainer,

lib/src/https.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55
net::{Shutdown, SocketAddr as StdSocketAddr},
66
os::unix::io::AsRawFd,
77
rc::{Rc, Weak},
8-
str::{from_utf8, from_utf8_unchecked},
8+
str::from_utf8,
99
sync::Arc,
1010
time::{Duration, Instant},
1111
};
@@ -53,16 +53,12 @@ use crate::{
5353
pool::Pool,
5454
protocol::{
5555
h2::Http2,
56-
http::{
57-
answers::HttpAnswers,
58-
parser::{hostname_and_port, Method},
59-
ResponseStream,
60-
},
56+
http::{answers::HttpAnswers, parser::Method, ResponseStream},
6157
proxy_protocol::expect::ExpectProxyProtocol,
6258
rustls::TlsHandshake,
6359
Http, Pipe, SessionState,
6460
},
65-
router::{RouteDirection, RouteResult, Router},
61+
router::{RouteResult, Router},
6662
server::{ListenToken, SessionManager},
6763
socket::{server_bind, FrontRustls},
6864
timer::TimeoutContainer,

lib/src/protocol/kawa_h1/editor.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub struct HttpContext {
2323
pub keep_alive_frontend: bool,
2424
/// the value of the sticky session cookie in the request
2525
pub sticky_session_found: Option<String>,
26+
/// position of the last authentication header, only valid until prepare is called
27+
pub authentication_found: Option<usize>,
2628
// ---------- Status Line
2729
/// the value of the method in the request line
2830
pub method: Option<Method>,
@@ -135,7 +137,7 @@ impl HttpContext {
135137
let mut has_x_port = false;
136138
let mut has_x_proto = false;
137139
let mut has_connection = false;
138-
for block in &mut request.blocks {
140+
for (i, block) in request.blocks.iter_mut().enumerate() {
139141
match block {
140142
kawa::Block::Header(header) if !header.is_elided() => {
141143
let key = header.key.data(buf);
@@ -182,6 +184,8 @@ impl HttpContext {
182184
.data_opt(buf)
183185
.and_then(|data| from_utf8(data).ok())
184186
.map(ToOwned::to_owned);
187+
} else if compare_no_case(key, b"Proxy-Authenticate") {
188+
self.authentication_found = Some(i);
185189
}
186190
}
187191
_ => {}

lib/src/protocol/kawa_h1/mod.rs

Lines changed: 78 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rusty_ulid::Ulid;
1818
use sozu_command::{
1919
config::MAX_LOOP_ITERATIONS,
2020
logging::EndpointRecord,
21-
proto::command::{Event, EventKind, ListenerType, RedirectScheme},
21+
proto::command::{Event, EventKind, ListenerType, RedirectPolicy, RedirectScheme},
2222
};
2323
// use time::{Duration, Instant};
2424

@@ -36,7 +36,7 @@ use crate::{
3636
SessionState,
3737
},
3838
retry::RetryPolicy,
39-
router::{RouteDirection, RouteResult},
39+
router::RouteResult,
4040
server::{push_event, CONN_RETRIES},
4141
socket::{stats::socket_rtt, SocketHandler, SocketResult, TransportProtocol},
4242
sozu_command::{logging::LogContext, ready::Ready},
@@ -245,6 +245,7 @@ impl<Front: SocketHandler, L: ListenerHandler + L7ListenerHandler> Http<Front, L
245245
sticky_name,
246246
sticky_session: None,
247247
sticky_session_found: None,
248+
authentication_found: None,
248249

249250
method: None,
250251
authority: None,
@@ -1295,91 +1296,92 @@ impl<Front: SocketHandler, L: ListenerHandler + L7ListenerHandler> Http<Front, L
12951296
.borrow()
12961297
.frontend_from_request(host, path, method);
12971298

1298-
let route = match route_result {
1299+
let RouteResult {
1300+
cluster_id,
1301+
redirect,
1302+
redirect_scheme,
1303+
redirect_template,
1304+
rewritten_host,
1305+
rewritten_path,
1306+
rewritten_port,
1307+
} = match route_result {
12991308
Ok(route) => route,
13001309
Err(frontend_error) => {
13011310
self.set_answer(DefaultAnswer::Answer404 {});
13021311
return Err(RetrieveClusterError::RetrieveFrontend(frontend_error));
13031312
}
13041313
};
13051314

1306-
match route {
1307-
RouteResult::Deny => {
1315+
if let Some(cluster_id) = &cluster_id {
1316+
time!(
1317+
"frontend_matching_time",
1318+
cluster_id,
1319+
start.elapsed().as_millis()
1320+
);
1321+
}
1322+
1323+
let host = rewritten_host.as_deref().unwrap_or(host);
1324+
let path = rewritten_path.as_deref().unwrap_or(path);
1325+
let port = rewritten_port.map_or_else(
1326+
|| {
1327+
port.map_or(String::new(), |port| {
1328+
format!(":{}", unsafe { from_utf8_unchecked(port) })
1329+
})
1330+
},
1331+
|port| format!(":{port}"),
1332+
);
1333+
let is_https = matches!(proxy.borrow().kind(), ListenerType::Https);
1334+
let proto = match (redirect_scheme, is_https) {
1335+
(RedirectScheme::UseHttp, _) | (RedirectScheme::UseSame, false) => "http",
1336+
(RedirectScheme::UseHttps, _) | (RedirectScheme::UseSame, true) => "https",
1337+
};
1338+
1339+
match (cluster_id, redirect, redirect_template) {
1340+
(_, RedirectPolicy::Unauthorized, _) | (None, RedirectPolicy::Forward, None) => {
13081341
self.set_answer(DefaultAnswer::Answer401 {});
1309-
Err(RetrieveClusterError::UnauthorizedRoute)
1342+
return Err(RetrieveClusterError::UnauthorizedRoute);
13101343
}
1311-
RouteResult::Flow {
1312-
direction: flow,
1313-
rewritten_host,
1314-
rewritten_path,
1315-
rewritten_port,
1316-
} => {
1317-
let is_https = matches!(proxy.borrow().kind(), ListenerType::Https);
1318-
if let RouteDirection::Forward(cluster_id) = &flow {
1319-
time!(
1320-
"frontend_matching_time",
1321-
cluster_id,
1322-
start.elapsed().as_millis()
1323-
);
1324-
let (https_redirect, https_redirect_port, authentication) = proxy
1325-
.borrow()
1326-
.clusters()
1327-
.get(cluster_id)
1328-
.map(|cluster| {
1329-
(
1330-
cluster.https_redirect,
1331-
cluster.https_redirect_port,
1332-
None::<()>,
1333-
)
1334-
})
1335-
.unwrap_or((false, None, None));
1336-
if !is_https && https_redirect {
1337-
let port = https_redirect_port
1338-
.map_or(String::new(), |port| format!(":{}", port as u16));
1339-
self.set_answer(DefaultAnswer::Answer301 {
1340-
location: format!("https://{host}{port}{path}"),
1341-
});
1342-
return Err(RetrieveClusterError::Redirected);
1343-
}
1344-
if let Some(authentication) = authentication {
1345-
return Err(RetrieveClusterError::UnauthorizedRoute);
1346-
}
1344+
(_, RedirectPolicy::Permanent, _) => {
1345+
self.set_answer(DefaultAnswer::Answer301 {
1346+
location: format!("{proto}://{host}{port}{path}"),
1347+
});
1348+
Err(RetrieveClusterError::Redirected)
1349+
}
1350+
(_, RedirectPolicy::Temporary, _) => todo!(),
1351+
(cluster_id, RedirectPolicy::Forward, Some(name)) => {
1352+
let location = format!("{proto}://{host}{port}{path}");
1353+
// TODO: this feels wrong
1354+
self.context.cluster_id = cluster_id;
1355+
self.set_answer(DefaultAnswer::AnswerCustom { name, location });
1356+
Err(RetrieveClusterError::Redirected)
1357+
}
1358+
(Some(cluster_id), RedirectPolicy::Forward, None) => {
1359+
let (https_redirect, https_redirect_port, authentication) = proxy
1360+
.borrow()
1361+
.clusters()
1362+
.get(&cluster_id)
1363+
.map(|cluster| {
1364+
(
1365+
cluster.https_redirect,
1366+
cluster.https_redirect_port,
1367+
None::<()>,
1368+
)
1369+
})
1370+
.unwrap_or((false, None, None));
1371+
if !is_https && https_redirect {
1372+
let port = rewritten_port
1373+
.or_else(|| https_redirect_port.map(|port| port as u16))
1374+
.map_or(String::new(), |port| format!(":{port}"));
1375+
self.set_answer(DefaultAnswer::Answer301 {
1376+
location: format!("https://{host}{port}{path}"),
1377+
});
1378+
return Err(RetrieveClusterError::Redirected);
13471379
}
1348-
let host = rewritten_host.as_deref().unwrap_or(host);
1349-
let path = rewritten_path.as_deref().unwrap_or(path);
1350-
let port = rewritten_port.map_or_else(
1351-
|| {
1352-
port.map_or(String::new(), |port| {
1353-
format!(":{}", unsafe { from_utf8_unchecked(port) })
1354-
})
1355-
},
1356-
|port| format!(":{port}"),
1357-
);
1358-
match flow {
1359-
RouteDirection::Forward(cluster_id) => Ok(cluster_id),
1360-
RouteDirection::Permanent(redirect_scheme) => {
1361-
let proto = match (redirect_scheme, is_https) {
1362-
(RedirectScheme::UseHttp, _) | (RedirectScheme::UseSame, false) => {
1363-
"http"
1364-
}
1365-
(RedirectScheme::UseHttps, _) | (RedirectScheme::UseSame, true) => {
1366-
"https"
1367-
}
1368-
};
1369-
self.set_answer(DefaultAnswer::Answer301 {
1370-
location: format!("{proto}://{host}{port}{path}"),
1371-
});
1372-
Err(RetrieveClusterError::Redirected)
1373-
}
1374-
RouteDirection::Temporary(_) => todo!(),
1375-
RouteDirection::Template(cluster_id, name) => {
1376-
let location = format!("{host}{port}{path}");
1377-
// TODO: this feels wrong
1378-
self.context.cluster_id = cluster_id;
1379-
self.set_answer(DefaultAnswer::AnswerCustom { name, location });
1380-
Err(RetrieveClusterError::Redirected)
1381-
}
1380+
if let Some(authentication) = authentication {
1381+
self.set_answer(DefaultAnswer::Answer401 {});
1382+
return Err(RetrieveClusterError::UnauthorizedRoute);
13821383
}
1384+
return Ok(cluster_id);
13831385
}
13841386
}
13851387
}

0 commit comments

Comments
 (0)