fix(rustproxy-http): preserve original Host header when proxying and add X-Forwarded-* headers; add TLS WebSocket echo backend helper and integration test for terminate-and-reencrypt websocket
This commit is contained in:
@@ -404,6 +404,51 @@ impl HttpProxyService {
|
||||
}
|
||||
}
|
||||
|
||||
// Add standard reverse-proxy headers (X-Forwarded-*)
|
||||
{
|
||||
let original_host = parts.headers.get("host")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.unwrap_or("");
|
||||
let forwarded_proto = if route_match.route.action.tls.as_ref()
|
||||
.map(|t| matches!(t.mode,
|
||||
rustproxy_config::TlsMode::Terminate
|
||||
| rustproxy_config::TlsMode::TerminateAndReencrypt))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
"https"
|
||||
} else {
|
||||
"http"
|
||||
};
|
||||
|
||||
// X-Forwarded-For: append client IP to existing chain
|
||||
let client_ip = peer_addr.ip().to_string();
|
||||
let xff_value = if let Some(existing) = upstream_headers.get("x-forwarded-for") {
|
||||
format!("{}, {}", existing.to_str().unwrap_or(""), client_ip)
|
||||
} else {
|
||||
client_ip
|
||||
};
|
||||
if let Ok(val) = hyper::header::HeaderValue::from_str(&xff_value) {
|
||||
upstream_headers.insert(
|
||||
hyper::header::HeaderName::from_static("x-forwarded-for"),
|
||||
val,
|
||||
);
|
||||
}
|
||||
// X-Forwarded-Host: original Host header
|
||||
if let Ok(val) = hyper::header::HeaderValue::from_str(original_host) {
|
||||
upstream_headers.insert(
|
||||
hyper::header::HeaderName::from_static("x-forwarded-host"),
|
||||
val,
|
||||
);
|
||||
}
|
||||
// X-Forwarded-Proto: original client protocol
|
||||
if let Ok(val) = hyper::header::HeaderValue::from_str(forwarded_proto) {
|
||||
upstream_headers.insert(
|
||||
hyper::header::HeaderName::from_static("x-forwarded-proto"),
|
||||
val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to upstream with timeout (TLS if upstream.use_tls is set)
|
||||
let backend = if upstream.use_tls {
|
||||
match tokio::time::timeout(
|
||||
@@ -469,7 +514,7 @@ impl HttpProxyService {
|
||||
body: Incoming,
|
||||
upstream_headers: hyper::HeaderMap,
|
||||
upstream_path: &str,
|
||||
upstream: &crate::upstream_selector::UpstreamSelection,
|
||||
_upstream: &crate::upstream_selector::UpstreamSelection,
|
||||
route: &rustproxy_config::RouteConfig,
|
||||
route_id: Option<&str>,
|
||||
source_ip: &str,
|
||||
@@ -496,11 +541,6 @@ impl HttpProxyService {
|
||||
|
||||
if let Some(headers) = upstream_req.headers_mut() {
|
||||
*headers = upstream_headers;
|
||||
if let Ok(host_val) = hyper::header::HeaderValue::from_str(
|
||||
&format!("{}:{}", upstream.host, upstream.port)
|
||||
) {
|
||||
headers.insert(hyper::header::HOST, host_val);
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the request body in CountingBody to track bytes_in
|
||||
@@ -535,7 +575,7 @@ impl HttpProxyService {
|
||||
body: Incoming,
|
||||
upstream_headers: hyper::HeaderMap,
|
||||
upstream_path: &str,
|
||||
upstream: &crate::upstream_selector::UpstreamSelection,
|
||||
_upstream: &crate::upstream_selector::UpstreamSelection,
|
||||
route: &rustproxy_config::RouteConfig,
|
||||
route_id: Option<&str>,
|
||||
source_ip: &str,
|
||||
@@ -562,11 +602,6 @@ impl HttpProxyService {
|
||||
|
||||
if let Some(headers) = upstream_req.headers_mut() {
|
||||
*headers = upstream_headers;
|
||||
if let Ok(host_val) = hyper::header::HeaderValue::from_str(
|
||||
&format!("{}:{}", upstream.host, upstream.port)
|
||||
) {
|
||||
headers.insert(hyper::header::HOST, host_val);
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the request body in CountingBody to track bytes_in
|
||||
@@ -739,13 +774,44 @@ impl HttpProxyService {
|
||||
parts.method, upstream_path
|
||||
);
|
||||
|
||||
let upstream_host = format!("{}:{}", upstream.host, upstream.port);
|
||||
// Copy all original headers (preserving the client's Host header).
|
||||
// Skip X-Forwarded-* since we set them ourselves below.
|
||||
for (name, value) in parts.headers.iter() {
|
||||
if name == hyper::header::HOST {
|
||||
raw_request.push_str(&format!("host: {}\r\n", upstream_host));
|
||||
} else {
|
||||
raw_request.push_str(&format!("{}: {}\r\n", name, value.to_str().unwrap_or("")));
|
||||
let name_str = name.as_str();
|
||||
if name_str == "x-forwarded-for"
|
||||
|| name_str == "x-forwarded-host"
|
||||
|| name_str == "x-forwarded-proto"
|
||||
{
|
||||
continue;
|
||||
}
|
||||
raw_request.push_str(&format!("{}: {}\r\n", name, value.to_str().unwrap_or("")));
|
||||
}
|
||||
|
||||
// Add standard reverse-proxy headers (X-Forwarded-*)
|
||||
{
|
||||
let original_host = parts.headers.get("host")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.unwrap_or("");
|
||||
let forwarded_proto = if route.action.tls.as_ref()
|
||||
.map(|t| matches!(t.mode,
|
||||
rustproxy_config::TlsMode::Terminate
|
||||
| rustproxy_config::TlsMode::TerminateAndReencrypt))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
"https"
|
||||
} else {
|
||||
"http"
|
||||
};
|
||||
|
||||
let client_ip = peer_addr.ip().to_string();
|
||||
let xff_value = if let Some(existing) = parts.headers.get("x-forwarded-for") {
|
||||
format!("{}, {}", existing.to_str().unwrap_or(""), client_ip)
|
||||
} else {
|
||||
client_ip
|
||||
};
|
||||
raw_request.push_str(&format!("x-forwarded-for: {}\r\n", xff_value));
|
||||
raw_request.push_str(&format!("x-forwarded-host: {}\r\n", original_host));
|
||||
raw_request.push_str(&format!("x-forwarded-proto: {}\r\n", forwarded_proto));
|
||||
}
|
||||
|
||||
if let Some(ref route_headers) = route.headers {
|
||||
|
||||
Reference in New Issue
Block a user