use std::sync::Arc;
use arc_swap::ArcSwap;
use serde::{Deserialize, Serialize};
pub trait S3ConfigProvider: Send + Sync + 'static {
fn snapshot(&self) -> Arc<S3Config>;
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default)]
#[non_exhaustive]
pub struct S3Config {
pub xml_max_body_size: usize,
pub post_object_max_file_size: u64,
pub form_max_field_size: usize,
pub form_max_fields_size: usize,
pub form_max_parts: usize,
pub presigned_url_max_skew_time_secs: u32,
}
impl Default for S3Config {
fn default() -> Self {
Self {
xml_max_body_size: 20 * 1024 * 1024, post_object_max_file_size: 5 * 1024 * 1024 * 1024, form_max_field_size: 1024 * 1024, form_max_fields_size: 20 * 1024 * 1024, form_max_parts: 1000,
presigned_url_max_skew_time_secs: 900, }
}
}
#[derive(Debug)]
pub struct StaticConfigProvider {
inner: Arc<S3Config>,
}
impl StaticConfigProvider {
#[must_use]
pub fn new(config: Arc<S3Config>) -> Self {
Self { inner: config }
}
}
impl Default for StaticConfigProvider {
fn default() -> Self {
Self::new(Arc::new(S3Config::default()))
}
}
impl S3ConfigProvider for StaticConfigProvider {
fn snapshot(&self) -> Arc<S3Config> {
Arc::clone(&self.inner)
}
}
#[derive(Debug)]
pub struct HotReloadConfigProvider {
inner: ArcSwap<S3Config>,
}
impl HotReloadConfigProvider {
#[must_use]
pub fn new(config: Arc<S3Config>) -> Self {
Self {
inner: ArcSwap::from(config),
}
}
pub fn update(&self, config: Arc<S3Config>) {
self.inner.store(config);
}
}
impl Default for HotReloadConfigProvider {
fn default() -> Self {
Self::new(Arc::new(S3Config::default()))
}
}
impl S3ConfigProvider for HotReloadConfigProvider {
fn snapshot(&self) -> Arc<S3Config> {
self.inner.load_full()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = S3Config::default();
assert_eq!(config.xml_max_body_size, 20 * 1024 * 1024);
assert_eq!(config.post_object_max_file_size, 5 * 1024 * 1024 * 1024);
assert_eq!(config.form_max_field_size, 1024 * 1024);
assert_eq!(config.form_max_fields_size, 20 * 1024 * 1024);
assert_eq!(config.form_max_parts, 1000);
assert_eq!(config.presigned_url_max_skew_time_secs, 900);
}
#[test]
fn test_static_config_provider() {
let provider = StaticConfigProvider::new(Arc::new(S3Config::default()));
assert_eq!(provider.snapshot().xml_max_body_size, 20 * 1024 * 1024);
let snapshot1 = provider.snapshot();
let snapshot2 = provider.snapshot();
assert!(Arc::ptr_eq(&snapshot1, &snapshot2));
}
#[test]
fn test_hot_reload_config_provider() {
let provider = HotReloadConfigProvider::new(Arc::new(S3Config::default()));
assert_eq!(provider.snapshot().xml_max_body_size, 20 * 1024 * 1024);
provider.update(Arc::new(S3Config {
xml_max_body_size: 5 * 1024 * 1024,
..Default::default()
}));
assert_eq!(provider.snapshot().xml_max_body_size, 5 * 1024 * 1024);
}
#[test]
fn test_hot_reload_snapshot_immutable() {
let provider = HotReloadConfigProvider::new(Arc::new(S3Config::default()));
let snapshot = provider.snapshot();
provider.update(Arc::new(S3Config {
xml_max_body_size: 5 * 1024 * 1024,
..Default::default()
}));
assert_eq!(snapshot.xml_max_body_size, 20 * 1024 * 1024);
assert_eq!(provider.snapshot().xml_max_body_size, 5 * 1024 * 1024);
}
#[test]
fn test_hot_reload_config_provider_arc() {
let provider = Arc::new(HotReloadConfigProvider::new(Arc::new(S3Config::default())));
let cloned = provider.clone();
assert_eq!(provider.snapshot().xml_max_body_size, 20 * 1024 * 1024);
assert_eq!(cloned.snapshot().xml_max_body_size, 20 * 1024 * 1024);
provider.update(Arc::new(S3Config {
xml_max_body_size: 5 * 1024 * 1024,
..Default::default()
}));
assert_eq!(provider.snapshot().xml_max_body_size, 5 * 1024 * 1024);
assert_eq!(cloned.snapshot().xml_max_body_size, 5 * 1024 * 1024);
}
#[test]
fn test_config_provider_trait() {
let provider: Arc<dyn S3ConfigProvider> = Arc::new(HotReloadConfigProvider::default());
let snapshot = provider.snapshot();
assert_eq!(snapshot.xml_max_body_size, 20 * 1024 * 1024);
assert_eq!(snapshot.post_object_max_file_size, 5 * 1024 * 1024 * 1024);
}
#[test]
fn test_static_config_provider_trait() {
let provider: Arc<dyn S3ConfigProvider> = Arc::new(StaticConfigProvider::default());
let snapshot = provider.snapshot();
assert_eq!(snapshot.xml_max_body_size, 20 * 1024 * 1024);
assert_eq!(snapshot.post_object_max_file_size, 5 * 1024 * 1024 * 1024);
}
#[test]
fn test_serde_roundtrip() {
let config = S3Config {
xml_max_body_size: 10 * 1024 * 1024,
post_object_max_file_size: 1024 * 1024 * 1024,
form_max_field_size: 512 * 1024,
form_max_fields_size: 5 * 1024 * 1024,
form_max_parts: 500,
presigned_url_max_skew_time_secs: 600,
};
let json = serde_json::to_string(&config).expect("serialize failed");
let deserialized: S3Config = serde_json::from_str(&json).expect("deserialize failed");
assert_eq!(config, deserialized);
}
#[test]
fn test_serde_default_values() {
let json = r#"{"xml_max_body_size": 1024}"#;
let config: S3Config = serde_json::from_str(json).expect("deserialize failed");
assert_eq!(config.xml_max_body_size, 1024);
assert_eq!(config.post_object_max_file_size, 5 * 1024 * 1024 * 1024);
assert_eq!(config.form_max_field_size, 1024 * 1024);
}
#[test]
fn test_hot_reload_in_service_layer() {
let provider = Arc::new(HotReloadConfigProvider::new(Arc::new(S3Config::default())));
let snapshot = provider.snapshot();
assert_eq!(snapshot.xml_max_body_size, 20 * 1024 * 1024);
provider.update(Arc::new(S3Config {
xml_max_body_size: 30 * 1024 * 1024,
..Default::default()
}));
let new_snapshot = provider.snapshot();
assert_eq!(new_snapshot.xml_max_body_size, 30 * 1024 * 1024);
}
}