Skip to main content

sofie_http/
serving.rs

1use std::num::NonZeroUsize;
2use std::sync::Arc;
3
4use sofie_auth::SigningKeys;
5use sofie_core::Result;
6
7use crate::health::{Dependency, Readiness};
8use crate::{Area, RequestContext};
9
10const UNNAMED: &str = "sofie";
11
12#[derive(Default)]
13pub struct Serving {
14    pub(crate) name: Option<&'static str>,
15    pub(crate) max_connections: Option<NonZeroUsize>,
16    pub(crate) keys: Option<SigningKeys>,
17    pub(crate) dependencies: Vec<Dependency>,
18    pub(crate) readiness: Readiness,
19    #[cfg(feature = "trusted-proxies")]
20    pub(crate) proxies: crate::TrustedProxies,
21    #[cfg(feature = "metrics")]
22    pub(crate) metrics_token: Option<crate::MetricsToken>,
23    #[cfg(feature = "browser-telemetry")]
24    pub(crate) browser_sample: crate::BrowserSample,
25    #[cfg(feature = "shield")]
26    pub(crate) shield: crate::Shield,
27    #[cfg(feature = "cors")]
28    pub(crate) cors_origins: crate::CorsOrigins,
29    #[cfg(feature = "storage")]
30    pub(crate) storage: Option<sofie_storage::Storage>,
31    #[cfg(feature = "tls")]
32    pub(crate) tls: Option<crate::Tls>,
33    #[cfg(feature = "engine")]
34    pub(crate) content_security: std::collections::HashMap<&'static str, crate::ContentSecurity>,
35    #[cfg(feature = "engine")]
36    pub(crate) content_security_everywhere: crate::ContentSecurity,
37}
38
39/// Given by `Serving::named`, `sofie` without one; for an authenticator's issuer and the like.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct AppName(&'static str);
42
43impl AppName {
44    pub(crate) fn of_serving(serving: &Serving) -> Self {
45        Self(serving.name.unwrap_or(UNNAMED))
46    }
47
48    #[must_use]
49    pub fn as_str(&self) -> &'static str {
50        self.0
51    }
52}
53
54impl std::fmt::Display for AppName {
55    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        formatter.write_str(self.0)
57    }
58}
59
60impl<A: Area, M> sofie_db::Resolve<RequestContext<A, M>> for AppName {
61    fn resolve(context: &mut RequestContext<A, M>) -> impl Future<Output = Result<Self>> + Send {
62        std::future::ready(Ok(context.matched.app))
63    }
64}
65
66impl Serving {
67    /// Names the app's cookies, `__Host-<name>-session` (cookies do not separate by port), and
68    /// the [`AppName`] handlers take.
69    #[must_use]
70    pub fn named(mut self, name: &'static str) -> Self {
71        self.name = Some(name);
72        self
73    }
74
75    /// Without them, serving reads `SOFIE_SIGNING_KEYS` and refuses to start when it is missing.
76    #[must_use]
77    pub fn signing_keys(mut self, keys: SigningKeys) -> Self {
78        self.keys = Some(keys);
79        self
80    }
81
82    /// Connections served at once, 4096 by default; keep it below `ulimit -n`.
83    #[must_use]
84    pub fn max_connections(mut self, limit: NonZeroUsize) -> Self {
85        self.max_connections = Some(limit);
86        self
87    }
88
89    /// Checked after the database and the queue; while it fails, the instance is not ready.
90    #[must_use]
91    pub fn dependency<F>(
92        mut self,
93        name: &'static str,
94        check: impl Fn() -> F + Send + Sync + 'static,
95    ) -> Self
96    where
97        F: Future<Output = Result<()>> + Send + 'static,
98    {
99        self.dependencies.push(Dependency {
100            name,
101            check: Arc::new(move || Box::pin(check())),
102        });
103        self
104    }
105}