1#[macro_use]
114mod ident;
115mod config;
116mod shutdown;
117mod ip_header;
118
119#[cfg(feature = "tls")]
120mod tls;
121
122#[cfg(feature = "secrets")]
123mod secret_key;
124
125#[doc(hidden)]
126pub use config::{pretty_print_error, bail_with_config_error};
127
128pub use config::Config;
129pub use crate::log::LogLevel;
130pub use shutdown::Shutdown;
131pub use ident::Ident;
132
133#[cfg(feature = "tls")]
134pub use tls::{TlsConfig, CipherSuite};
135
136#[cfg(feature = "mtls")]
137pub use tls::MutualTls;
138
139#[cfg(feature = "secrets")]
140pub use secret_key::SecretKey;
141
142#[cfg(unix)]
143pub use shutdown::Sig;
144
145#[cfg(test)]
146mod tests {
147 use std::net::Ipv4Addr;
148 use figment::{Figment, Profile};
149 use pretty_assertions::assert_eq;
150
151 use crate::log::LogLevel;
152 use crate::data::{Limits, ToByteUnit};
153 use crate::config::Config;
154
155 #[test]
156 fn test_figment_is_default() {
157 figment::Jail::expect_with(|_| {
158 let mut default: Config = Config::figment().extract().unwrap();
159 default.profile = Config::default().profile;
160 assert_eq!(default, Config::default());
161 Ok(())
162 });
163 }
164
165 #[test]
166 fn test_default_round_trip() {
167 figment::Jail::expect_with(|_| {
168 let original = Config::figment();
169 let roundtrip = Figment::from(Config::from(&original));
170 for figment in &[original, roundtrip] {
171 let config = Config::from(figment);
172 assert_eq!(config, Config::default());
173 }
174
175 Ok(())
176 });
177 }
178
179 #[test]
180 fn test_profile_env() {
181 figment::Jail::expect_with(|jail| {
182 jail.set_env("ROCKET_PROFILE", "debug");
183 let figment = Config::figment();
184 assert_eq!(figment.profile(), "debug");
185
186 jail.set_env("ROCKET_PROFILE", "release");
187 let figment = Config::figment();
188 assert_eq!(figment.profile(), "release");
189
190 jail.set_env("ROCKET_PROFILE", "random");
191 let figment = Config::figment();
192 assert_eq!(figment.profile(), "random");
193
194 Ok(())
195 });
196 }
197
198 #[test]
199 fn test_toml_file() {
200 figment::Jail::expect_with(|jail| {
201 jail.create_file("Rocket.toml", r#"
202 [default]
203 address = "1.2.3.4"
204 ident = "Something Cool"
205 port = 1234
206 workers = 20
207 keep_alive = 10
208 log_level = "off"
209 cli_colors = 0
210 "#)?;
211
212 let config = Config::from(Config::figment());
213 assert_eq!(config, Config {
214 address: Ipv4Addr::new(1, 2, 3, 4).into(),
215 port: 1234,
216 workers: 20,
217 ident: ident!("Something Cool"),
218 keep_alive: 10,
219 log_level: LogLevel::Off,
220 cli_colors: false,
221 ..Config::default()
222 });
223
224 jail.create_file("Rocket.toml", r#"
225 [global]
226 address = "1.2.3.4"
227 ident = "Something Else Cool"
228 port = 1234
229 workers = 20
230 keep_alive = 10
231 log_level = "off"
232 cli_colors = 0
233 "#)?;
234
235 let config = Config::from(Config::figment());
236 assert_eq!(config, Config {
237 address: Ipv4Addr::new(1, 2, 3, 4).into(),
238 port: 1234,
239 workers: 20,
240 ident: ident!("Something Else Cool"),
241 keep_alive: 10,
242 log_level: LogLevel::Off,
243 cli_colors: false,
244 ..Config::default()
245 });
246
247 jail.set_env("ROCKET_CONFIG", "Other.toml");
248 jail.create_file("Other.toml", r#"
249 [default]
250 address = "1.2.3.4"
251 port = 1234
252 workers = 20
253 keep_alive = 10
254 log_level = "off"
255 cli_colors = 0
256 "#)?;
257
258 let config = Config::from(Config::figment());
259 assert_eq!(config, Config {
260 address: Ipv4Addr::new(1, 2, 3, 4).into(),
261 port: 1234,
262 workers: 20,
263 keep_alive: 10,
264 log_level: LogLevel::Off,
265 cli_colors: false,
266 ..Config::default()
267 });
268
269 Ok(())
270 });
271 }
272
273 #[test]
274 #[cfg(feature = "tls")]
275 fn test_tls_config_from_file() {
276 use crate::config::{TlsConfig, CipherSuite, Ident, Shutdown};
277
278 figment::Jail::expect_with(|jail| {
279 jail.create_file("Rocket.toml", r#"
280 [global]
281 shutdown.ctrlc = 0
282 ident = false
283
284 [global.tls]
285 certs = "/ssl/cert.pem"
286 key = "/ssl/key.pem"
287
288 [global.limits]
289 forms = "1mib"
290 json = "10mib"
291 stream = "50kib"
292 "#)?;
293
294 let config = Config::from(Config::figment());
295 assert_eq!(config, Config {
296 shutdown: Shutdown { ctrlc: false, ..Default::default() },
297 ident: Ident::none(),
298 tls: Some(TlsConfig::from_paths("/ssl/cert.pem", "/ssl/key.pem")),
299 limits: Limits::default()
300 .limit("forms", 1.mebibytes())
301 .limit("json", 10.mebibytes())
302 .limit("stream", 50.kibibytes()),
303 ..Config::default()
304 });
305
306 jail.create_file("Rocket.toml", r#"
307 [global.tls]
308 certs = "cert.pem"
309 key = "key.pem"
310 "#)?;
311
312 let config = Config::from(Config::figment());
313 assert_eq!(config, Config {
314 tls: Some(TlsConfig::from_paths(
315 jail.directory().join("cert.pem"),
316 jail.directory().join("key.pem")
317 )),
318 ..Config::default()
319 });
320
321 jail.create_file("Rocket.toml", r#"
322 [global.tls]
323 certs = "cert.pem"
324 key = "key.pem"
325 prefer_server_cipher_order = true
326 ciphers = [
327 "TLS_CHACHA20_POLY1305_SHA256",
328 "TLS_AES_256_GCM_SHA384",
329 "TLS_AES_128_GCM_SHA256",
330 "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
331 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
332 "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
333 ]
334 "#)?;
335
336 let config = Config::from(Config::figment());
337 let cert_path = jail.directory().join("cert.pem");
338 let key_path = jail.directory().join("key.pem");
339 assert_eq!(config, Config {
340 tls: Some(TlsConfig::from_paths(cert_path, key_path)
341 .with_preferred_server_cipher_order(true)
342 .with_ciphers([
343 CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
344 CipherSuite::TLS_AES_256_GCM_SHA384,
345 CipherSuite::TLS_AES_128_GCM_SHA256,
346 CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
347 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
348 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
349 ])),
350 ..Config::default()
351 });
352
353 jail.create_file("Rocket.toml", r#"
354 [global]
355 shutdown.ctrlc = 0
356 ident = false
357
358 [global.tls]
359 certs = "/ssl/cert.pem"
360 key = "/ssl/key.pem"
361
362 [global.limits]
363 forms = "1mib"
364 json = "10mib"
365 stream = "50kib"
366 "#)?;
367
368 let config = Config::from(Config::figment());
369 assert_eq!(config, Config {
370 shutdown: Shutdown { ctrlc: false, ..Default::default() },
371 ident: Ident::none(),
372 tls: Some(TlsConfig::from_paths("/ssl/cert.pem", "/ssl/key.pem")),
373 limits: Limits::default()
374 .limit("forms", 1.mebibytes())
375 .limit("json", 10.mebibytes())
376 .limit("stream", 50.kibibytes()),
377 ..Config::default()
378 });
379
380 jail.create_file("Rocket.toml", r#"
381 [global.tls]
382 certs = "cert.pem"
383 key = "key.pem"
384 "#)?;
385
386 let config = Config::from(Config::figment());
387 assert_eq!(config, Config {
388 tls: Some(TlsConfig::from_paths(
389 jail.directory().join("cert.pem"),
390 jail.directory().join("key.pem")
391 )),
392 ..Config::default()
393 });
394
395 jail.create_file("Rocket.toml", r#"
396 [global.tls]
397 certs = "cert.pem"
398 key = "key.pem"
399 prefer_server_cipher_order = true
400 ciphers = [
401 "TLS_CHACHA20_POLY1305_SHA256",
402 "TLS_AES_256_GCM_SHA384",
403 "TLS_AES_128_GCM_SHA256",
404 "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
405 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
406 "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
407 ]
408 "#)?;
409
410 let config = Config::from(Config::figment());
411 let cert_path = jail.directory().join("cert.pem");
412 let key_path = jail.directory().join("key.pem");
413 assert_eq!(config, Config {
414 tls: Some(TlsConfig::from_paths(cert_path, key_path)
415 .with_preferred_server_cipher_order(true)
416 .with_ciphers([
417 CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
418 CipherSuite::TLS_AES_256_GCM_SHA384,
419 CipherSuite::TLS_AES_128_GCM_SHA256,
420 CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
421 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
422 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
423 ])),
424 ..Config::default()
425 });
426
427 Ok(())
428 });
429 }
430
431 #[test]
432 #[cfg(feature = "mtls")]
433 fn test_mtls_config() {
434 use std::path::Path;
435
436 figment::Jail::expect_with(|jail| {
437 jail.create_file("Rocket.toml", r#"
438 [default.tls]
439 certs = "/ssl/cert.pem"
440 key = "/ssl/key.pem"
441 "#)?;
442
443 let config = Config::from(Config::figment());
444 assert!(config.tls.is_some());
445 assert!(config.tls.as_ref().unwrap().mutual.is_none());
446 assert!(config.tls_enabled());
447 assert!(!config.mtls_enabled());
448
449 jail.create_file("Rocket.toml", r#"
450 [default.tls]
451 certs = "/ssl/cert.pem"
452 key = "/ssl/key.pem"
453 mutual = { ca_certs = "/ssl/ca.pem" }
454 "#)?;
455
456 let config = Config::from(Config::figment());
457 assert!(config.tls_enabled());
458 assert!(config.mtls_enabled());
459
460 let mtls = config.tls.as_ref().unwrap().mutual.as_ref().unwrap();
461 assert_eq!(mtls.ca_certs().unwrap_left(), Path::new("/ssl/ca.pem"));
462 assert!(!mtls.mandatory);
463
464 jail.create_file("Rocket.toml", r#"
465 [default.tls]
466 certs = "/ssl/cert.pem"
467 key = "/ssl/key.pem"
468
469 [default.tls.mutual]
470 ca_certs = "/ssl/ca.pem"
471 mandatory = true
472 "#)?;
473
474 let config = Config::from(Config::figment());
475 let mtls = config.tls.as_ref().unwrap().mutual.as_ref().unwrap();
476 assert_eq!(mtls.ca_certs().unwrap_left(), Path::new("/ssl/ca.pem"));
477 assert!(mtls.mandatory);
478
479 jail.create_file("Rocket.toml", r#"
480 [default.tls]
481 certs = "/ssl/cert.pem"
482 key = "/ssl/key.pem"
483 mutual = { ca_certs = "relative/ca.pem" }
484 "#)?;
485
486 let config = Config::from(Config::figment());
487 let mtls = config.tls.as_ref().unwrap().mutual().unwrap();
488 assert_eq!(mtls.ca_certs().unwrap_left(),
489 jail.directory().join("relative/ca.pem"));
490
491 Ok(())
492 });
493 }
494
495 #[test]
496 fn test_profiles_merge() {
497 figment::Jail::expect_with(|jail| {
498 jail.create_file("Rocket.toml", r#"
499 [default.limits]
500 stream = "50kb"
501
502 [global]
503 limits = { forms = "2kb" }
504
505 [debug.limits]
506 file = "100kb"
507 "#)?;
508
509 jail.set_env("ROCKET_PROFILE", "unknown");
510 let config = Config::from(Config::figment());
511 assert_eq!(config, Config {
512 profile: Profile::const_new("unknown"),
513 limits: Limits::default()
514 .limit("stream", 50.kilobytes())
515 .limit("forms", 2.kilobytes()),
516 ..Config::default()
517 });
518
519 jail.set_env("ROCKET_PROFILE", "debug");
520 let config = Config::from(Config::figment());
521 assert_eq!(config, Config {
522 profile: Profile::const_new("debug"),
523 limits: Limits::default()
524 .limit("stream", 50.kilobytes())
525 .limit("forms", 2.kilobytes())
526 .limit("file", 100.kilobytes()),
527 ..Config::default()
528 });
529
530 Ok(())
531 });
532 }
533
534 #[test]
535 #[cfg(feature = "tls")]
536 fn test_env_vars_merge() {
537 use crate::config::{TlsConfig, Ident};
538
539 figment::Jail::expect_with(|jail| {
540 jail.set_env("ROCKET_PORT", 9999);
541 let config = Config::from(Config::figment());
542 assert_eq!(config, Config {
543 port: 9999,
544 ..Config::default()
545 });
546
547 jail.set_env("ROCKET_TLS", r#"{certs="certs.pem"}"#);
548 let first_figment = Config::figment();
549 jail.set_env("ROCKET_TLS", r#"{key="key.pem"}"#);
550 let prev_figment = Config::figment().join(&first_figment);
551 let config = Config::from(&prev_figment);
552 assert_eq!(config, Config {
553 port: 9999,
554 tls: Some(TlsConfig::from_paths("certs.pem", "key.pem")),
555 ..Config::default()
556 });
557
558 jail.set_env("ROCKET_TLS", r#"{certs="new.pem"}"#);
559 let config = Config::from(Config::figment().join(&prev_figment));
560 assert_eq!(config, Config {
561 port: 9999,
562 tls: Some(TlsConfig::from_paths("new.pem", "key.pem")),
563 ..Config::default()
564 });
565
566 jail.set_env("ROCKET_LIMITS", r#"{stream=100kiB}"#);
567 let config = Config::from(Config::figment().join(&prev_figment));
568 assert_eq!(config, Config {
569 port: 9999,
570 tls: Some(TlsConfig::from_paths("new.pem", "key.pem")),
571 limits: Limits::default().limit("stream", 100.kibibytes()),
572 ..Config::default()
573 });
574
575 jail.set_env("ROCKET_IDENT", false);
576 let config = Config::from(Config::figment().join(&prev_figment));
577 assert_eq!(config, Config {
578 port: 9999,
579 tls: Some(TlsConfig::from_paths("new.pem", "key.pem")),
580 limits: Limits::default().limit("stream", 100.kibibytes()),
581 ident: Ident::none(),
582 ..Config::default()
583 });
584
585 Ok(())
586 });
587 }
588
589 #[test]
590 fn test_precedence() {
591 figment::Jail::expect_with(|jail| {
592 jail.create_file("Rocket.toml", r#"
593 [global.limits]
594 forms = "1mib"
595 stream = "50kb"
596 file = "100kb"
597 "#)?;
598
599 let config = Config::from(Config::figment());
600 assert_eq!(config, Config {
601 limits: Limits::default()
602 .limit("forms", 1.mebibytes())
603 .limit("stream", 50.kilobytes())
604 .limit("file", 100.kilobytes()),
605 ..Config::default()
606 });
607
608 jail.set_env("ROCKET_LIMITS", r#"{stream=3MiB,capture=2MiB}"#);
609 let config = Config::from(Config::figment());
610 assert_eq!(config, Config {
611 limits: Limits::default()
612 .limit("file", 100.kilobytes())
613 .limit("forms", 1.mebibytes())
614 .limit("stream", 3.mebibytes())
615 .limit("capture", 2.mebibytes()),
616 ..Config::default()
617 });
618
619 jail.set_env("ROCKET_PROFILE", "foo");
620 let val: Result<String, _> = Config::figment().extract_inner("profile");
621 assert!(val.is_err());
622
623 Ok(())
624 });
625 }
626
627 #[test]
628 #[cfg(feature = "secrets")]
629 #[should_panic]
630 fn test_err_on_non_debug_and_no_secret_key() {
631 figment::Jail::expect_with(|jail| {
632 jail.set_env("ROCKET_PROFILE", "release");
633 let rocket = crate::custom(Config::figment());
634 let _result = crate::local::blocking::Client::untracked(rocket);
635 Ok(())
636 });
637 }
638
639 #[test]
640 #[cfg(feature = "secrets")]
641 #[should_panic]
642 fn test_err_on_non_debug2_and_no_secret_key() {
643 figment::Jail::expect_with(|jail| {
644 jail.set_env("ROCKET_PROFILE", "boop");
645 let rocket = crate::custom(Config::figment());
646 let _result = crate::local::blocking::Client::tracked(rocket);
647 Ok(())
648 });
649 }
650
651 #[test]
652 fn test_no_err_on_debug_and_no_secret_key() {
653 figment::Jail::expect_with(|jail| {
654 jail.set_env("ROCKET_PROFILE", "debug");
655 let figment = Config::figment();
656 assert!(crate::local::blocking::Client::untracked(crate::custom(&figment)).is_ok());
657 crate::async_main(async {
658 let rocket = crate::custom(&figment);
659 assert!(crate::local::asynchronous::Client::tracked(rocket).await.is_ok());
660 });
661
662 Ok(())
663 });
664 }
665
666 #[test]
667 fn test_no_err_on_release_and_custom_secret_key() {
668 figment::Jail::expect_with(|jail| {
669 jail.set_env("ROCKET_PROFILE", "release");
670 let key = "Bx4Gb+aSIfuoEyMHD4DvNs92+wmzfQK98qc6MiwyPY4=";
671 let figment = Config::figment().merge(("secret_key", key));
672
673 assert!(crate::local::blocking::Client::tracked(crate::custom(&figment)).is_ok());
674 crate::async_main(async {
675 let rocket = crate::custom(&figment);
676 assert!(crate::local::asynchronous::Client::untracked(rocket).await.is_ok());
677 });
678
679 Ok(())
680 });
681 }
682}