ferron/util/
env_config.rs1use std::env;
2use yaml_rust2::Yaml;
3
4pub fn apply_env_vars_to_config(yaml_config: &mut Yaml) {
7 let yaml_config_hash = match yaml_config.as_mut_hash() {
8 Some(h) => h,
9 None => return,
10 };
11
12 let global_yaml = match yaml_config_hash.get_mut(&Yaml::String("global".to_string())) {
13 Some(y) => y,
14 None => {
15 yaml_config_hash.insert(
16 Yaml::String("global".to_owned()),
17 Yaml::Hash(yaml_rust2::yaml::Hash::new()),
18 );
19 match yaml_config_hash.get_mut(&Yaml::String("global".to_string())) {
20 Some(y) => y,
21 None => return,
22 }
23 }
24 };
25
26 let global_hash = match global_yaml.as_mut_hash() {
27 Some(h) => h,
28 None => return,
29 };
30
31 if let Ok(port_val) = env::var("FERRON_PORT") {
33 if let Ok(port) = port_val.parse::<i64>() {
34 global_hash.insert(Yaml::String("port".into()), Yaml::Integer(port));
35 } else {
36 global_hash.insert(Yaml::String("port".into()), Yaml::String(port_val));
38 }
39 }
40
41 if let Ok(sport_val) = env::var("FERRON_SPORT") {
42 if let Ok(sport) = sport_val.parse::<i64>() {
43 global_hash.insert(Yaml::String("sport".into()), Yaml::Integer(sport));
44 } else {
45 global_hash.insert(Yaml::String("sport".into()), Yaml::String(sport_val));
47 }
48 }
49
50 let http2_initial_window = env::var("FERRON_HTTP2_INITIAL_WINDOW_SIZE")
52 .ok()
53 .and_then(|val| val.parse::<i64>().ok());
54 let http2_max_frame = env::var("FERRON_HTTP2_MAX_FRAME_SIZE")
55 .ok()
56 .and_then(|val| val.parse::<i64>().ok());
57 let http2_max_streams = env::var("FERRON_HTTP2_MAX_CONCURRENT_STREAMS")
58 .ok()
59 .and_then(|val| val.parse::<i64>().ok());
60 let http2_max_header = env::var("FERRON_HTTP2_MAX_HEADER_LIST_SIZE")
61 .ok()
62 .and_then(|val| val.parse::<i64>().ok());
63 let http2_enable_connect = env::var("FERRON_HTTP2_ENABLE_CONNECT_PROTOCOL")
64 .ok()
65 .map(|val| matches!(val.to_ascii_lowercase().as_str(), "1" | "true" | "yes"));
66
67 if http2_initial_window.is_some()
69 || http2_max_frame.is_some()
70 || http2_max_streams.is_some()
71 || http2_max_header.is_some()
72 || http2_enable_connect.is_some()
73 {
74 let mut http2_hash = yaml_rust2::yaml::Hash::new();
75
76 if let Some(size) = http2_initial_window {
78 http2_hash.insert(
79 Yaml::String("initialWindowSize".into()),
80 Yaml::Integer(size),
81 );
82 }
83
84 if let Some(size) = http2_max_frame {
85 http2_hash.insert(Yaml::String("maxFrameSize".into()), Yaml::Integer(size));
86 }
87
88 if let Some(streams) = http2_max_streams {
89 http2_hash.insert(
90 Yaml::String("maxConcurrentStreams".into()),
91 Yaml::Integer(streams),
92 );
93 }
94
95 if let Some(size) = http2_max_header {
96 http2_hash.insert(
97 Yaml::String("maxHeaderListSize".into()),
98 Yaml::Integer(size),
99 );
100 }
101
102 if let Some(enable) = http2_enable_connect {
103 http2_hash.insert(
104 Yaml::String("enableConnectProtocol".into()),
105 Yaml::Boolean(enable),
106 );
107 }
108
109 if !http2_hash.is_empty() {
111 global_hash.insert(Yaml::String("http2Settings".into()), Yaml::Hash(http2_hash));
112 }
113 }
114
115 if let Ok(path) = env::var("FERRON_LOG_FILE_PATH") {
117 global_hash.insert(Yaml::String("logFilePath".into()), Yaml::String(path));
118 }
119
120 if let Ok(path) = env::var("FERRON_ERROR_LOG_FILE_PATH") {
121 global_hash.insert(Yaml::String("errorLogFilePath".into()), Yaml::String(path));
122 }
123
124 if let Ok(cert) = env::var("FERRON_CERT") {
126 global_hash.insert(Yaml::String("cert".into()), Yaml::String(cert));
127 }
128
129 if let Ok(key) = env::var("FERRON_KEY") {
130 global_hash.insert(Yaml::String("key".into()), Yaml::String(key));
131 }
132
133 if let Ok(min_ver) = env::var("FERRON_TLS_MIN_VERSION") {
134 global_hash.insert(Yaml::String("tlsMinVersion".into()), Yaml::String(min_ver));
135 }
136
137 if let Ok(max_ver) = env::var("FERRON_TLS_MAX_VERSION") {
138 global_hash.insert(Yaml::String("tlsMaxVersion".into()), Yaml::String(max_ver));
139 }
140
141 if let Ok(v) = env::var("FERRON_SECURE") {
143 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
144 global_hash.insert(Yaml::String("secure".into()), Yaml::Boolean(enable));
145 }
146
147 if let Ok(v) = env::var("FERRON_ENABLE_HTTP2") {
148 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
149 global_hash.insert(Yaml::String("enableHTTP2".into()), Yaml::Boolean(enable));
150 }
151
152 if let Ok(v) = env::var("FERRON_ENABLE_HTTP3") {
153 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
154 global_hash.insert(Yaml::String("enableHTTP3".into()), Yaml::Boolean(enable));
155 }
156
157 if let Ok(v) = env::var("FERRON_DISABLE_NON_ENCRYPTED_SERVER") {
158 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
159 global_hash.insert(
160 Yaml::String("disableNonEncryptedServer".into()),
161 Yaml::Boolean(enable),
162 );
163 }
164
165 if let Ok(v) = env::var("FERRON_ENABLE_OCSP_STAPLING") {
166 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
167 global_hash.insert(
168 Yaml::String("enableOCSPStapling".into()),
169 Yaml::Boolean(enable),
170 );
171 }
172
173 if let Ok(v) = env::var("FERRON_ENABLE_DIRECTORY_LISTING") {
174 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
175 global_hash.insert(
176 Yaml::String("enableDirectoryListing".into()),
177 Yaml::Boolean(enable),
178 );
179 }
180
181 if let Ok(v) = env::var("FERRON_ENABLE_COMPRESSION") {
182 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
183 global_hash.insert(
184 Yaml::String("enableCompression".into()),
185 Yaml::Boolean(enable),
186 );
187 }
188
189 if let Ok(list) = env::var("FERRON_LOAD_MODULES") {
191 let arr: Vec<Yaml> = list
192 .split(',')
193 .filter_map(|s| {
194 let t = s.trim();
195 if t.is_empty() {
196 None
197 } else {
198 Some(Yaml::String(t.to_string()))
199 }
200 })
201 .collect();
202 if !arr.is_empty() {
203 global_hash.insert(Yaml::String("loadModules".into()), Yaml::Array(arr));
204 }
205 }
206
207 if let Ok(list) = env::var("FERRON_BLOCKLIST") {
209 let arr: Vec<Yaml> = list
210 .split(',')
211 .filter_map(|s| {
212 let t = s.trim();
213 if t.is_empty() {
214 None
215 } else {
216 Some(Yaml::String(t.to_string()))
217 }
218 })
219 .collect();
220 if !arr.is_empty() {
221 global_hash.insert(Yaml::String("blocklist".into()), Yaml::Array(arr));
222 }
223 }
224
225 if let Ok(sni_hosts) = env::var("FERRON_SNI_HOSTS") {
227 let hosts: Vec<&str> = sni_hosts
228 .split(',')
229 .map(|s| s.trim())
230 .filter(|s| !s.is_empty())
231 .collect();
232
233 if !hosts.is_empty() {
234 let mut sni_hash = yaml_rust2::yaml::Hash::new();
235
236 for host in hosts {
237 let cert_env_var = format!(
238 "FERRON_SNI_{}_CERT",
239 host
240 .replace('.', "_")
241 .replace('*', "WILDCARD")
242 .to_uppercase()
243 );
244 let key_env_var = format!(
245 "FERRON_SNI_{}_KEY",
246 host
247 .replace('.', "_")
248 .replace('*', "WILDCARD")
249 .to_uppercase()
250 );
251
252 if let (Ok(cert), Ok(key)) = (env::var(&cert_env_var), env::var(&key_env_var)) {
253 let mut host_hash = yaml_rust2::yaml::Hash::new();
254 host_hash.insert(Yaml::String("cert".into()), Yaml::String(cert));
255 host_hash.insert(Yaml::String("key".into()), Yaml::String(key));
256 sni_hash.insert(Yaml::String(host.to_string()), Yaml::Hash(host_hash));
257 }
258 }
259
260 if !sni_hash.is_empty() {
261 global_hash.insert(Yaml::String("sni".into()), Yaml::Hash(sni_hash));
262 }
263 }
264 }
265
266 if let Ok(env_list) = env::var("FERRON_ENV_VARS") {
268 let vars: Vec<&str> = env_list
269 .split(',')
270 .map(|s| s.trim())
271 .filter(|s| !s.is_empty())
272 .collect();
273
274 if !vars.is_empty() {
275 let mut env_hash = yaml_rust2::yaml::Hash::new();
276
277 for var_name in vars {
278 let env_var = format!("FERRON_ENV_{}", var_name.to_uppercase());
279
280 if let Ok(value) = env::var(&env_var) {
281 env_hash.insert(Yaml::String(var_name.to_string()), Yaml::String(value));
282 }
283 }
284
285 if !env_hash.is_empty() {
286 global_hash.insert(
287 Yaml::String("environmentVariables".into()),
288 Yaml::Hash(env_hash),
289 );
290 }
291 }
292 }
293}
294
295pub fn log_env_var_overrides() -> Vec<String> {
297 env::vars()
298 .filter(|(k, _)| k.starts_with("FERRON_"))
299 .map(|(k, v)| format!("Environment override: {k}={v}"))
300 .collect()
301}