spacegate_model/
http_route.rs

1use std::fmt::Display;
2
3use crate::constants::DEFAULT_NAMESPACE;
4
5pub use super::route_match::*;
6use serde::{Deserialize, Serialize};
7
8use super::{gateway::SgBackendProtocol, PluginInstanceId};
9
10/// HTTPRoute provides a way to route HTTP requests.
11///
12/// Reference: [Kubernetes Gateway](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.HTTPRoute)
13#[derive(Debug, Serialize, Deserialize, Clone)]
14#[cfg_attr(feature = "typegen", derive(ts_rs::TS), ts(export))]
15#[serde(default)]
16pub struct SgHttpRoute<P = PluginInstanceId> {
17    /// Route name
18    pub route_name: String,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    /// Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request.
21    pub hostnames: Option<Vec<String>>,
22    #[serde(skip_serializing_if = "Vec::is_empty")]
23    /// Filters define the filters that are applied to requests that match this hostnames.
24    pub plugins: Vec<P>,
25    #[serde(skip_serializing_if = "Vec::is_empty")]
26    /// Rules are a list of HTTP matchers, filters and actions.
27    pub rules: Vec<SgHttpRouteRule<P>>,
28    /// Rule priority, the rule of higher priority will be chosen.
29    pub priority: i16,
30}
31
32impl<P> SgHttpRoute<P> {
33    pub fn map_plugins<F, T>(self, mut f: F) -> SgHttpRoute<T>
34    where
35        F: FnMut(P) -> T,
36    {
37        SgHttpRoute {
38            route_name: self.route_name,
39            hostnames: self.hostnames,
40            plugins: self.plugins.into_iter().map(&mut f).collect(),
41            rules: self.rules.into_iter().map(|rule| rule.map_plugins(&mut f)).collect(),
42            priority: self.priority,
43        }
44    }
45}
46
47impl<P> Default for SgHttpRoute<P> {
48    fn default() -> Self {
49        Self {
50            route_name: Default::default(),
51            hostnames: Default::default(),
52            plugins: Default::default(),
53            rules: Default::default(),
54            priority: 1,
55        }
56    }
57}
58
59/// HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object
60#[derive(Debug, Serialize, Deserialize, Clone)]
61#[cfg_attr(feature = "typegen", derive(ts_rs::TS), ts(export))]
62#[serde(default)]
63pub struct SgHttpRouteRule<P = PluginInstanceId> {
64    #[serde(skip_serializing_if = "Option::is_none")]
65    /// Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if any one of the matches is satisfied.
66    pub matches: Option<Vec<SgHttpRouteMatch>>,
67    #[serde(skip_serializing_if = "Vec::is_empty")]
68    /// Filters define the filters that are applied to requests that match this rule.
69    pub plugins: Vec<P>,
70    #[serde(skip_serializing_if = "Vec::is_empty")]
71    /// BackendRefs defines the backend(s) where matching requests should be sent.
72    pub backends: Vec<SgBackendRef<P>>,
73    #[serde(skip_serializing_if = "Option::is_none")]
74    /// Timeout define the timeout for requests that match this rule.
75    pub timeout_ms: Option<u32>,
76}
77
78impl<P> SgHttpRouteRule<P> {
79    pub fn map_plugins<F, T>(self, mut f: F) -> SgHttpRouteRule<T>
80    where
81        F: FnMut(P) -> T,
82    {
83        SgHttpRouteRule {
84            matches: self.matches,
85            plugins: self.plugins.into_iter().map(&mut f).collect(),
86            backends: self.backends.into_iter().map(|backend| backend.map_plugins(&mut f)).collect(),
87            timeout_ms: self.timeout_ms,
88        }
89    }
90}
91
92impl<P> Default for SgHttpRouteRule<P> {
93    fn default() -> Self {
94        Self {
95            matches: Default::default(),
96            plugins: Default::default(),
97            backends: Default::default(),
98            timeout_ms: Default::default(),
99        }
100    }
101}
102
103/// BackendRef defines how a HTTPRoute should forward an HTTP request.
104#[derive(Debug, Serialize, Deserialize, Clone)]
105#[cfg_attr(feature = "typegen", derive(ts_rs::TS), ts(export))]
106#[serde(default)]
107pub struct SgBackendRef<P = PluginInstanceId> {
108    // #[serde(flatten)]
109    pub host: BackendHost,
110    #[serde(skip_serializing_if = "Option::is_none")]
111    /// Port specifies the destination port number to use for this resource.
112    pub port: Option<u16>,
113    #[serde(skip_serializing_if = "Option::is_none")]
114    /// Timeout specifies the timeout for requests forwarded to the referenced backend.
115    pub timeout_ms: Option<u32>,
116    #[serde(skip_serializing_if = "Option::is_none")]
117    // Protocol specifies the protocol used to talk to the referenced backend.
118    pub protocol: Option<SgBackendProtocol>,
119    #[serde(skip_serializing_if = "Option::is_none")]
120    /// Downgrade HTTP2 connections, it is useful when the backend does not support HTTP2.
121    pub downgrade_http2: Option<bool>,
122    #[serde(skip_serializing_if = "Option::is_none")]
123    /// Weight specifies the proportion of requests forwarded to the referenced backend.
124    /// This is computed as weight/(sum of all weights in this BackendRefs list).
125    /// For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports.
126    /// Weight is not a percentage and the sum of weights does not need to equal 100.
127    pub weight: Option<u16>,
128    #[serde(skip_serializing_if = "Vec::is_empty")]
129    /// plugins define the filters that are applied to backend that match this hostnames.
130    ///
131    /// # Notice!
132    /// this field is ordered, the first plugin will be the outermost plugin.
133    pub plugins: Vec<P>,
134}
135
136impl<P> SgBackendRef<P> {
137    pub fn map_plugins<F, T>(self, f: F) -> SgBackendRef<T>
138    where
139        F: FnMut(P) -> T,
140    {
141        SgBackendRef {
142            host: self.host,
143            port: self.port,
144            timeout_ms: self.timeout_ms,
145            protocol: self.protocol,
146            downgrade_http2: self.downgrade_http2,
147            weight: self.weight,
148            plugins: self.plugins.into_iter().map(f).collect(),
149        }
150    }
151
152    pub fn get_host(&self) -> String {
153        self.host.to_string()
154    }
155}
156
157impl<P> Default for SgBackendRef<P> {
158    fn default() -> Self {
159        Self {
160            host: Default::default(),
161            port: Default::default(),
162            timeout_ms: Default::default(),
163            downgrade_http2: Default::default(),
164            protocol: Default::default(),
165            weight: Default::default(),
166            plugins: Default::default(),
167        }
168    }
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
172#[cfg_attr(feature = "typegen", derive(ts_rs::TS), ts(export))]
173pub struct K8sServiceData {
174    pub name: String,
175    #[serde(alias = "ns")]
176    pub namespace: Option<String>,
177}
178
179impl Display for K8sServiceData {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        match self.namespace {
182            Some(ref ns) => write!(f, "{}.{}", self.name, ns),
183            None => write!(f, "{}.{}", self.name, DEFAULT_NAMESPACE),
184        }
185    }
186}
187
188#[derive(Debug, Serialize, Deserialize, Clone)]
189#[cfg_attr(feature = "typegen", derive(ts_rs::TS), ts(export))]
190#[serde(tag = "kind")]
191pub enum BackendHost {
192    Host { host: String },
193    K8sService(K8sServiceData),
194    File { path: String },
195}
196
197impl Display for BackendHost {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        match self {
200            Self::Host { host } => write!(f, "{}", host),
201            Self::K8sService(k8s_service) => write!(f, "{}", k8s_service),
202            Self::File { path } => write!(f, "{}", path),
203        }
204    }
205}
206
207impl Default for BackendHost {
208    fn default() -> Self {
209        Self::Host { host: String::default() }
210    }
211}