monolake_services/common/
context.rs

1//! Context insertion service for request handling, with support for `certain_map`.
2//!
3//! This module provides a `ContextService` that inserts context information,
4//! into the request processing pipeline. It's designed
5//! to work seamlessly with the `service_async` framework and the `certain_map` crate
6//! for efficient context management.
7//!
8//! # Key Components
9//!
10//! - [`ContextService`]: The main service component that adds context information to requests.
11//!
12//! # Features
13//!
14//! - Works with `certain_map` for flexible and type-safe context management
15//!
16//! # Usage with certain_map
17//!
18//! `ContextService` is designed to work with contexts defined using the `certain_map` macro.
19//! This allows for efficient and type-safe context management. Here's an example of how to
20//! define a context and use it with `ContextService`:
21//!
22//! # Usage in a Service Stack
23//!
24//! `ContextService` is typically used as part of a larger service stack. Here's an example
25//! from a Layer 7 proxy factory:
26//!
27//! ```ignore
28//! use monolake_services::common::ContextService;
29//! use service_async::stack::FactoryStack;
30//!
31//! let stacks = FactoryStack::new(config)
32//!         // ... other layers ...
33//!         .push(ContextService::<EmptyContext, _>::layer())
34//!         // ... more processing ...
35//!         ;
36//! // ... rest of the factory setup ...
37//! ```
38//!
39//! In this example, `ContextService` is used to transform an `EmptyContext` into a `FullContext`
40//! by setting the `peer_addr` field.
41use std::marker::PhantomData;
42
43use certain_map::Handler;
44use monolake_core::{context::PeerAddr, listener::AcceptedAddr};
45use service_async::{
46    layer::{layer_fn, FactoryLayer},
47    AsyncMakeService, MakeService, ParamSet, Service,
48};
49
50/// A service to insert Context into the request processing pipeline, compatible with `certain_map`.
51#[derive(Debug)]
52pub struct ContextService<CXStore, T> {
53    pub inner: T,
54    pub ctx: PhantomData<CXStore>,
55}
56
57unsafe impl<CXStore, T: Send> Send for ContextService<CXStore, T> {}
58unsafe impl<CXStore, T: Sync> Sync for ContextService<CXStore, T> {}
59
60// Manually impl Clone because CXStore does not have to impl Clone.
61impl<CXStore, T> Clone for ContextService<CXStore, T>
62where
63    T: Clone,
64{
65    fn clone(&self) -> Self {
66        Self {
67            inner: self.inner.clone(),
68            ctx: PhantomData,
69        }
70    }
71}
72
73// Manually impl Copy because CXStore does not have to impl Copy.
74impl<CXStore, T> Copy for ContextService<CXStore, T> where T: Copy {}
75
76impl<R, T, CXStore, Resp, Err> Service<(R, AcceptedAddr)> for ContextService<CXStore, T>
77where
78    CXStore: Default + Handler,
79    // HRTB is your friend!
80    // Please pay attention to when to use bound associated types and when to use associated types
81    // directly(here `Transformed` is not bound but `Response` and `Error` are).
82    for<'a> CXStore::Hdr<'a>: ParamSet<PeerAddr>,
83    for<'a> T: Service<
84        (R, <CXStore::Hdr<'a> as ParamSet<PeerAddr>>::Transformed),
85        Response = Resp,
86        Error = Err,
87    >,
88{
89    type Response = Resp;
90    type Error = Err;
91
92    async fn call(&self, (req, addr): (R, AcceptedAddr)) -> Result<Self::Response, Self::Error> {
93        let mut store = CXStore::default();
94        let hdr = store.handler();
95        let hdr = hdr.param_set(PeerAddr(addr));
96        self.inner.call((req, hdr)).await
97    }
98}
99
100impl<CX, F> ContextService<CX, F> {
101    pub fn layer<C>() -> impl FactoryLayer<C, F, Factory = Self> {
102        layer_fn(|_: &C, inner| ContextService {
103            inner,
104            ctx: PhantomData,
105        })
106    }
107}
108
109impl<CXStore, F: MakeService> MakeService for ContextService<CXStore, F> {
110    type Service = ContextService<CXStore, F::Service>;
111    type Error = F::Error;
112
113    fn make_via_ref(&self, old: Option<&Self::Service>) -> Result<Self::Service, Self::Error> {
114        Ok(ContextService {
115            ctx: PhantomData,
116            inner: self
117                .inner
118                .make_via_ref(old.map(|o| &o.inner))
119                .map_err(Into::into)?,
120        })
121    }
122}
123
124impl<CXStore, F: AsyncMakeService> AsyncMakeService for ContextService<CXStore, F> {
125    type Service = ContextService<CXStore, F::Service>;
126    type Error = F::Error;
127
128    async fn make_via_ref(
129        &self,
130        old: Option<&Self::Service>,
131    ) -> Result<Self::Service, Self::Error> {
132        Ok(ContextService {
133            ctx: PhantomData,
134            inner: self
135                .inner
136                .make_via_ref(old.map(|o| &o.inner))
137                .await
138                .map_err(Into::into)?,
139        })
140    }
141}