Skip to content

Commit 3e9820e

Browse files
authored
Merge pull request #94 from Eeshu-Yadav/fix/implement-auto-host-rewrite
Implement AutoHostRewrite functionality in request handling
2 parents fd56b8d + 2b301f6 commit 3e9820e

File tree

2 files changed

+141
-6
lines changed
  • orion-configuration/src/config/network_filters/http_connection_manager
  • orion-lib/src/listeners/http_connection_manager

2 files changed

+141
-6
lines changed

orion-configuration/src/config/network_filters/http_connection_manager/route.rs

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ use std::{
3838
str::FromStr,
3939
time::Duration,
4040
};
41-
4241
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
4342
#[serde(rename_all = "snake_case")]
4443
pub enum Action {
@@ -132,6 +131,47 @@ impl PathRewriteSpecifier {
132131
}
133132
}
134133

134+
impl AuthorityRewriteSpecifier {
135+
/// Applies authority rewrite based on the specifier type
136+
pub fn apply(
137+
&self,
138+
uri: &http::Uri,
139+
headers: &http::HeaderMap,
140+
upstream_authority: &Authority,
141+
) -> Option<Authority> {
142+
match self {
143+
AuthorityRewriteSpecifier::Authority(authority) => Some(authority.clone()),
144+
145+
AuthorityRewriteSpecifier::Header(header_name) => {
146+
let Some(header_value) = headers.get(header_name.as_str()) else {
147+
return None;
148+
};
149+
let Ok(header_str) = header_value.to_str() else {
150+
return None;
151+
};
152+
Authority::from_str(header_str).ok()
153+
},
154+
155+
AuthorityRewriteSpecifier::Regex(regex) => {
156+
let current_authority = uri
157+
.authority()
158+
.map(Authority::as_str)
159+
.or_else(|| headers.get(http::header::HOST).and_then(|h| h.to_str().ok()))
160+
.unwrap_or_default();
161+
162+
let replacement = regex.pattern.replace_all(current_authority, regex.substitution.as_str());
163+
if let std::borrow::Cow::Borrowed(_) = replacement {
164+
None
165+
} else {
166+
Authority::from_str(&replacement).ok()
167+
}
168+
},
169+
170+
AuthorityRewriteSpecifier::AutoHostRewrite => Some(upstream_authority.clone()),
171+
}
172+
}
173+
}
174+
135175
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
136176
pub struct RedirectAction {
137177
pub response_code: RedirectResponseCode,
@@ -649,6 +689,87 @@ mod tests {
649689
let result = route_match.match_request(&req);
650690
assert!(!result.matched());
651691
}
692+
693+
#[test]
694+
fn test_authority_rewrite_auto_host_rewrite() {
695+
let authority_rewrite = AuthorityRewriteSpecifier::AutoHostRewrite;
696+
let upstream_authority = Authority::from_str("upstream.example.com:8080").unwrap();
697+
let uri = "http://original.example.com/path".parse::<http::Uri>().unwrap();
698+
let mut headers = http::HeaderMap::new();
699+
headers.insert("host", "original.example.com".parse().unwrap());
700+
701+
let result = authority_rewrite.apply(&uri, &headers, &upstream_authority);
702+
assert_eq!(result, Some(upstream_authority));
703+
}
704+
705+
#[test]
706+
fn test_authority_rewrite_specific_authority() {
707+
let target_authority = Authority::from_str("target.example.com:9090").unwrap();
708+
let authority_rewrite = AuthorityRewriteSpecifier::Authority(target_authority.clone());
709+
let upstream_authority = Authority::from_str("upstream.example.com:8080").unwrap();
710+
let uri = "http://original.example.com/path".parse::<http::Uri>().unwrap();
711+
let mut headers = http::HeaderMap::new();
712+
headers.insert("host", "original.example.com".parse().unwrap());
713+
714+
let result = authority_rewrite.apply(&uri, &headers, &upstream_authority);
715+
assert_eq!(result, Some(target_authority));
716+
}
717+
718+
#[test]
719+
fn test_authority_rewrite_header() {
720+
let authority_rewrite = AuthorityRewriteSpecifier::Header("x-target-host".into());
721+
let upstream_authority = Authority::from_str("upstream.example.com:8080").unwrap();
722+
let uri = "http://original.example.com/path".parse::<http::Uri>().unwrap();
723+
let mut headers = http::HeaderMap::new();
724+
headers.insert("host", "original.example.com".parse().unwrap());
725+
headers.insert("x-target-host", "header.example.com:7070".parse().unwrap());
726+
727+
let result = authority_rewrite.apply(&uri, &headers, &upstream_authority);
728+
assert_eq!(result, Some(Authority::from_str("header.example.com:7070").unwrap()));
729+
}
730+
731+
#[test]
732+
fn test_authority_rewrite_header_missing() {
733+
let authority_rewrite = AuthorityRewriteSpecifier::Header("x-missing-header".into());
734+
let upstream_authority = Authority::from_str("upstream.example.com:8080").unwrap();
735+
let uri = "http://original.example.com/path".parse::<http::Uri>().unwrap();
736+
let mut headers = http::HeaderMap::new();
737+
headers.insert("host", "original.example.com".parse().unwrap());
738+
739+
let result = authority_rewrite.apply(&uri, &headers, &upstream_authority);
740+
assert_eq!(result, None);
741+
}
742+
743+
#[test]
744+
fn test_authority_rewrite_regex() {
745+
let regex = RegexMatchAndSubstitute {
746+
pattern: Regex::new(r"^([^.]+)\.original\.com$").unwrap(),
747+
substitution: "${1}.rewritten.com".into(),
748+
};
749+
let authority_rewrite = AuthorityRewriteSpecifier::Regex(regex);
750+
let upstream_authority = Authority::from_str("upstream.example.com:8080").unwrap();
751+
let uri = "http://api.original.com/path".parse::<http::Uri>().unwrap();
752+
let mut headers = http::HeaderMap::new();
753+
headers.insert("host", "api.original.com".parse().unwrap());
754+
755+
let result = authority_rewrite.apply(&uri, &headers, &upstream_authority);
756+
assert_eq!(result, Some(Authority::from_str("api.rewritten.com").unwrap()));
757+
}
758+
759+
#[test]
760+
fn test_authority_rewrite_regex_no_host_header() {
761+
let regex = RegexMatchAndSubstitute {
762+
pattern: Regex::new(r"^([^.]+)\.original\.com$").unwrap(),
763+
substitution: "${1}.rewritten.com".into(),
764+
};
765+
let authority_rewrite = AuthorityRewriteSpecifier::Regex(regex);
766+
let upstream_authority = Authority::from_str("upstream.example.com:8080").unwrap();
767+
let uri = "/path".parse::<http::Uri>().unwrap();
768+
let headers = http::HeaderMap::new();
769+
770+
let result = authority_rewrite.apply(&uri, &headers, &upstream_authority);
771+
assert_eq!(result, None);
772+
}
652773
}
653774

654775
#[cfg(feature = "envoy-conversions")]

orion-lib/src/listeners/http_connection_manager/route.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,30 @@ impl<'a> RequestHandler<(MatchedRequest<'a>, &HttpConnectionManager)> for &Route
9797
} else {
9898
None
9999
};
100-
if path_and_query_replacement.is_some() {
100+
101+
let authority_replacement = if let Some(authority_rewrite) = &self.authority_rewrite {
102+
authority_rewrite.apply(&parts.uri, &parts.headers, &svc_channel.upstream_authority)
103+
} else {
104+
None
105+
};
106+
107+
if path_and_query_replacement.is_some() || authority_replacement.is_some() {
101108
parts.uri = {
102-
let UriParts { scheme, authority, path_and_query: _, .. } = parts.uri.into_parts();
109+
let UriParts { scheme, authority, path_and_query, .. } = parts.uri.into_parts();
103110
let mut new_parts = UriParts::default();
104111
new_parts.scheme = scheme;
105-
new_parts.authority = authority;
106-
new_parts.path_and_query = path_and_query_replacement;
107-
Uri::from_parts(new_parts).with_context_msg("failed to replace request path_and_query")?
112+
new_parts.authority = authority_replacement.clone().or(authority);
113+
new_parts.path_and_query = path_and_query_replacement.or(path_and_query);
114+
Uri::from_parts(new_parts).with_context_msg("failed to replace request URI")?
108115
}
109116
}
117+
118+
if let Some(new_authority) = &authority_replacement {
119+
let header_value = http::HeaderValue::from_str(new_authority.as_str())
120+
.with_context_msg("failed to create Host header value")?;
121+
parts.headers.insert(http::header::HOST, header_value);
122+
}
123+
110124
Request::from_parts(parts, body.map_into())
111125
};
112126

0 commit comments

Comments
 (0)