apigate_core/routing/
path_sticky.rs1use 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}