1pub use pingora::server::configuration::ServerConf;
2use serde::{Deserialize, Deserializer, Serialize};
3use std::collections::HashMap;
4use std::sync::Arc;
5
6#[cfg(feature = "api")]
7use utoipa::ToSchema;
8
9#[derive(Debug, Serialize, Deserialize)]
10pub struct RootConfig {
11 pub jokoway: JokowayConfig,
12 pub pingora: Option<ServerConf>,
13}
14
15#[derive(Debug, Serialize, Deserialize, Clone, Default)]
16#[serde(deny_unknown_fields)]
17pub struct HttpServerOptionsConfig {
18 pub keepalive_request_limit: Option<u32>,
19 #[serde(default = "default_false")]
20 pub h2c: bool,
21 #[serde(default = "default_true")]
22 pub allow_connect_method_proxying: bool,
23}
24
25fn default_false() -> bool {
26 false
27}
28
29#[derive(Debug, Serialize, Deserialize, Clone, Default)]
30pub struct JokowayConfig {
31 pub http_listen: String,
32 pub https_listen: Option<String>,
33 pub api: Option<ApiSettings>,
34 pub tls: Option<TlsSettings>,
35 pub http_server_options: Option<HttpServerOptionsConfig>,
36
37 #[serde(default)]
38 pub upstreams: Vec<Upstream>,
39 #[serde(default)]
40 pub services: Vec<Arc<Service>>,
41 #[serde(default)]
42 pub dns: Option<DnsSettings>,
43
44 #[serde(flatten)]
46 pub extra: HashMap<String, serde_yaml::Value>,
47}
48
49#[derive(Debug, Serialize, Deserialize, Clone, Default)]
50#[serde(deny_unknown_fields)]
51pub struct ApiSettings {
52 pub listen: String,
53 #[serde(default, deserialize_with = "deserialize_basic_auth_credentials")]
54 pub basic_auth: Option<Vec<BasicAuth>>,
55 #[serde(default, deserialize_with = "deserialize_api_keys")]
56 pub api_keys: Option<Vec<String>>,
57 pub rate_limit: Option<RateLimit>,
58 pub openapi: Option<OpenApiSettings>,
59}
60
61#[derive(Deserialize)]
62#[serde(untagged)]
63enum BasicAuthCredentials {
64 Single(BasicAuth),
65 Multiple(Vec<BasicAuth>),
66}
67
68#[derive(Deserialize)]
69#[serde(untagged)]
70enum ApiKeyCredentials {
71 Single(String),
72 Multiple(Vec<String>),
73}
74
75fn deserialize_basic_auth_credentials<'de, D>(
76 deserializer: D,
77) -> Result<Option<Vec<BasicAuth>>, D::Error>
78where
79 D: Deserializer<'de>,
80{
81 let value = Option::<BasicAuthCredentials>::deserialize(deserializer)?;
82 Ok(value.map(|v| match v {
83 BasicAuthCredentials::Single(auth) => vec![auth],
84 BasicAuthCredentials::Multiple(auths) => auths,
85 }))
86}
87
88fn deserialize_api_keys<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
89where
90 D: Deserializer<'de>,
91{
92 let value = Option::<ApiKeyCredentials>::deserialize(deserializer)?;
93 Ok(value.map(|v| match v {
94 ApiKeyCredentials::Single(key) => vec![key],
95 ApiKeyCredentials::Multiple(keys) => keys,
96 }))
97}
98
99#[derive(Debug, Serialize, Deserialize, Clone, Default)]
100#[serde(deny_unknown_fields)]
101pub struct BasicAuth {
102 pub username: String,
103 pub password: String,
104}
105
106#[derive(Debug, Serialize, Deserialize, Clone, Default)]
107#[serde(deny_unknown_fields)]
108pub struct RateLimit {
109 pub requests_per_second: u32,
110 pub burst: u32,
111}
112
113#[derive(Debug, Serialize, Deserialize, Clone, Default)]
114#[serde(deny_unknown_fields)]
115pub struct DnsSettings {
116 pub nameservers: Option<Vec<String>>,
117 pub timeout: Option<u64>,
118 pub attempts: Option<usize>,
119 pub strategy: Option<String>,
120 pub cache_size: Option<usize>,
121 #[serde(default = "default_true")]
122 pub use_hosts_file: bool,
123 #[serde(default = "default_true")]
124 pub system_conf: bool,
125}
126
127fn default_true() -> bool {
128 true
129}
130
131#[derive(Debug, Serialize, Deserialize, Clone, Default)]
132#[cfg_attr(feature = "api", derive(ToSchema))]
133#[serde(deny_unknown_fields)]
134pub struct TcpKeepaliveConfig {
135 pub idle: Option<u64>,
136 pub interval: Option<u64>,
137 pub count: Option<u32>,
138 #[cfg(target_os = "linux")]
139 pub user_timeout: Option<u64>,
140}
141
142#[derive(Debug, Serialize, Deserialize, Clone, Default)]
143#[cfg_attr(feature = "api", derive(ToSchema))]
144#[serde(deny_unknown_fields)]
145pub struct PeerOptions {
146 pub connection_timeout: Option<u64>,
147 pub read_timeout: Option<u64>,
148 pub idle_timeout: Option<u64>,
149 pub write_timeout: Option<u64>,
150 pub verify_cert: Option<bool>,
151 pub verify_hostname: Option<bool>,
152 pub alternative_cn: Option<String>,
153 pub alpn: Option<String>,
155 pub tcp_keepalive: Option<TcpKeepaliveConfig>,
156 pub tcp_recv_buf: Option<usize>,
157 pub dscp: Option<u8>,
158 pub h2_ping_interval: Option<u64>,
159 pub max_h2_streams: Option<usize>,
160 pub allow_h1_response_invalid_content_length: Option<bool>,
161 pub extra_proxy_headers: Option<std::collections::HashMap<String, String>>,
162 pub curves: Option<String>,
163 pub second_keyshare: Option<bool>,
164 pub tcp_fast_open: Option<bool>,
165 pub cacert: Option<String>,
166 pub client_cert: Option<String>,
167 pub client_key: Option<String>,
168 pub sni: Option<String>,
169}
170
171#[derive(Debug, Serialize, Deserialize, Clone, Default)]
172#[serde(deny_unknown_fields)]
173pub struct TlsSettings {
174 pub cacert: Option<String>,
175 pub server_cert: Option<String>,
176 pub server_key: Option<String>,
177 pub sans: Option<Vec<String>>,
178 pub cipher_suites: Option<Vec<String>>,
179}
180
181fn default_openapi_title() -> String {
182 "Jokoway API".to_string()
183}
184
185fn default_openapi_description() -> String {
186 "Jokoway Management API".to_string()
187}
188
189fn default_openapi_root_path() -> String {
190 "/docs".to_string()
191}
192
193#[derive(Debug, Serialize, Deserialize, Clone)]
194#[serde(deny_unknown_fields)]
195pub struct OpenApiSettings {
196 #[serde(default = "default_openapi_title")]
197 pub title: String,
198 #[serde(default = "default_openapi_description")]
199 pub description: String,
200 #[serde(default = "default_openapi_root_path")]
201 pub root_path: String,
202}
203
204impl Default for OpenApiSettings {
205 fn default() -> Self {
206 Self {
207 title: default_openapi_title(),
208 description: default_openapi_description(),
209 root_path: default_openapi_root_path(),
210 }
211 }
212}
213
214#[derive(Debug, Serialize, Deserialize, Clone, Default)]
215#[cfg_attr(feature = "api", derive(ToSchema))]
216#[serde(deny_unknown_fields)]
217pub struct Upstream {
218 pub name: String,
219 pub peer_options: Option<PeerOptions>,
220 #[serde(default)]
221 pub servers: Vec<UpstreamServer>,
222 pub health_check: Option<HealthCheckConfig>,
223 pub update_frequency: Option<u64>,
224}
225
226#[derive(Debug, Serialize, Deserialize, Clone, Default)]
227#[cfg_attr(feature = "api", derive(ToSchema))]
228#[serde(deny_unknown_fields)]
229pub struct UpstreamServer {
230 pub host: String,
231 pub weight: Option<u32>,
232 pub tls: Option<bool>,
233 pub peer_options: Option<PeerOptions>,
234}
235
236#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
237#[cfg_attr(feature = "api", derive(ToSchema))]
238pub enum ServiceProtocol {
239 #[serde(rename = "http")]
240 Http,
241 #[serde(rename = "https")]
242 Https,
243 #[serde(rename = "ws")]
244 Ws,
245 #[serde(rename = "wss")]
246 Wss,
247 #[serde(rename = "grpc")]
248 Grpc,
249 #[serde(rename = "grpcs")]
250 Grpcs,
251}
252
253#[derive(Debug, Serialize, Deserialize, Clone, Default)]
254#[cfg_attr(feature = "api", derive(ToSchema))]
255#[serde(deny_unknown_fields)]
256pub struct Service {
257 pub name: String,
258 pub host: String,
259 pub protocols: Vec<ServiceProtocol>,
260 pub max_retries: Option<u32>,
261 #[serde(default)]
262 pub routes: Vec<Route>,
263}
264
265#[derive(Debug, Serialize, Deserialize, Clone, Default)]
266#[cfg_attr(feature = "api", derive(ToSchema))]
267#[serde(deny_unknown_fields)]
268pub struct Route {
269 pub name: String,
270 pub rule: String,
271 pub priority: Option<i32>,
272 pub max_retries: Option<u32>,
273 pub request_transformer: Option<String>,
274 pub response_transformer: Option<String>,
275}
276
277fn default_health_check_interval() -> u64 {
280 10
281}
282
283fn default_health_check_timeout() -> u64 {
284 3
285}
286
287fn default_unhealthy_threshold() -> u32 {
288 3
289}
290
291fn default_healthy_threshold() -> u32 {
292 2
293}
294
295#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
296#[cfg_attr(feature = "api", derive(ToSchema))]
297#[serde(rename_all = "lowercase")]
298pub enum HealthCheckType {
299 Http,
300 Https,
301 Tcp,
302}
303
304#[derive(Debug, Serialize, Deserialize, Clone)]
305#[cfg_attr(feature = "api", derive(ToSchema))]
306#[serde(deny_unknown_fields)]
307pub struct HealthCheckConfig {
308 #[serde(rename = "type")]
309 pub check_type: HealthCheckType,
310
311 #[serde(default = "default_health_check_interval")]
312 pub interval: u64, #[serde(default = "default_health_check_timeout")]
315 pub timeout: u64, #[serde(default = "default_unhealthy_threshold")]
318 pub unhealthy_threshold: u32,
319
320 #[serde(default = "default_healthy_threshold")]
321 pub healthy_threshold: u32,
322
323 pub path: Option<String>,
325 pub method: Option<String>, pub expected_status: Option<Vec<u16>>,
327 pub headers: Option<HashMap<String, String>>,
328}