fix: strip /agent/<name>/ via rewrite in named location
nginx forbids a URI part on proxy_pass inside named locations, so the split-mode `@<name>_dynamic` fallback (introduced when the gateway started serving static dist directly) failed config-test with: "proxy_pass" cannot have URI part in location given by regular expression, or inside named location Render two upstream forms: prefix locations keep the trailing slash so nginx auto-strips the location prefix; the named-location path strips `/agent/<name>/` via `rewrite ... break` and uses a bare upstream.
This commit is contained in:
parent
fe62db8917
commit
f9de183f4e
1 changed files with 51 additions and 7 deletions
|
|
@ -76,13 +76,22 @@ fn render(names: &[String], frontend_dir: Option<&str>) -> String {
|
|||
);
|
||||
for name in names {
|
||||
let port = lifecycle::agent_web_port(name);
|
||||
let upstream = if agent_sockets::ready_marker_for(name).exists() {
|
||||
format!(
|
||||
"http://unix:{}:/",
|
||||
agent_sockets::socket_path_for(name).display()
|
||||
// Two upstream forms because named locations (split mode's
|
||||
// `@<name>_dynamic`) forbid a URI part on `proxy_pass`. The
|
||||
// legacy prefix-location path keeps the trailing `/` so nginx
|
||||
// strips `/agent/<name>/` automatically; the named-location
|
||||
// path strips the prefix via `rewrite` and uses a bare upstream.
|
||||
let (upstream_prefix, upstream_bare) = if agent_sockets::ready_marker_for(name).exists() {
|
||||
let sock = agent_sockets::socket_path_for(name).display().to_string();
|
||||
(
|
||||
format!("http://unix:{sock}:/"),
|
||||
format!("http://unix:{sock}:"),
|
||||
)
|
||||
} else {
|
||||
format!("http://127.0.0.1:{port}/")
|
||||
(
|
||||
format!("http://127.0.0.1:{port}/"),
|
||||
format!("http://127.0.0.1:{port}"),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(frontend) = frontend_dir {
|
||||
|
|
@ -124,7 +133,8 @@ fn render(names: &[String], frontend_dir: Option<&str>) -> String {
|
|||
}}\n\
|
||||
\nlocation @{name}_dynamic {{\n\
|
||||
{PROXY_HEADER_BLOCK}\n\
|
||||
\n proxy_pass {upstream};\n\
|
||||
\n rewrite ^/agent/{name}/(.*)$ /$1 break;\n\
|
||||
\n proxy_pass {upstream_bare};\n\
|
||||
\n proxy_set_header X-Forwarded-Prefix /agent/{name};\n\
|
||||
\n proxy_buffering off;\n\
|
||||
\n proxy_read_timeout 1d;\n\
|
||||
|
|
@ -138,7 +148,7 @@ fn render(names: &[String], frontend_dir: Option<&str>) -> String {
|
|||
out,
|
||||
"\nlocation /agent/{name}/ {{\n\
|
||||
{PROXY_HEADER_BLOCK}\n\
|
||||
\n proxy_pass {upstream};\n\
|
||||
\n proxy_pass {upstream_prefix};\n\
|
||||
\n proxy_set_header X-Forwarded-Prefix /agent/{name};\n\
|
||||
\n proxy_buffering off;\n\
|
||||
\n proxy_read_timeout 1d;\n\
|
||||
|
|
@ -381,6 +391,40 @@ mod tests {
|
|||
assert!(body.contains("__hive_agent_unreachable"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_mode_named_location_strips_prefix_without_uri_part() {
|
||||
// Named locations forbid a URI part on proxy_pass — nginx rejects
|
||||
// `proxy_pass http://host/` inside `location @name`. Must use
|
||||
// bare upstream (no trailing `/` or path) plus a `rewrite` to
|
||||
// strip the /agent/<name>/ prefix.
|
||||
let names = vec!["iris".to_owned()];
|
||||
let body = render(&names, Some(FAKE_FRONTEND));
|
||||
assert!(
|
||||
body.contains("rewrite ^/agent/iris/(.*)$ /$1 break;"),
|
||||
"expected prefix-strip rewrite, got:\n{body}"
|
||||
);
|
||||
// The named-location proxy_pass must have no URI part (no
|
||||
// trailing slash or path). Extract the `@iris_dynamic { ... }`
|
||||
// block and check every proxy_pass directive in it.
|
||||
let block_start = body.find("location @iris_dynamic {").expect("named location");
|
||||
let block = &body[block_start..];
|
||||
let block_end = block.find("\n}\n").expect("block close");
|
||||
let block = &block[..block_end];
|
||||
for line in block.lines() {
|
||||
let line = line.trim();
|
||||
if let Some(rest) = line.strip_prefix("proxy_pass ") {
|
||||
let target = rest.trim_end_matches(';');
|
||||
// No URI part means: TCP form `http://host:port` (no
|
||||
// trailing `/`), UDS form `http://unix:/path:` (trailing
|
||||
// colon, nothing after). Both cases: must not end in `/`.
|
||||
assert!(
|
||||
!target.ends_with('/'),
|
||||
"named-location proxy_pass must have no URI part, got: {target}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_mode_includes_manager() {
|
||||
let names: Vec<String> = [MANAGER_NAME, "iris"]
|
||||
|
|
|
|||
Loading…
Reference in a new issue