Fix spec deviations and implement graceful shutdown drain
- Replace determine_if_https() with ProxyState.is_https field so X-Forwarded-Proto reflects the listener's protocol instead of guessing from the Host header - Return ProxyError::BadGateway with host/upstream context for non-connect upstream errors instead of bare StatusCode::BAD_GATEWAY - Implement InFlightCounter with RAII guard for tracking in-flight connections - Add drain_in_flight() to wait for connections to complete on shutdown, with configurable timeout before forcing exit - Mark review/core-components and review/integration-readiness as complete
This commit is contained in:
@@ -22,6 +22,7 @@ pub struct ProxyState {
|
||||
pub config: Arc<ArcSwap<DynamicConfig>>,
|
||||
pub http_client: Client<HttpConnector, Body>,
|
||||
pub https_client: Client<hyper_rustls::HttpsConnector<HttpConnector>, Body>,
|
||||
pub is_https: bool,
|
||||
}
|
||||
|
||||
async fn health_handler() -> impl IntoResponse {
|
||||
@@ -53,8 +54,8 @@ async fn proxy_handler(
|
||||
None => return ProxyError::UnknownHost.into_response(),
|
||||
};
|
||||
|
||||
let is_https = determine_if_https(host);
|
||||
inject_proxy_headers(req.headers_mut(), remote_addr, is_https);
|
||||
let host_owned = host.to_string();
|
||||
inject_proxy_headers(req.headers_mut(), remote_addr, state.is_https);
|
||||
remove_hop_by_hop(req.headers_mut());
|
||||
|
||||
let upstream_scheme = site.upstream_scheme.clone();
|
||||
@@ -89,24 +90,18 @@ async fn proxy_handler(
|
||||
if e.is_connect() {
|
||||
ProxyError::UpstreamConnection(e).into_response()
|
||||
} else {
|
||||
warn!(error = %e, "upstream request failed");
|
||||
StatusCode::BAD_GATEWAY.into_response()
|
||||
let upstream_addr = format!("{}://{}", upstream_scheme, upstream);
|
||||
ProxyError::BadGateway {
|
||||
host: host_owned,
|
||||
upstream: upstream_addr,
|
||||
}
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
Err(_) => ProxyError::UpstreamTimeout.into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_if_https(host: &str) -> bool {
|
||||
let port_str = host.split(':').nth(1);
|
||||
if let Some(port) = port_str {
|
||||
if let Ok(p) = port.parse::<u16>() {
|
||||
return p == 443;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn build_upstream_uri(scheme: &str, upstream: &str, original_uri: &Uri) -> Uri {
|
||||
let path = original_uri.path();
|
||||
let query = original_uri
|
||||
@@ -200,6 +195,7 @@ mod tests {
|
||||
))),
|
||||
http_client: create_http_client(),
|
||||
https_client: create_https_client(),
|
||||
is_https: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -298,26 +294,6 @@ mod tests {
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_determine_if_https_port_443() {
|
||||
assert!(determine_if_https("example.com:443"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_determine_if_https_port_80() {
|
||||
assert!(!determine_if_https("example.com:80"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_determine_if_https_no_port() {
|
||||
assert!(determine_if_https("example.com"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_determine_if_https_port_8443() {
|
||||
assert!(!determine_if_https("example.com:8443"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_upstream_uri_with_query() {
|
||||
let uri: Uri = "/path?foo=bar".parse().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user