Skip to main content

apigate_core/routing/
path_sticky.rs

1use crate::backend::BackendPool;
2
3use super::{AffinityKey, CandidateSet, RouteCtx, RouteStrategy, RoutingDecision};
4
5#[derive(Debug, Clone)]
6pub struct PathSticky {
7    param: &'static str,
8}
9
10impl PathSticky {
11    pub fn new(param: &'static str) -> Self {
12        Self { param }
13    }
14}
15
16impl RouteStrategy for PathSticky {
17    fn route<'a>(&self, ctx: &RouteCtx<'a>, _pool: &'a BackendPool) -> RoutingDecision<'a> {
18        let affinity = extract_param(ctx.route_path, ctx.prefix, ctx.uri.path(), self.param)
19            .map(AffinityKey::borrowed);
20
21        RoutingDecision {
22            affinity,
23            candidates: CandidateSet::All,
24        }
25    }
26}
27
28#[inline]
29fn extract_param<'a>(
30    route_path: &str,
31    prefix: &str,
32    uri_path: &'a str,
33    param: &str,
34) -> Option<&'a str> {
35    let stripped = uri_path.strip_prefix(prefix).unwrap_or(uri_path);
36    let mut path_segs = stripped.split('/').filter(|s| !s.is_empty());
37
38    for tmpl in route_path.split('/').filter(|s| !s.is_empty()) {
39        let value = path_segs.next()?;
40        if let Some(name) = tmpl.strip_prefix('{').and_then(|s| s.strip_suffix('}')) {
41            if name == param {
42                return Some(value);
43            }
44        }
45    }
46
47    None
48}