mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-12 20:19:51 +08:00
fix(gateway): restore HTTP and WS upstream support
This commit is contained in:
@@ -129,9 +129,6 @@ pub(crate) fn normalize_admin_base_url(base_url: &str) -> Result<String, String>
|
||||
if parsed.host_str().is_none() {
|
||||
return Err("base_url 必须包含有效主机".to_string());
|
||||
}
|
||||
if !aether_http::is_https_or_loopback_http_url(&parsed) {
|
||||
return Err("base_url 必须使用 HTTPS;HTTP 仅允许字面量 loopback 主机".to_string());
|
||||
}
|
||||
if !parsed.username().is_empty() || parsed.password().is_some() {
|
||||
return Err("base_url 不允许包含用户名或密码".to_string());
|
||||
}
|
||||
@@ -154,16 +151,42 @@ mod normalize_admin_base_url_tests {
|
||||
"https://user:[email protected]/v1",
|
||||
"https://api.example.test/v1?key=secret",
|
||||
"https://api.example.test/v1#secret",
|
||||
"http://api.example.test/v1",
|
||||
"http://10.0.0.1/v1",
|
||||
"http://[::ffff:127.0.0.1]/v1",
|
||||
"http://user:password@api.example.test/v1",
|
||||
"http://api.example.test/v1?key=secret",
|
||||
"http://api.example.test/v1#secret",
|
||||
"ftp://api.example.test/v1",
|
||||
"file:///v1",
|
||||
"api.example.test/v1",
|
||||
"",
|
||||
"https://",
|
||||
"http://",
|
||||
"https://api.example.test:invalid/v1",
|
||||
] {
|
||||
assert!(normalize_admin_base_url(value).is_err(), "accepted {value}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn endpoint_base_url_accepts_remote_http_hosts() {
|
||||
for (raw_url, expected) in [
|
||||
(
|
||||
" HTTP://API.EXAMPLE.TEST:8080/v1/ ",
|
||||
"http://api.example.test:8080/v1",
|
||||
),
|
||||
("http://8.8.8.8:8080/v1/", "http://8.8.8.8:8080/v1"),
|
||||
("http://10.0.0.1:8080/v1/", "http://10.0.0.1:8080/v1"),
|
||||
(
|
||||
"http://[2606:4700:4700::1111]:8080/v1/",
|
||||
"http://[2606:4700:4700::1111]:8080/v1",
|
||||
),
|
||||
] {
|
||||
assert_eq!(
|
||||
normalize_admin_base_url(raw_url).expect("HTTP base URL should be accepted"),
|
||||
expected,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn endpoint_base_url_is_parsed_and_normalized() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -53,9 +53,6 @@ async fn resolve_test_connection_target(
|
||||
{
|
||||
return Err("provider endpoint must be an HTTP(S) URL without credentials or fragment");
|
||||
}
|
||||
if url.scheme() == "http" && !(allow_private_targets && literal_loopback) {
|
||||
return Err("provider endpoint must use HTTPS");
|
||||
}
|
||||
let host = url
|
||||
.host_str()
|
||||
.ok_or("provider endpoint is missing a host")?
|
||||
@@ -574,10 +571,11 @@ mod tests {
|
||||
async fn test_connection_target_rejects_private_addresses_in_production_mode() {
|
||||
for raw_url in [
|
||||
"http://127.0.0.1:8080/v1/chat/completions",
|
||||
"http://10.0.0.1/v1/chat/completions",
|
||||
"http://169.254.169.254/v1/chat/completions",
|
||||
"https://10.0.0.1/v1/chat/completions",
|
||||
"https://[::1]/v1/chat/completions",
|
||||
"https://localhost/v1/chat/completions",
|
||||
"http://8.8.8.8/v1/chat/completions",
|
||||
] {
|
||||
assert!(
|
||||
resolve_test_connection_target(raw_url, false)
|
||||
@@ -588,6 +586,26 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_connection_target_accepts_public_http_and_https_addresses() {
|
||||
for allow_private_targets in [false, true] {
|
||||
for (raw_url, expected_port) in [
|
||||
("http://8.8.8.8/v1/chat", 80),
|
||||
("http://8.8.8.8:8080/v1/chat", 8080),
|
||||
("https://8.8.8.8/v1/chat", 443),
|
||||
] {
|
||||
let target = resolve_test_connection_target(raw_url, allow_private_targets)
|
||||
.await
|
||||
.expect("public HTTP(S) provider target should resolve");
|
||||
assert_eq!(target.url.as_str(), raw_url);
|
||||
assert_eq!(target.host, "8.8.8.8");
|
||||
assert_eq!(target.addresses.len(), 1);
|
||||
assert_eq!(target.addresses[0].ip().to_string(), "8.8.8.8");
|
||||
assert_eq!(target.addresses[0].port(), expected_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_connection_target_allows_loopback_only_for_test_fixtures() {
|
||||
let target = resolve_test_connection_target("http://127.0.0.1:8080/v1/chat", true)
|
||||
@@ -596,10 +614,10 @@ mod tests {
|
||||
assert_eq!(target.host, "127.0.0.1");
|
||||
assert_eq!(target.addresses.len(), 1);
|
||||
assert!(
|
||||
resolve_test_connection_target("http://8.8.8.8/v1/chat", true)
|
||||
resolve_test_connection_target("http://10.0.0.1/v1/chat", true)
|
||||
.await
|
||||
.is_err(),
|
||||
"test mode must not make cleartext public endpoints acceptable"
|
||||
"test mode must not make private non-loopback HTTP endpoints acceptable"
|
||||
);
|
||||
assert!(
|
||||
resolve_test_connection_target("https://10.0.0.1/v1/chat", true)
|
||||
@@ -620,6 +638,9 @@ mod tests {
|
||||
for raw_url in [
|
||||
"https://user:[email protected]/v1/chat",
|
||||
"https://example.com/v1/chat#fragment",
|
||||
"http://user:[email protected]/v1/chat",
|
||||
"http://example.com/v1/chat#fragment",
|
||||
"ftp://example.com/v1/chat",
|
||||
] {
|
||||
assert!(
|
||||
resolve_test_connection_target(raw_url, false)
|
||||
|
||||
Reference in New Issue
Block a user