pub mod chain;
pub mod listener;
pub mod middleware;
pub mod tuning;
pub mod upstream;
use std::path::Path;
use crate::config_format::default_config_format_registry;
use crate::error::ProximaError;
pub use chain::{Chain, Composable};
pub use listener::HttpListener;
#[cfg(unix)]
pub use listener::HttpUdsListener;
#[cfg(feature = "tls")]
pub use listener::HttpsListener;
pub use middleware::{BearerAuth, ClientAuth, DigestAuth, OauthAuth, RateLimit, SigV4Auth};
pub use upstream::HttpUpstream;
use std::collections::BTreeMap;
use bon::Builder;
use conflaguration::{Settings, Validate, ValidationMessage};
use serde::{Deserialize, Serialize};
pub use tuning::{BufferPoolTuning, HttpTuning, ZstdTuning};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RegistryEntry {
pub r#type: String,
#[serde(flatten)]
pub spec: serde_json::Value,
}
#[derive(Debug, Clone, Default, Builder, Deserialize, Serialize, Settings)]
#[settings(prefix = "PROXIMA")]
#[builder(derive(Clone, Debug))]
pub struct ProximaSettings {
#[setting(skip)]
#[serde(default)]
#[builder(default)]
pub listeners: BTreeMap<String, RegistryEntry>,
#[setting(skip)]
#[serde(default)]
#[builder(default)]
pub upstreams: BTreeMap<String, RegistryEntry>,
#[setting(skip)]
#[serde(default)]
#[builder(default)]
pub middlewares: BTreeMap<String, RegistryEntry>,
#[setting(skip)]
#[serde(default)]
#[builder(default)]
pub pipes: BTreeMap<String, RegistryEntry>,
#[setting(skip)]
#[serde(default)]
#[builder(default)]
pub producers: BTreeMap<String, RegistryEntry>,
#[setting(nested)]
#[serde(default)]
#[builder(default)]
pub http: HttpTuning,
#[setting(nested)]
#[serde(default)]
#[builder(default)]
pub zstd: ZstdTuning,
#[setting(nested)]
#[serde(default)]
#[builder(default)]
pub buffer_pool: BufferPoolTuning,
}
impl ProximaSettings {
pub fn from_path(path: impl AsRef<Path>) -> Result<Self, ProximaError> {
let path = path.as_ref();
let raw = std::fs::read_to_string(path).map_err(ProximaError::Io)?;
let hint = path
.extension()
.and_then(|ext| ext.to_str())
.map(str::to_string);
let registry = default_config_format_registry()?;
let value = registry.parse_with_hint(&raw, hint.as_deref())?;
serde_json::from_value(value)
.map_err(|err| ProximaError::Config(format!("decode ProximaSettings: {err}")))
}
}
impl Validate for ProximaSettings {
fn validate(&self) -> conflaguration::Result<()> {
let mut errors = Vec::new();
self.http
.validate()
.map_err(|err| collect_validation_errors(&mut errors, "http", err))
.ok();
self.zstd
.validate()
.map_err(|err| collect_validation_errors(&mut errors, "zstd", err))
.ok();
self.buffer_pool
.validate()
.map_err(|err| collect_validation_errors(&mut errors, "buffer_pool", err))
.ok();
for (name, entry) in &self.listeners {
if entry.r#type.is_empty() {
errors.push(ValidationMessage::new(
format!("listener.{name}.type"),
"must not be empty",
));
}
}
for (name, entry) in &self.upstreams {
if entry.r#type.is_empty() {
errors.push(ValidationMessage::new(
format!("upstream.{name}.type"),
"must not be empty",
));
}
}
for (name, entry) in &self.middlewares {
if entry.r#type.is_empty() {
errors.push(ValidationMessage::new(
format!("middleware.{name}.type"),
"must not be empty",
));
}
}
if errors.is_empty() {
Ok(())
} else {
Err(conflaguration::Error::Validation { errors })
}
}
}
fn collect_validation_errors(
target: &mut Vec<ValidationMessage>,
section: &str,
error: conflaguration::Error,
) {
if let conflaguration::Error::Validation { errors } = error {
for mut message in errors {
message.prepend_path(section);
target.push(message);
}
}
}