pub use pingora::server::configuration::ServerConf;
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
#[cfg(feature = "api")]
use utoipa::ToSchema;
#[derive(Debug, Serialize, Deserialize)]
pub struct RootConfig {
pub jokoway: JokowayConfig,
pub pingora: Option<ServerConf>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(deny_unknown_fields)]
pub struct HttpServerOptionsConfig {
pub keepalive_request_limit: Option<u32>,
#[serde(default = "default_false")]
pub h2c: bool,
#[serde(default = "default_true")]
pub allow_connect_method_proxying: bool,
}
fn default_false() -> bool {
false
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct JokowayConfig {
pub http_listen: String,
pub https_listen: Option<String>,
pub api: Option<ApiSettings>,
pub tls: Option<TlsSettings>,
pub http_server_options: Option<HttpServerOptionsConfig>,
#[serde(default)]
pub upstreams: Vec<Upstream>,
#[serde(default)]
pub services: Vec<Arc<Service>>,
#[serde(default)]
pub dns: Option<DnsSettings>,
#[serde(flatten)]
pub extra: HashMap<String, serde_yaml::Value>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(deny_unknown_fields)]
pub struct ApiSettings {
pub listen: String,
#[serde(default, deserialize_with = "deserialize_basic_auth_credentials")]
pub basic_auth: Option<Vec<BasicAuth>>,
#[serde(default, deserialize_with = "deserialize_api_keys")]
pub api_keys: Option<Vec<String>>,
pub rate_limit: Option<RateLimit>,
pub openapi: Option<OpenApiSettings>,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum BasicAuthCredentials {
Single(BasicAuth),
Multiple(Vec<BasicAuth>),
}
#[derive(Deserialize)]
#[serde(untagged)]
enum ApiKeyCredentials {
Single(String),
Multiple(Vec<String>),
}
fn deserialize_basic_auth_credentials<'de, D>(
deserializer: D,
) -> Result<Option<Vec<BasicAuth>>, D::Error>
where
D: Deserializer<'de>,
{
let value = Option::<BasicAuthCredentials>::deserialize(deserializer)?;
Ok(value.map(|v| match v {
BasicAuthCredentials::Single(auth) => vec![auth],
BasicAuthCredentials::Multiple(auths) => auths,
}))
}
fn deserialize_api_keys<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
where
D: Deserializer<'de>,
{
let value = Option::<ApiKeyCredentials>::deserialize(deserializer)?;
Ok(value.map(|v| match v {
ApiKeyCredentials::Single(key) => vec![key],
ApiKeyCredentials::Multiple(keys) => keys,
}))
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(deny_unknown_fields)]
pub struct BasicAuth {
pub username: String,
pub password: String,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(deny_unknown_fields)]
pub struct RateLimit {
pub requests_per_second: u32,
pub burst: u32,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(deny_unknown_fields)]
pub struct DnsSettings {
pub nameservers: Option<Vec<String>>,
pub timeout: Option<u64>,
pub attempts: Option<usize>,
pub strategy: Option<String>,
pub cache_size: Option<usize>,
#[serde(default = "default_true")]
pub use_hosts_file: bool,
#[serde(default = "default_true")]
pub system_conf: bool,
}
fn default_true() -> bool {
true
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[cfg_attr(feature = "api", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct TcpKeepaliveConfig {
pub idle: Option<u64>,
pub interval: Option<u64>,
pub count: Option<u32>,
#[cfg(target_os = "linux")]
pub user_timeout: Option<u64>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[cfg_attr(feature = "api", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct PeerOptions {
pub connection_timeout: Option<u64>,
pub read_timeout: Option<u64>,
pub idle_timeout: Option<u64>,
pub write_timeout: Option<u64>,
pub verify_cert: Option<bool>,
pub verify_hostname: Option<bool>,
pub alternative_cn: Option<String>,
pub alpn: Option<String>,
pub tcp_keepalive: Option<TcpKeepaliveConfig>,
pub tcp_recv_buf: Option<usize>,
pub dscp: Option<u8>,
pub h2_ping_interval: Option<u64>,
pub max_h2_streams: Option<usize>,
pub allow_h1_response_invalid_content_length: Option<bool>,
pub extra_proxy_headers: Option<std::collections::HashMap<String, String>>,
pub curves: Option<String>,
pub second_keyshare: Option<bool>,
pub tcp_fast_open: Option<bool>,
pub cacert: Option<String>,
pub client_cert: Option<String>,
pub client_key: Option<String>,
pub sni: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(deny_unknown_fields)]
pub struct TlsSettings {
pub cacert: Option<String>,
pub server_cert: Option<String>,
pub server_key: Option<String>,
pub sans: Option<Vec<String>>,
pub cipher_suites: Option<Vec<String>>,
}
fn default_openapi_title() -> String {
"Jokoway API".to_string()
}
fn default_openapi_description() -> String {
"Jokoway Management API".to_string()
}
fn default_openapi_root_path() -> String {
"/docs".to_string()
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct OpenApiSettings {
#[serde(default = "default_openapi_title")]
pub title: String,
#[serde(default = "default_openapi_description")]
pub description: String,
#[serde(default = "default_openapi_root_path")]
pub root_path: String,
}
impl Default for OpenApiSettings {
fn default() -> Self {
Self {
title: default_openapi_title(),
description: default_openapi_description(),
root_path: default_openapi_root_path(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[cfg_attr(feature = "api", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct Upstream {
pub name: String,
pub peer_options: Option<PeerOptions>,
#[serde(default)]
pub servers: Vec<UpstreamServer>,
pub health_check: Option<HealthCheckConfig>,
pub update_frequency: Option<u64>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[cfg_attr(feature = "api", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct UpstreamServer {
pub host: String,
pub weight: Option<u32>,
pub tls: Option<bool>,
pub peer_options: Option<PeerOptions>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "api", derive(ToSchema))]
pub enum ServiceProtocol {
#[serde(rename = "http")]
Http,
#[serde(rename = "https")]
Https,
#[serde(rename = "ws")]
Ws,
#[serde(rename = "wss")]
Wss,
#[serde(rename = "grpc")]
Grpc,
#[serde(rename = "grpcs")]
Grpcs,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[cfg_attr(feature = "api", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct Service {
pub name: String,
pub host: String,
pub protocols: Vec<ServiceProtocol>,
pub max_retries: Option<u32>,
#[serde(default)]
pub routes: Vec<Route>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[cfg_attr(feature = "api", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct Route {
pub name: String,
pub rule: String,
pub priority: Option<i32>,
pub max_retries: Option<u32>,
pub request_transformer: Option<String>,
pub response_transformer: Option<String>,
}
fn default_health_check_interval() -> u64 {
10
}
fn default_health_check_timeout() -> u64 {
3
}
fn default_unhealthy_threshold() -> u32 {
3
}
fn default_healthy_threshold() -> u32 {
2
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[cfg_attr(feature = "api", derive(ToSchema))]
#[serde(rename_all = "lowercase")]
pub enum HealthCheckType {
Http,
Https,
Tcp,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "api", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct HealthCheckConfig {
#[serde(rename = "type")]
pub check_type: HealthCheckType,
#[serde(default = "default_health_check_interval")]
pub interval: u64,
#[serde(default = "default_health_check_timeout")]
pub timeout: u64,
#[serde(default = "default_unhealthy_threshold")]
pub unhealthy_threshold: u32,
#[serde(default = "default_healthy_threshold")]
pub healthy_threshold: u32,
pub path: Option<String>,
pub method: Option<String>, pub expected_status: Option<Vec<u16>>,
pub headers: Option<HashMap<String, String>>,
}