fix(cli): respect SURFPOOL_STUDIO_HOST environment variable for server binding#699
fix(cli): respect SURFPOOL_STUDIO_HOST environment variable for server binding#699Amit5601 wants to merge 1 commit into
Conversation
| let final_bind_addr = std::env::var("SURFPOOL_STUDIO_HOST") | ||
| .map(|host| { | ||
| if host.contains(']') { | ||
| if host.rfind(':') > host.rfind(']') { | ||
| host | ||
| } else { | ||
| format!("{}:{}", host, default_port) | ||
| } | ||
| } else { | ||
| match host.matches(':').count() { | ||
| 0 => format!("{}:{}", host, default_port), | ||
| 1 => host, | ||
| _ => format!("[{}]:{}", host, default_port), | ||
| } | ||
| } | ||
| }) | ||
| .unwrap_or(network_binding); |
There was a problem hiding this comment.
No log when env var overrides the bind address
When SURFPOOL_STUDIO_HOST is set, the resolved bind address is silently substituted for network_binding with no log output. In Docker/containerised environments — exactly the use-case this PR targets — a missing or mis-typed env var will leave operators with no indication of which address the server actually tried to bind on. Adding an info!("Binding studio server to {} (from SURFPOOL_STUDIO_HOST)", final_bind_addr) line before the HttpServer::new(...) call would make this diagnosable without a debugger.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| let template_registry_wrapped = Data::new(RwLock::new(TemplateRegistry::new())); | ||
| let loaded_scenarios = Data::new(RwLock::new(LoadedScenarios::new())); | ||
|
|
||
| let default_port = network_binding.split(':').last().unwrap_or("18488"); |
There was a problem hiding this comment.
unwrap_or("18488") is unreachable — split on a non-empty string always yields at least one element
str::split on a non-empty network_binding string always returns at least one element, so .last() always returns Some(_), meaning .unwrap_or("18488") can never activate. If network_binding carries no colon (e.g. a bare hostname), default_port becomes the whole network_binding string and the formatted address will be malformed. Using rsplit_once makes the intent explicit and provides a real fallback.
| let default_port = network_binding.split(':').last().unwrap_or("18488"); | |
| let default_port = network_binding | |
| .rsplit_once(':') | |
| .map(|(_, port)| port) | |
| .unwrap_or("18488"); |
| match host.matches(':').count() { | ||
| 0 => format!("{}:{}", host, default_port), | ||
| 1 => host, | ||
| _ => format!("[{}]:{}", host, default_port), | ||
| } |
There was a problem hiding this comment.
Verbatim passthrough for the
count == 1 case doesn't guard against a missing port
When SURFPOOL_STUDIO_HOST contains exactly one colon (e.g. "myhost:" or ":") the value is returned as-is. actix-web will then fail at bind() with a generic "invalid address" error that gives no hint the env var is to blame. Verifying that the part after the colon is non-empty would surface the mis-configuration before the bind attempt.
| match host.matches(':').count() { | |
| 0 => format!("{}:{}", host, default_port), | |
| 1 => host, | |
| _ => format!("[{}]:{}", host, default_port), | |
| } | |
| match host.matches(':').count() { | |
| 0 => format!("{}:{}", host, default_port), | |
| 1 => { | |
| if host.rsplit_once(':').map_or(false, |(_, p)| !p.is_empty()) { | |
| host | |
| } else { | |
| format!("{}:{}", host.trim_end_matches(':'), default_port) | |
| } | |
| } | |
| _ => format!("[{}]:{}", host, default_port), | |
| } |
fixes #588
This resolves network binding restrictions when running the Studio backend within containerized environments (like Docker) or isolated local network architectures where an explicit host override is required.
@MicaiahReid @lgalabru Re-opening this cleanly.