hive_router/
shared_state.rs1use graphql_tools::validation::validate::ValidationPlan;
2use hive_router_config::HiveRouterConfig;
3use hive_router_plan_executor::headers::{
4 compile::compile_headers_plan, errors::HeaderRuleCompileError, plan::HeaderRulesPlan,
5};
6use moka::future::Cache;
7use moka::Expiry;
8use std::sync::Arc;
9use std::time::{Duration, SystemTime, UNIX_EPOCH};
10
11use crate::jwt::context::JwtTokenPayload;
12use crate::jwt::JwtAuthRuntime;
13use crate::pipeline::cors::{CORSConfigError, Cors};
14use crate::pipeline::progressive_override::{OverrideLabelsCompileError, OverrideLabelsEvaluator};
15
16pub type JwtClaimsCache = Cache<String, Arc<JwtTokenPayload>>;
17
18const DEFAULT_JWT_CACHE_TTL_SECS: u64 = 5;
20
21struct JwtClaimsExpiry;
22
23impl Expiry<String, Arc<JwtTokenPayload>> for JwtClaimsExpiry {
24 fn expire_after_create(
25 &self,
26 _key: &String,
27 value: &Arc<JwtTokenPayload>,
28 _created_at: std::time::Instant,
29 ) -> Option<Duration> {
30 const DEFAULT_TTL: Duration = Duration::from_secs(DEFAULT_JWT_CACHE_TTL_SECS);
31
32 let exp = match value.claims.exp {
34 Some(e) => e,
35 None => return Some(DEFAULT_TTL),
36 };
37
38 let now = match SystemTime::now().duration_since(UNIX_EPOCH) {
39 Ok(duration) => duration.as_secs(),
40 Err(_) => return Some(DEFAULT_TTL), };
42
43 if exp <= now {
45 return Some(Duration::ZERO);
46 }
47
48 let time_until_exp = Duration::from_secs(exp - now);
50
51 Some(DEFAULT_TTL.min(time_until_exp))
55 }
56}
57
58pub struct RouterSharedState {
59 pub validation_plan: ValidationPlan,
60 pub parse_cache: Cache<u64, Arc<graphql_parser::query::Document<'static, String>>>,
61 pub router_config: Arc<HiveRouterConfig>,
62 pub headers_plan: HeaderRulesPlan,
63 pub override_labels_evaluator: OverrideLabelsEvaluator,
64 pub cors_runtime: Option<Cors>,
65 pub jwt_claims_cache: JwtClaimsCache,
70 pub jwt_auth_runtime: Option<JwtAuthRuntime>,
71}
72
73impl RouterSharedState {
74 pub fn new(
75 router_config: Arc<HiveRouterConfig>,
76 jwt_auth_runtime: Option<JwtAuthRuntime>,
77 ) -> Result<Self, SharedStateError> {
78 Ok(Self {
79 validation_plan: graphql_tools::validation::rules::default_rules_validation_plan(),
80 headers_plan: compile_headers_plan(&router_config.headers).map_err(Box::new)?,
81 parse_cache: moka::future::Cache::new(1000),
82 cors_runtime: Cors::from_config(&router_config.cors).map_err(Box::new)?,
83 jwt_claims_cache: Cache::builder()
84 .max_capacity(10_000)
87 .expire_after(JwtClaimsExpiry)
88 .build(),
89 router_config: router_config.clone(),
90 override_labels_evaluator: OverrideLabelsEvaluator::from_config(
91 &router_config.override_labels,
92 )
93 .map_err(Box::new)?,
94 jwt_auth_runtime,
95 })
96 }
97}
98
99#[derive(thiserror::Error, Debug)]
100pub enum SharedStateError {
101 #[error("invalid headers config: {0}")]
102 HeaderRuleCompile(#[from] Box<HeaderRuleCompileError>),
103 #[error("invalid regex in CORS config: {0}")]
104 CORSConfig(#[from] Box<CORSConfigError>),
105 #[error("invalid override labels config: {0}")]
106 OverrideLabelsCompile(#[from] Box<OverrideLabelsCompileError>),
107}