running_process/broker/
broker_http_port.rs1use std::net::{IpAddr, Ipv4Addr};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum BrokerHttpPort {
19 Static {
21 port: u16,
23 },
24 Dynamic,
26 StaticOrFallback {
28 preferred: u16,
30 },
31}
32
33pub const PORT_OVERRIDE_ENV: &str = "RUNNING_PROCESS_BROKER_HTTP_PORT";
37
38pub const BIND_OVERRIDE_ENV: &str = "RUNNING_PROCESS_BROKER_HTTP_BIND";
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct ResolvedHttpBind {
45 pub port: BrokerHttpPort,
47 pub addr: IpAddr,
49}
50
51impl BrokerHttpPort {
52 pub fn resolve(config: BrokerHttpPort) -> ResolvedHttpBind {
64 let port = match parse_port_env() {
65 Some(p) => BrokerHttpPort::Static { port: p },
66 None => config,
67 };
68 let addr = parse_bind_env().unwrap_or(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
69 ResolvedHttpBind { port, addr }
70 }
71}
72
73fn parse_port_env() -> Option<u16> {
74 crate::env_vars::BROKER_HTTP_PORT.port()
76}
77
78fn parse_bind_env() -> Option<IpAddr> {
79 crate::env_vars::BROKER_HTTP_BIND
80 .text()?
81 .trim()
82 .parse::<IpAddr>()
83 .ok()
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use std::env;
90 use std::sync::Mutex;
91
92 static ENV_LOCK: Mutex<()> = Mutex::new(());
96
97 fn with_env<F: FnOnce()>(port: Option<&str>, bind: Option<&str>, f: F) {
98 let _g = ENV_LOCK.lock().expect("env mutex poisoned");
99 let prev_port = env::var(PORT_OVERRIDE_ENV).ok();
101 let prev_bind = env::var(BIND_OVERRIDE_ENV).ok();
102 match port {
103 Some(p) => env::set_var(PORT_OVERRIDE_ENV, p),
104 None => env::remove_var(PORT_OVERRIDE_ENV),
105 }
106 match bind {
107 Some(b) => env::set_var(BIND_OVERRIDE_ENV, b),
108 None => env::remove_var(BIND_OVERRIDE_ENV),
109 }
110 f();
111 match prev_port {
113 Some(p) => env::set_var(PORT_OVERRIDE_ENV, p),
114 None => env::remove_var(PORT_OVERRIDE_ENV),
115 }
116 match prev_bind {
117 Some(b) => env::set_var(BIND_OVERRIDE_ENV, b),
118 None => env::remove_var(BIND_OVERRIDE_ENV),
119 }
120 }
121
122 #[test]
123 fn no_env_returns_config_and_loopback_default() {
124 with_env(None, None, || {
125 let r = BrokerHttpPort::resolve(BrokerHttpPort::Dynamic);
126 assert_eq!(r.port, BrokerHttpPort::Dynamic);
127 assert_eq!(r.addr, IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
128 });
129 }
130
131 #[test]
132 fn port_env_set_overrides_to_static() {
133 with_env(Some("8080"), None, || {
134 let r = BrokerHttpPort::resolve(BrokerHttpPort::StaticOrFallback { preferred: 12_345 });
135 assert_eq!(r.port, BrokerHttpPort::Static { port: 8080 });
136 });
137 }
138
139 #[test]
140 fn bind_env_set_overrides_addr() {
141 with_env(None, Some("0.0.0.0"), || {
142 let r = BrokerHttpPort::resolve(BrokerHttpPort::Static { port: 4242 });
143 assert_eq!(r.port, BrokerHttpPort::Static { port: 4242 });
144 assert_eq!(r.addr, IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)));
145 });
146 }
147
148 #[test]
149 fn invalid_port_env_falls_back_to_config() {
150 with_env(Some("not-a-port"), None, || {
151 let r = BrokerHttpPort::resolve(BrokerHttpPort::Dynamic);
152 assert_eq!(r.port, BrokerHttpPort::Dynamic);
153 });
154 }
155
156 #[test]
157 fn empty_port_env_falls_back_to_config() {
158 with_env(Some(""), None, || {
159 let r = BrokerHttpPort::resolve(BrokerHttpPort::Dynamic);
160 assert_eq!(r.port, BrokerHttpPort::Dynamic);
161 });
162 }
163
164 #[test]
165 fn invalid_bind_env_falls_back_to_loopback() {
166 with_env(None, Some("not-an-ip"), || {
167 let r = BrokerHttpPort::resolve(BrokerHttpPort::Dynamic);
168 assert_eq!(r.addr, IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
169 });
170 }
171
172 #[test]
173 fn both_env_overrides_compose() {
174 with_env(Some("9999"), Some("0.0.0.0"), || {
175 let r = BrokerHttpPort::resolve(BrokerHttpPort::Dynamic);
176 assert_eq!(r.port, BrokerHttpPort::Static { port: 9999 });
177 assert_eq!(r.addr, IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)));
178 });
179 }
180}