Skip to main content

rainier_framework/
bootstrap.rs

1//! Bootstrapping — [`Rainier`], the builder that assembles an application.
2//!
3//! Wiring a framework by hand means binding a dozen services in the right
4//! order before anything works. This builder does the parts every application
5//! wants the same way — environment, config, events, views, logging, the
6//! middleware registry, the facades — and leaves the parts that differ (the
7//! database, the queue driver, the routes) to explicit calls.
8
9use std::path::PathBuf;
10use std::sync::Arc;
11
12use rainier_cache::CacheManager;
13use rainier_config::{Config, Env};
14use rainier_container::{Application, ServiceProvider};
15use rainier_crypt::{Encryption, Key, KeyRing};
16use rainier_database::Database;
17use rainier_events::Dispatcher;
18use rainier_filesystem::Storage;
19use rainier_mail::Mailer;
20use rainier_middleware::MiddlewareRegistry;
21use rainier_queue::QueueManager;
22use rainier_routing::{Router, UrlGenerator};
23use rainier_server::Kernel;
24use rainier_session::{MemorySessionStore, SessionManager};
25use rainier_support::{Error, Result};
26use rainier_view::{TemplateEngine, ViewEngine};
27
28use crate::facades::Views;
29use crate::keys;
30
31/// Assembles an [`Application`].
32///
33/// ```no_run
34/// use rainier_framework::Rainier;
35///
36/// # #[tokio::main] async fn main() -> rainier_support::Result<()> {
37/// let app = Rainier::new(".")
38///     .with_routes(|router| {
39///         router.get("/", || async { "Hello from Rainier" });
40///     })
41///     .boot()
42///     .await?;
43/// # let _ = app;
44/// # Ok(()) }
45/// ```
46pub struct Rainier {
47    base_path: PathBuf,
48    env: Env,
49    config: Config,
50    /// A configuration error from [`Rainier::new`], surfaced by
51    /// [`boot`](Rainier::boot).
52    ///
53    /// `new` cannot return a `Result` without putting a `?` in the middle of
54    /// every builder chain, and an unreadable `CACHE_DRIVER` must not be
55    /// swallowed. So it waits here.
56    deferred: Option<Error>,
57    events: Dispatcher,
58    middleware: MiddlewareRegistry,
59    router: Router,
60    views: Option<Arc<dyn ViewEngine>>,
61    database: Option<Database>,
62    queue: Option<QueueManager>,
63    mailer: Option<Mailer>,
64    sessions: Option<SessionManager>,
65    crypt: Option<Encryption>,
66    cache: Option<CacheManager>,
67    notifier: Option<rainier_notify::Notifier>,
68    broadcasting: Option<rainier_broadcast::Broadcasting>,
69    websockets: Option<rainier_websocket::WebSocketRoutes>,
70    /// Values bound as-is, in declaration order.
71    #[allow(clippy::type_complexity, reason = "one boxed closure per bound value")]
72    instances: Vec<Box<dyn Fn(&Application)>>,
73    schedule: Option<rainier_scheduler::Schedule>,
74    storage: Option<Storage>,
75    providers: Vec<Arc<dyn ServiceProvider>>,
76    install_facades: bool,
77    install_tracing: bool,
78}
79
80impl Rainier {
81    /// Start from `base_path`, reading `.env` from it if there is one.
82    ///
83    /// Infallible so the builder chain reads as one expression. A driver name
84    /// the environment got wrong is held in `deferred` and returned from
85    /// [`boot`](Self::boot) — the error still stops the application, it just
86    /// does not force a `?` into the middle of the chain.
87    pub fn new(base_path: impl Into<PathBuf>) -> Self {
88        let base_path = base_path.into();
89        let env = Env::load_or_default(base_path.join(".env"));
90        let (config, deferred) = match default_config(&env, &base_path) {
91            Ok(config) => (config, None),
92            // Carry on with an empty tree so `with_*` calls still apply; `boot`
93            // returns the error before any of it is used.
94            Err(e) => (Config::new(), Some(e)),
95        };
96
97        Self {
98            base_path,
99            env,
100            config,
101            deferred,
102            events: Dispatcher::new(),
103            middleware: default_middleware(),
104            router: Router::new(),
105            views: None,
106            database: None,
107            queue: None,
108            mailer: None,
109            sessions: None,
110            crypt: None,
111            cache: None,
112            notifier: None,
113            broadcasting: None,
114            websockets: None,
115            instances: Vec::new(),
116            schedule: None,
117            storage: None,
118            providers: Vec::new(),
119            install_facades: true,
120            install_tracing: true,
121        }
122    }
123
124    /// The environment, for reading deployment values while building.
125    pub fn env(&self) -> &Env {
126        &self.env
127    }
128
129    /// The configuration, for adding to it while building.
130    pub fn config(&self) -> &Config {
131        &self.config
132    }
133
134    /// Adjust the configuration.
135    pub fn configure(self, adjust: impl FnOnce(&Config)) -> Self {
136        adjust(&self.config);
137        self
138    }
139
140    /// Register event listeners.
141    pub fn with_events(self, register: impl FnOnce(&Dispatcher)) -> Self {
142        register(&self.events);
143        self
144    }
145
146    /// Register middleware aliases and groups.
147    pub fn with_middleware(self, register: impl FnOnce(&MiddlewareRegistry)) -> Self {
148        register(&self.middleware);
149        self
150    }
151
152    /// Declare routes.
153    pub fn with_routes(mut self, declare: impl FnOnce(&mut Router)) -> Self {
154        declare(&mut self.router);
155        self
156    }
157
158    /// Use a specific view engine. Defaults to a [`TemplateEngine`] over
159    /// `<base>/resources/views`.
160    pub fn with_views(mut self, engine: Arc<dyn ViewEngine>) -> Self {
161        self.views = Some(engine);
162        self
163    }
164
165    /// Use this database.
166    pub fn with_database(mut self, database: Database) -> Self {
167        self.database = Some(database);
168        self
169    }
170
171    /// Use this queue.
172    pub fn with_queue(mut self, queue: QueueManager) -> Self {
173        self.queue = Some(queue);
174        self
175    }
176
177    /// Use this mailer.
178    pub fn with_mailer(mut self, mailer: Mailer) -> Self {
179        self.mailer = Some(mailer);
180        self
181    }
182
183    /// Use this session store, and register the `session` middleware alias.
184    ///
185    /// Without this, sessions default to an in-process store — right for
186    /// development and wrong the moment there are two instances. See
187    /// [`SessionManager`].
188    pub fn with_sessions(mut self, sessions: SessionManager) -> Self {
189        self.sessions = Some(sessions);
190        self
191    }
192
193    /// Use this cache.
194    ///
195    /// Defaults to an in-process one, which is right for development and wrong
196    /// the moment a second instance exists — see [`CacheManager`].
197    pub fn with_cache(mut self, cache: CacheManager) -> Self {
198        self.cache = Some(cache);
199        self
200    }
201
202    /// Use these notification channels.
203    ///
204    /// Without it, notifications go to the log — which delivers nothing, and is
205    /// the right thing to have by accident: a misconfigured deployment logs
206    /// them rather than emailing real people from a copy of production data.
207    ///
208    /// ```ignore
209    /// Rainier::new(".").with_notifier(
210    ///     Notifier::new()
211    ///         .with(MailChannel::new(mailer))
212    ///         .with(DatabaseChannel::new(database)),
213    /// )
214    /// ```
215    pub fn with_notifier(mut self, notifier: rainier_notify::Notifier) -> Self {
216        self.notifier = Some(notifier);
217        self
218    }
219
220    /// Use this broadcast driver.
221    ///
222    /// Without it, broadcasts go to the log — they reach no browser, which is
223    /// the right accident to have: an application that has not configured a
224    /// relay logs what it would have published instead of failing requests.
225    ///
226    /// The **channel table** is not set here, because it is generic over your
227    /// user model. Bind a `ChannelRegistry<User>` in a provider; until you do,
228    /// every private channel is denied.
229    ///
230    /// ```ignore
231    /// Rainier::new(".").with_broadcasting(Broadcasting::new(Arc::new(
232    ///     RedisBroadcaster::connect(&connector).await?.with_pusher_auth(auth),
233    /// )))
234    /// ```
235    pub fn with_broadcasting(mut self, broadcasting: rainier_broadcast::Broadcasting) -> Self {
236        self.broadcasting = Some(broadcasting);
237        self
238    }
239
240    /// Serve these WebSocket routes.
241    ///
242    /// On the **same port** as HTTP: a socket connection begins as a `GET`
243    /// asking to upgrade, so there is no second listener to start and nothing
244    /// to run alongside anything.
245    ///
246    /// ```ignore
247    /// Rainier::new(".").with_websockets(
248    ///     WebSocketRoutes::new().add("/ws/rooms/{room}", Chat::new(rooms)),
249    /// )
250    /// ```
251    pub fn with_websockets(mut self, routes: rainier_websocket::WebSocketRoutes) -> Self {
252        self.websockets = Some(routes);
253        self
254    }
255
256    /// Bind a value you already have.
257    ///
258    /// The gap the typed `with_*` methods leave: an application often holds
259    /// something the framework has never heard of — a
260    /// [`Rooms`](rainier_websocket::Rooms) registry, a feature-flag client, an
261    /// HTTP client with a pool — and it needs to be resolvable without a
262    /// provider whose whole body is one `instance` call.
263    ///
264    /// A provider is still the right answer when the value has to be *built*
265    /// from other bound services; this is for one that already exists.
266    pub fn with_instance<T: Send + Sync + 'static>(mut self, value: T) -> Self {
267        let value = Arc::new(value);
268        self.instances.push(Box::new(move |app| app.instance_arc(Arc::clone(&value))));
269        self
270    }
271
272    /// Bind something already shared, keeping your handle to it.
273    pub fn with_instance_arc<T: Send + Sync + 'static>(mut self, value: Arc<T>) -> Self {
274        self.instances.push(Box::new(move |app| app.instance_arc(Arc::clone(&value))));
275        self
276    }
277
278    /// Declare the scheduled tasks.
279    ///
280    /// ```ignore
281    /// Rainier::new(".").with_schedule(routes::console::schedule)
282    /// ```
283    ///
284    /// Bound in the container, so `schedule:run` and `schedule:list` find it.
285    pub fn with_schedule(mut self, declare: impl FnOnce(&mut rainier_scheduler::Schedule)) -> Self {
286        let mut schedule = self.schedule.take().unwrap_or_default();
287        declare(&mut schedule);
288        self.schedule = Some(schedule);
289        self
290    }
291
292    /// Use this file storage.
293    ///
294    /// Defaults to `<base>/storage/app`, so `Storage::instance()` works on a
295    /// fresh clone without configuration.
296    pub fn with_storage(mut self, storage: Storage) -> Self {
297        self.storage = Some(storage);
298        self
299    }
300
301    /// Use these keys for encryption and signing.
302    ///
303    /// Defaults to `APP_KEY` from the environment. With neither, a random key
304    /// is generated per boot — which works, and silently invalidates every
305    /// encrypted value the last boot produced, so it warns.
306    pub fn with_encryption(mut self, crypt: Encryption) -> Self {
307        self.crypt = Some(crypt);
308        self
309    }
310
311    /// Register a service provider.
312    pub fn with_provider(mut self, provider: impl ServiceProvider) -> Self {
313        self.providers.push(Arc::new(provider));
314        self
315    }
316
317    /// Do not install the facades' global application.
318    ///
319    /// Two applications in one process would otherwise fight over it — which
320    /// is exactly what a test suite building an app per test does.
321    pub fn without_facades(mut self) -> Self {
322        self.install_facades = false;
323        self
324    }
325
326    /// Do not install a tracing subscriber.
327    pub fn without_tracing(mut self) -> Self {
328        self.install_tracing = false;
329        self
330    }
331
332    /// Build and boot the application.
333    ///
334    /// Compiling the router here is what makes an unknown middleware alias or
335    /// a duplicate route name a **boot** failure rather than a surprise on the
336    /// first request that hits it.
337    pub async fn boot(self) -> Result<Arc<Application>> {
338        // Before anything is installed, and before tracing: a `.env` the
339        // process cannot read is not a state to start serving in.
340        if let Some(error) = self.deferred {
341            return Err(error);
342        }
343
344        if self.install_tracing {
345            install_tracing(&self.env, &self.config);
346        }
347
348        let environment = self.config.get(keys::APP_ENV).unwrap_or_default().to_string();
349        let app = Arc::new(Application::new(self.base_path.clone()).with_environment(&environment));
350
351        // Installed *before* anything else runs, not at the end. A service
352        // provider's `boot`, and a middleware alias factory resolved while the
353        // router compiles, both legitimately reach for a facade — and both
354        // happen during this call. Installing last would make them panic.
355        if self.install_facades {
356            rainier_container::set_facade_application(Arc::clone(&app));
357        }
358
359        let views = self.views.unwrap_or_else(|| {
360            Arc::new(TemplateEngine::new(self.base_path.join("resources").join("views")))
361        });
362
363        let crypt = self.crypt.unwrap_or_else(|| {
364            let keys = app_keys(&self.env);
365
366            match self.config.setting(keys::APP_CIPHER).unwrap_or_default() {
367                rainier_crypt::CryptScheme::Native => Encryption::from_keys(keys),
368                // A database PHP already filled. Reads and writes the PHP
369                // envelope byte for byte; see `rainier_crypt::php` for why
370                // this is a migration position rather than a destination.
371                rainier_crypt::CryptScheme::Php => Encryption::new(
372                    Arc::new(rainier_crypt::PhpEncrypter::new(keys.clone())),
373                    Arc::new(rainier_crypt::HmacSigner::new(keys)),
374                ),
375            }
376        });
377
378        // Signed URLs, over the application's own ring where it has one. An
379        // application that supplied a hand-built `Encryption` did not hand
380        // over a ring, so this falls back to `APP_KEY` — the same ring in
381        // every normal setup, and the honest answer when it is not.
382        //
383        // Bound **here** rather than beside the URL generator: a route can
384        // carry `ValidateSignature`, and a route's middleware is built when
385        // the router compiles, which is long before there are named routes to
386        // generate links to.
387        let url_signer = rainier_crypt::UrlSigner::new(
388            crypt.keys().cloned().unwrap_or_else(|| app_keys(&self.env)),
389        );
390        let sessions = self
391            .sessions
392            .unwrap_or_else(|| SessionManager::new(Arc::new(MemorySessionStore::default())));
393
394        app.instance(self.config);
395        app.instance(self.env);
396        app.instance(self.events);
397        app.instance(Views::new(views));
398        app.instance(crypt);
399        app.instance(url_signer);
400        app.instance(sessions);
401        // Nobody handed one over, so this is the in-process default. If the
402        // configuration asked for Redis, that is a gap between what the
403        // deployment believes and what it has — and the symptom otherwise is a
404        // rate limiter that counts to `N ×` its limit with nothing logged.
405        let cache = match self.cache {
406            Some(cache) => cache,
407            None => {
408                if let Ok(driver) = app.resolve::<Config>()?.setting(keys::CACHE_DRIVER) {
409                    if driver.is_shared() {
410                        tracing::warn!(
411                            configured = %driver,
412                            "CACHE_DRIVER asks for a shared cache but none was built, so \
413                             this process is using the in-process one — locks, rate limits \
414                             and anything else cached for correctness are per-instance. \
415                             Build the store and pass it to `Rainier::with_cache`."
416                        );
417                    }
418                }
419                CacheManager::memory()
420            }
421        };
422
423        // The locks behind `without_overlapping` and `on_one_server` — and
424        // anything else that needs one. Bound from the same cache the
425        // application uses, so "shared" means the same thing for both.
426        app.instance(rainier_cache::LockManager::new(Arc::clone(cache.store())));
427
428        // Rate-limit counters, in the same place as the locks and for the same
429        // reason: a deployment decides where its shared state lives once.
430        app.instance(crate::limits::RateLimits::over_cache(Arc::clone(cache.store())));
431
432        // And the codes people type — a verification code, a second factor, an
433        // email change. Cache-backed, so they expire on their own and there is
434        // no purge job to write.
435        app.instance(rainier_auth::Challenges::new(Arc::clone(cache.store())));
436        app.instance(cache);
437
438        // An empty schedule, so `schedule:list` and `schedule:run` work on a
439        // fresh clone. An application replaces it in a provider.
440        app.instance(self.schedule.unwrap_or_default());
441
442        // Whatever the application handed over, before the providers run so
443        // one of them can resolve it.
444        for bind in self.instances {
445            bind(&app);
446        }
447
448        // No socket routes unless the application declares some, and an
449        // upgrade then falls through to the router like any other GET.
450        app.instance(self.websockets.unwrap_or_default());
451
452        // Broadcasts default to the log, for the same reason notifications do
453        // — except the risk here is the reverse one: publishing a private
454        // channel to a relay the application never meant to reach.
455        app.instance(self.broadcasting.unwrap_or_else(rainier_broadcast::Broadcasting::log));
456
457        // Notifications default to the log channel. Not to mail: a default that
458        // can reach a real person is a default that reaches one from staging.
459        app.instance(
460            self.notifier.unwrap_or_else(|| {
461                rainier_notify::Notifier::new().with(rainier_notify::LogChannel)
462            }),
463        );
464        app.instance(
465            self.storage
466                .unwrap_or_else(|| Storage::local(self.base_path.join("storage").join("app"))),
467        );
468
469        if let Some(database) = self.database {
470            app.instance(database);
471        }
472        if let Some(queue) = self.queue {
473            // Given the same locks as everything else, so `unique_id` is
474            // enforced against the cache the application actually shares.
475            // Without this a job declaring one is dispatched anyway, with a
476            // warning — degraded rather than silently broken.
477            app.instance(queue.with_locks(rainier_cache::LockManager::new(Arc::clone(
478                app.resolve::<CacheManager>()?.store(),
479            ))));
480        }
481        if let Some(mailer) = self.mailer {
482            app.instance(mailer);
483        }
484
485        // Providers register before the router compiles, so a middleware that
486        // resolves a service gets one a provider bound.
487        for provider in self.providers {
488            app.register_arc(provider)?;
489        }
490
491        // Compiled once and shared: the kernel serves it, and the container
492        // holds the same table so `route:list` can describe what is actually
493        // being served rather than a second compilation of it.
494        //
495        // This is also where every `resolved`/`deferred` middleware stage is
496        // built, which is why it happens after the providers and before the
497        // server starts.
498        let compiled = Arc::new(self.router.compile(&app)?);
499        let urls = Arc::new(
500            UrlGenerator::from_routes(compiled.named_routes())
501                .with_base(app.resolve::<Config>()?.get_or(keys::APP_URL, String::new())),
502        );
503
504        // Signed links, over the same key ring everything else uses — so
505        // rotating a key retires the links it signed, which is correct and
506        // worth knowing before rotating one.
507        app.instance(crate::signed::SignedUrls::new(
508            Arc::clone(&urls),
509            app.resolve::<rainier_crypt::UrlSigner>()?,
510        ));
511
512        let mut global = self.middleware.global_stack(&app)?;
513        let settings = app.resolve::<Config>()?;
514
515        // Outside everything the application registered, so it compresses
516        // whatever any of it produced.
517        if settings.get_or(keys::SERVER_COMPRESSION, false) {
518            global.insert(0, Arc::new(rainier_middleware::Compress::new()));
519        }
520
521        // Outside even that: a timeout that sits inside the throttle cannot
522        // cancel time spent in the throttle.
523        let timeout_secs = settings.get_or(keys::SERVER_REQUEST_TIMEOUT_SECS, 0u64);
524        if timeout_secs > 0 {
525            global.insert(0, Arc::new(rainier_middleware::Timeout::seconds(timeout_secs)));
526        }
527
528        let kernel = Kernel::from_shared(Arc::clone(&compiled), global)
529            .with_debug(app.resolve::<Config>()?.get_or(keys::APP_DEBUG, false));
530
531        app.instance_arc(Arc::clone(&compiled));
532        app.instance_arc(urls);
533        app.instance(self.middleware);
534        app.instance(kernel);
535
536        app.boot().await?;
537
538        // Last, because a provider's `boot` can still add scheduled tasks. A
539        // task declaring `on_one_server` over an in-process lock is a
540        // guarantee that is not one, and every process gets told — but only
541        // `schedule:run` refuses, because that is the one whose guarantees are
542        // at stake. A web container refusing to serve HTTP over a scheduling
543        // concern would be a larger outage than the one being prevented.
544        crate::scheduling::warn_if_locks_are_not_shared(&app);
545
546        // The same question one layer over: a route that rate-limits against a
547        // per-process counter permits its limit once per replica, and looks
548        // entirely correct while doing it.
549        crate::limits::warn_if_rate_limits_are_not_shared(&app, &compiled);
550
551        Ok(app)
552    }
553}
554
555/// The configuration every application starts with, read from the environment.
556///
557/// Fails only on a driver name outside its [closed
558/// set](rainier_support::Setting) — `QUEUE_DRIVER=databse` is a deployment
559/// mistake, and running on the default instead of the driver somebody asked for
560/// is not a recovery. Everything else here has a total parse: a `SERVER_PORT`
561/// that is not a number falls back, because the alternative is refusing to boot
562/// over a trailing space.
563fn default_config(env: &Env, base_path: &std::path::Path) -> Result<Config> {
564    let config = Config::new();
565
566    config.set(keys::APP_NAME, env.string("APP_NAME", "Rainier"))?;
567    config.set(keys::APP_ENV, env.setting("APP_ENV")?)?;
568    config.set(keys::APP_DEBUG, env.bool("APP_DEBUG", false))?;
569    config.set(keys::APP_URL, env.string("APP_URL", "http://localhost:8000"))?;
570    // A closed set, like the drivers: `LOG_FORMAT=jsn` on a production box
571    // would otherwise log prose into something that wanted objects, and say
572    // nothing.
573    config.set(keys::LOG_FORMAT, env.setting("LOG_FORMAT")?)?;
574
575    // A closed set for the same reason the drivers are: writing the wrong
576    // envelope is not a preference that degrades, it is a column nothing can
577    // read.
578    config.set(keys::APP_CIPHER, env.setting("APP_CIPHER")?)?;
579    config.set(keys::APP_BASE_PATH, base_path.to_string_lossy().into_owned())?;
580
581    config.set(keys::SERVER_HOST, env.string("SERVER_HOST", "127.0.0.1"))?;
582    config.set(keys::SERVER_PORT, env.int("SERVER_PORT", 8000) as u16)?;
583    config.set(keys::SERVER_MAX_BODY_BYTES, env.int("SERVER_MAX_BODY", 2 * 1024 * 1024) as u64)?;
584    config.set(
585        keys::SERVER_REQUEST_TIMEOUT_SECS,
586        env.int("SERVER_REQUEST_TIMEOUT", 0).max(0) as u64,
587    )?;
588    config.set(keys::SERVER_COMPRESSION, env.bool("SERVER_COMPRESSION", false))?;
589
590    config.set(keys::DATABASE_URL, env.string("DATABASE_URL", "sqlite::memory:"))?;
591
592    config.set(keys::CACHE_DRIVER, env.setting("CACHE_DRIVER")?)?;
593    config.set(keys::CACHE_REDIS_URL, env.string("REDIS_URL", "redis://127.0.0.1:6379/"))?;
594    config.set(keys::CACHE_MEMCACHED_URL, env.string("MEMCACHED_URL", "127.0.0.1:11211"))?;
595
596    config.set(keys::SESSION_DRIVER, env.setting("SESSION_DRIVER")?)?;
597    config.set(keys::SESSION_LIFETIME, env.int("SESSION_LIFETIME", 7200))?;
598    config.set(keys::SESSION_COOKIE, env.string("SESSION_COOKIE", "rainier_session"))?;
599    config.set(keys::SESSION_SECURE, env.bool("SESSION_SECURE", false))?;
600
601    config.set(keys::QUEUE_DRIVER, env.setting("QUEUE_DRIVER")?)?;
602    config.set(keys::QUEUE_DEFAULT, env.string("QUEUE_DEFAULT", "default"))?;
603
604    config.set(keys::KAFKA_BROKERS, env.string("KAFKA_BROKERS", ""))?;
605    config.set(keys::KAFKA_GROUP, env.string("KAFKA_GROUP", "rainier"))?;
606    config.set(keys::KAFKA_TOPIC_PREFIX, env.string("KAFKA_TOPIC_PREFIX", ""))?;
607    config.set(keys::KAFKA_BROADCAST_TOPIC, env.string("KAFKA_BROADCAST_TOPIC", "broadcasts"))?;
608    config.set(keys::KAFKA_TLS, env.bool("KAFKA_TLS", false))?;
609    config.set(keys::KAFKA_USERNAME, env.string("KAFKA_USERNAME", ""))?;
610    config.set(keys::KAFKA_PASSWORD, env.string("KAFKA_PASSWORD", ""))?;
611    config.set(keys::KAFKA_SASL_MECHANISM, env.string("KAFKA_SASL_MECHANISM", "plain"))?;
612
613    config.set(keys::MAIL_DRIVER, env.setting("MAIL_DRIVER")?)?;
614    config.set(keys::MAIL_FROM_ADDRESS, env.string("MAIL_FROM", "hello@example.com"))?;
615    config.set(keys::MAIL_FROM_NAME, env.string("MAIL_FROM_NAME", "Rainier"))?;
616
617    Ok(config)
618}
619
620/// The keys encryption and signing use.
621///
622/// `APP_KEY` is the current key; `APP_PREVIOUS_KEYS` is a comma-separated list
623/// of retired ones, still needed to read what they wrote. See
624/// [`rainier_crypt`].
625fn app_keys(env: &Env) -> KeyRing {
626    let previous: Vec<String> =
627        env.get("APP_PREVIOUS_KEYS").unwrap_or_default().split(',').map(str::to_string).collect();
628
629    match env.get("APP_KEY").filter(|key| !key.trim().is_empty()) {
630        Some(key) => match KeyRing::from_base64(&key, &previous) {
631            Ok(ring) => ring,
632            Err(e) => {
633                // Falling back to a random key would "work" and quietly make
634                // every existing encrypted value unreadable, so say plainly
635                // what is wrong instead of hiding it in a decrypt failure
636                // three screens later.
637                tracing::error!(
638                    error = %e.message(),
639                    "APP_KEY is not usable; generating a temporary key. Encrypted values \
640                     written before this boot cannot be read, and values written now cannot \
641                     be read after a restart."
642                );
643                KeyRing::new(Key::generate())
644            }
645        },
646        None => {
647            tracing::warn!(
648                "APP_KEY is not set; generating a temporary key. Set one with \
649                 `APP_KEY={}` or nothing encrypted will survive a restart.",
650                Key::generate().to_base64()
651            );
652            KeyRing::new(Key::generate())
653        }
654    }
655}
656
657/// The middleware every application starts with.
658///
659/// Only the input normalisers are **global**, because they are the ones whose
660/// absence produces confusing bugs rather than missing features. Everything
661/// else is registered as an **alias** and opted into per route, since applying
662/// CORS, throttling, sessions or authentication everywhere by default is
663/// nearly always wrong.
664fn default_middleware() -> MiddlewareRegistry {
665    let registry = MiddlewareRegistry::new();
666
667    // Input normalisation only. Everything else — CORS, throttling, sessions,
668    // authentication — is opted into per route or per group, because applying
669    // any of it everywhere by default is how an API ends up allocating session
670    // rows nobody reads.
671    //
672    // The groups Rainier ships are functions in [`crate::groups`], not entries
673    // here: there is nothing to look them up by.
674    registry.global(crate::groups::normalise_input());
675
676    registry
677}
678
679fn install_tracing(env: &Env, config: &Config) {
680    let environment = config.get(keys::APP_ENV).unwrap_or_default().to_string();
681
682    // A second subscriber would panic, and an application that installs its
683    // own should keep it — so a failure here is expected and ignored.
684    crate::observability::TelemetrySettings::from_config(config)
685        .install_logging(&environment, &env.string("RUST_LOG", "info"));
686}
687
688#[cfg(test)]
689mod tests {
690    use super::*;
691    use crate::groups;
692    use rainier_http::{Method, Request, StatusCode};
693    use rainier_session::SessionRequestExt as _;
694    use rainier_session::StartSession;
695
696    async fn app() -> Arc<Application> {
697        Rainier::new(".")
698            .without_facades()
699            .without_tracing()
700            .with_routes(|router| {
701                router.get("/", || async { "home" }).name("home");
702                router.get("/posts/{post}", || async { "post" }).name("posts.show");
703            })
704            .boot()
705            .await
706            .expect("boots")
707    }
708
709    #[tokio::test]
710    async fn booting_binds_the_core_services() {
711        let app = app().await;
712
713        assert!(app.resolve::<Config>().is_ok());
714        assert!(app.resolve::<Dispatcher>().is_ok());
715        assert!(app.resolve::<Views>().is_ok());
716        assert!(app.resolve::<Kernel>().is_ok());
717        assert!(app.resolve::<UrlGenerator>().is_ok());
718        assert!(app.resolve::<MiddlewareRegistry>().is_ok());
719        assert!(app.is_booted());
720    }
721
722    #[tokio::test]
723    async fn the_kernel_serves_the_declared_routes() {
724        let app = app().await;
725        let kernel = app.resolve::<Kernel>().unwrap();
726
727        let response =
728            kernel.handle_request(Request::builder().method(Method::GET).uri("/").build()).await;
729        assert_eq!(response.status(), StatusCode::OK);
730    }
731
732    #[tokio::test]
733    async fn named_routes_reach_the_url_generator() {
734        let app = app().await;
735        let urls = app.resolve::<UrlGenerator>().unwrap();
736
737        assert_eq!(urls.route("posts.show", &[("post", "7")]).unwrap(), "/posts/7");
738    }
739
740    #[tokio::test]
741    async fn the_default_config_comes_from_the_environment() {
742        let app = app().await;
743        let config = app.resolve::<Config>().unwrap();
744
745        assert_eq!(config.get(keys::APP_NAME).as_deref(), Some("Rainier"));
746        assert_eq!(config.get(keys::SERVER_PORT), Some(8000));
747        assert_eq!(config.get(keys::APP_DEBUG), Some(false));
748    }
749
750    #[test]
751    fn the_default_drivers_are_the_ones_that_need_no_infrastructure() {
752        // What a fresh clone gets, and the reason `cargo run -- serve` works
753        // with an empty `.env`.
754        let config = default_config(&Env::new(), std::path::Path::new(".")).unwrap();
755
756        assert_eq!(config.setting(keys::APP_ENV).unwrap(), rainier_config::AppEnv::Production);
757        assert_eq!(config.setting(keys::CACHE_DRIVER).unwrap(), rainier_cache::CacheDriver::Memory);
758        assert_eq!(
759            config.setting(keys::SESSION_DRIVER).unwrap(),
760            rainier_session::SessionDriver::Memory
761        );
762        assert_eq!(config.setting(keys::QUEUE_DRIVER).unwrap(), rainier_queue::QueueDriver::Sync);
763        assert_eq!(config.setting(keys::MAIL_DRIVER).unwrap(), rainier_mail::MailDriver::Log);
764    }
765
766    #[test]
767    fn a_driver_written_as_its_wire_spelling_reads_back_as_the_enum() {
768        let env = Env::parse("CACHE_DRIVER=redis-cluster\nQUEUE_DRIVER=database\nAPP_ENV=local");
769        let config = default_config(&env, std::path::Path::new(".")).unwrap();
770
771        assert_eq!(
772            config.setting(keys::CACHE_DRIVER).unwrap(),
773            rainier_cache::CacheDriver::RedisCluster
774        );
775        assert_eq!(
776            config.setting(keys::QUEUE_DRIVER).unwrap(),
777            rainier_queue::QueueDriver::Database
778        );
779        assert!(config.setting(keys::APP_ENV).unwrap().is_developing());
780    }
781
782    #[test]
783    fn a_misspelled_driver_is_a_configuration_error_not_a_fallback() {
784        // The bug this whole layer exists to prevent: `redys` used to boot on
785        // an in-process cache and stay that way until someone noticed the rate
786        // limiter letting through N× its limit.
787        let env = Env::parse("CACHE_DRIVER=redys");
788        let err = default_config(&env, std::path::Path::new(".")).unwrap_err();
789
790        assert!(err.message().contains("CACHE_DRIVER"), "{}", err.message());
791        assert!(err.message().contains("`redys`"), "{}", err.message());
792        assert!(err.message().contains("`redis-cluster`"), "{}", err.message());
793    }
794
795    #[tokio::test]
796    async fn a_misspelled_driver_fails_the_boot_rather_than_being_deferred_forever() {
797        // `Rainier::new` cannot return a `Result`, so the error waits on the
798        // builder. It must not be possible to reach a running application
799        // without seeing it.
800        let mut builder = Rainier::new(".").without_facades().without_tracing();
801        builder.deferred = Some(Error::internal("`QUEUE_DRIVER`: nope"));
802
803        let err = builder.boot().await.unwrap_err();
804        assert!(err.message().contains("QUEUE_DRIVER"), "{}", err.message());
805    }
806
807    #[tokio::test]
808    async fn configuration_can_be_adjusted_while_building() {
809        let app = Rainier::new(".")
810            .without_facades()
811            .without_tracing()
812            .configure(|config| {
813                config.set("app.name", "Custom").unwrap();
814            })
815            .boot()
816            .await
817            .unwrap();
818
819        assert_eq!(app.resolve::<Config>().unwrap().string("app.name").as_deref(), Some("Custom"));
820    }
821
822    #[tokio::test]
823    async fn the_default_middleware_normalises_input() {
824        let app = Rainier::new(".")
825            .without_facades()
826            .without_tracing()
827            .with_routes(|router| {
828                router.post("/echo", |request: rainier_routing::Req| async move {
829                    request.input_or("name", "(absent)")
830                });
831            })
832            .boot()
833            .await
834            .unwrap();
835
836        let response = app
837            .resolve::<Kernel>()
838            .unwrap()
839            .handle_request(
840                Request::builder()
841                    .method(Method::POST)
842                    .uri("/echo")
843                    .json(&serde_json::json!({ "name": "  Ada  " }))
844                    .build(),
845            )
846            .await;
847
848        let body = response.into_http().into_body().collect().await.unwrap();
849        assert_eq!(body, "Ada", "TrimStrings should be global");
850    }
851
852    #[tokio::test]
853    async fn the_shipped_groups_all_build() {
854        // Each of these is a stack an application may attach without wiring
855        // anything. The one that can fail is `web`, whose session stage is
856        // resolved from the container — so this is really asserting that the
857        // builder binds a `SessionManager` before it compiles the router.
858        let app = Rainier::new(".")
859            .without_facades()
860            .without_tracing()
861            .with_routes(|router| {
862                router.get("/a", || async { "a" }).middleware(groups::api());
863                router.get("/b", || async { "b" }).middleware(groups::web());
864                router.get("/c", || async { "c" }).middleware(groups::api_throttled(5));
865                router.get("/d", || async { "d" }).middleware(groups::session());
866                router.get("/e", || async { "e" }).middleware(groups::trust_local_proxies());
867                router.get("/f", || async { "f" }).middleware(groups::normalise_input());
868            })
869            .boot()
870            .await;
871
872        assert!(app.is_ok(), "{:?}", app.err().map(|e| e.message().to_string()));
873    }
874
875    #[tokio::test]
876    async fn booting_binds_encryption_and_sessions() {
877        let app = app().await;
878
879        assert!(app.resolve::<Encryption>().is_ok());
880        assert!(app.resolve::<SessionManager>().is_ok());
881        assert_eq!(app.resolve::<SessionManager>().unwrap().driver(), "memory");
882    }
883
884    #[tokio::test]
885    async fn encryption_round_trips_through_the_container() {
886        let app = app().await;
887        let crypt = app.resolve::<Encryption>().unwrap();
888
889        let sealed = crypt.encrypt("a secret").unwrap();
890        assert_eq!(crypt.decrypt(&sealed).unwrap(), "a secret");
891    }
892
893    #[tokio::test]
894    async fn an_app_key_from_the_environment_is_used() {
895        let key = Key::generate();
896        let sealed = Encryption::from_keys(KeyRing::new(key.clone())).encrypt("before").unwrap();
897
898        let app = Rainier::new(".")
899            .without_facades()
900            .without_tracing()
901            .configure(|_| {})
902            .boot()
903            .await
904            .unwrap();
905
906        // The default boot has no APP_KEY here, so it must *not* read that.
907        assert!(app.resolve::<Encryption>().unwrap().decrypt(&sealed).is_err());
908
909        // With the key on the ring, it must.
910        let with_key = Rainier::new(".")
911            .without_facades()
912            .without_tracing()
913            .with_encryption(Encryption::from_keys(KeyRing::new(key)))
914            .boot()
915            .await
916            .unwrap();
917
918        assert_eq!(with_key.resolve::<Encryption>().unwrap().decrypt(&sealed).unwrap(), "before");
919    }
920
921    #[tokio::test]
922    async fn the_session_middleware_gives_a_route_a_session() {
923        let app = Rainier::new(".")
924            .without_facades()
925            .without_tracing()
926            .with_routes(|router| {
927                router
928                    .get("/count", |request: rainier_routing::Req| async move {
929                        let session = request.session().expect("StartSession ran");
930                        let seen: u64 = session.get("seen").unwrap_or(0);
931                        session.put("seen", seen + 1).unwrap();
932                        format!("{seen}")
933                    })
934                    .middleware(groups::session());
935            })
936            .boot()
937            .await
938            .unwrap();
939
940        let kernel = app.resolve::<Kernel>().unwrap();
941        let response = kernel
942            .handle_request(Request::builder().method(Method::GET).uri("/count").build())
943            .await;
944
945        assert!(response.header("set-cookie").is_some(), "the session should be persisted");
946        let body = response.into_http().into_body().collect().await.unwrap();
947        assert_eq!(body, "0");
948    }
949
950    #[tokio::test]
951    async fn a_route_without_the_session_middleware_has_no_session() {
952        let app = Rainier::new(".")
953            .without_facades()
954            .without_tracing()
955            .with_routes(|router| {
956                router.get("/plain", |request: rainier_routing::Req| async move {
957                    format!("{}", request.session().is_some())
958                });
959            })
960            .boot()
961            .await
962            .unwrap();
963
964        let response = app
965            .resolve::<Kernel>()
966            .unwrap()
967            .handle_request(Request::builder().method(Method::GET).uri("/plain").build())
968            .await;
969
970        let body = response.into_http().into_body().collect().await.unwrap();
971        assert_eq!(body, "false");
972    }
973
974    #[tokio::test]
975    async fn middleware_that_needs_an_unbound_service_fails_at_boot() {
976        // The only way a route's middleware can now fail to attach. A
977        // misspelled name is not on the list, because there is no name.
978        struct NeverBound;
979
980        let result = Rainier::new(".")
981            .without_facades()
982            .without_tracing()
983            .with_routes(|router| {
984                router.get("/a", || async { "a" }).middleware(
985                    rainier_middleware::MiddlewareStack::new().resolved(|_: Arc<NeverBound>| {
986                        StartSession::new(Arc::new(MemorySessionStore::default()))
987                    }),
988                );
989            })
990            .boot()
991            .await;
992
993        let err = result.err().expect("boot should fail");
994        assert!(err.message().contains("/a"), "{}", err.message());
995        assert!(err.message().contains("NeverBound"), "{}", err.message());
996    }
997
998    #[tokio::test]
999    async fn a_route_attaches_middleware_by_value() {
1000        // The headline of the whole design: the middleware itself, no name.
1001        let app = Rainier::new(".")
1002            .without_facades()
1003            .without_tracing()
1004            .with_routes(|router| {
1005                router
1006                    .get("/guarded", || async { "ok" })
1007                    .middleware(rainier_middleware::AddHeaders::security_defaults());
1008            })
1009            .boot()
1010            .await
1011            .unwrap();
1012
1013        let response = app
1014            .resolve::<Kernel>()
1015            .unwrap()
1016            .handle_request(Request::builder().method(Method::GET).uri("/guarded").build())
1017            .await;
1018
1019        assert!(response.header("x-content-type-options").is_some());
1020    }
1021
1022    #[tokio::test]
1023    async fn a_provider_registers_before_the_router_compiles() {
1024        struct AliasProvider;
1025        impl ServiceProvider for AliasProvider {
1026            fn register(&self, app: &Application) -> Result<()> {
1027                app.instance(42u32);
1028                Ok(())
1029            }
1030        }
1031
1032        let app = Rainier::new(".")
1033            .without_facades()
1034            .without_tracing()
1035            .with_provider(AliasProvider)
1036            .boot()
1037            .await
1038            .unwrap();
1039
1040        assert_eq!(*app.resolve::<u32>().unwrap(), 42);
1041    }
1042
1043    #[tokio::test]
1044    async fn listeners_registered_while_building_survive_into_the_dispatcher() {
1045        struct Ping;
1046
1047        let app = Rainier::new(".")
1048            .without_facades()
1049            .without_tracing()
1050            .with_events(|events| {
1051                events.listen(|_: Arc<Ping>| async { Ok(()) });
1052            })
1053            .boot()
1054            .await
1055            .unwrap();
1056
1057        assert!(app.resolve::<Dispatcher>().unwrap().has_listeners::<Ping>());
1058    }
1059}
1060
1061#[cfg(test)]
1062mod storage_tests {
1063    use super::*;
1064    use rainier_cache::CacheExt as _;
1065    use rainier_filesystem::{FilesystemExt as _, MemoryFilesystem};
1066
1067    async fn app() -> Arc<Application> {
1068        Rainier::new(".")
1069            .without_facades()
1070            .without_tracing()
1071            .with_storage(Storage::new(Arc::new(MemoryFilesystem::new())))
1072            .boot()
1073            .await
1074            .expect("boots")
1075    }
1076
1077    #[tokio::test]
1078    async fn booting_binds_a_cache_and_a_storage() {
1079        let app = app().await;
1080
1081        assert_eq!(app.resolve::<CacheManager>().unwrap().driver(), "memory");
1082        assert_eq!(app.resolve::<Storage>().unwrap().driver(), "memory");
1083    }
1084
1085    #[tokio::test]
1086    async fn the_default_storage_is_local_under_the_base_path() {
1087        // A fresh clone should have working storage without configuring one.
1088        let app = Rainier::new(".").without_facades().without_tracing().boot().await.unwrap();
1089
1090        assert_eq!(app.resolve::<Storage>().unwrap().driver(), "local");
1091    }
1092
1093    #[tokio::test]
1094    async fn the_cache_round_trips_through_the_container() {
1095        let app = app().await;
1096        let cache = app.resolve::<CacheManager>().unwrap();
1097
1098        cache.put_string("k", "v", None).await.unwrap();
1099        assert_eq!(cache.get_string("k").await.unwrap().as_deref(), Some("v"));
1100    }
1101
1102    #[tokio::test]
1103    async fn storage_round_trips_through_the_container() {
1104        let app = app().await;
1105        let storage = app.resolve::<Storage>().unwrap();
1106
1107        storage.put_string("uploads/a.txt", "hello").await.unwrap();
1108        assert_eq!(storage.get_string("uploads/a.txt").await.unwrap().as_deref(), Some("hello"));
1109    }
1110
1111    #[tokio::test]
1112    async fn a_traversal_through_the_container_is_still_refused() {
1113        let storage = app().await.resolve::<Storage>().unwrap();
1114        assert!(storage.get("../../etc/passwd").await.is_err());
1115    }
1116}