#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::sync::Arc;
use core::sync::atomic::{AtomicU16, AtomicU64, AtomicU8, Ordering};
use crate::colony::common::current_timestamp_ms;
use crate::policy::{GatePolicy, TransitStatus};
use crate::utils::BasisPoints;
use crate::Frame;
#[cfg(feature = "x509")]
use crate::crypto::x509::store::CertificateTrust;
#[cfg(feature = "x509")]
use crate::der::Encode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum CircuitState {
Closed = 0,
Open = 1,
HalfOpen = 2,
}
pub struct ClusterCircuitBreaker {
state: AtomicU8,
failures: AtomicU8,
opened_at: AtomicU64,
failure_threshold: u8,
cooldown_ms: u64,
}
impl ClusterCircuitBreaker {
pub fn new(failure_threshold: u8, cooldown_ms: u64) -> Self {
Self {
state: AtomicU8::new(CircuitState::Closed as u8),
failures: AtomicU8::new(0),
opened_at: AtomicU64::new(0),
failure_threshold,
cooldown_ms,
}
}
pub fn allow_request(&self) -> bool {
match self.state() {
CircuitState::Closed => true,
CircuitState::Open => {
let now = current_timestamp_ms();
let opened = self.opened_at.load(Ordering::Relaxed);
if now.saturating_sub(opened) >= self.cooldown_ms {
self.state.store(CircuitState::HalfOpen as u8, Ordering::Release);
true
} else {
false
}
}
CircuitState::HalfOpen => true, }
}
pub fn record_success(&self) {
self.failures.store(0, Ordering::Relaxed);
self.state.store(CircuitState::Closed as u8, Ordering::Release);
}
pub fn record_auth_failure(&self) {
let failures = self.failures.fetch_add(1, Ordering::AcqRel) + 1;
if failures >= self.failure_threshold {
self.state.store(CircuitState::Open as u8, Ordering::Release);
self.opened_at.store(current_timestamp_ms(), Ordering::Relaxed);
}
}
pub fn state(&self) -> CircuitState {
match self.state.load(Ordering::Acquire) {
0 => CircuitState::Closed,
1 => CircuitState::Open,
_ => CircuitState::HalfOpen,
}
}
pub fn is_open(&self) -> bool {
self.state() == CircuitState::Open
}
pub fn reset(&self) {
self.failures.store(0, Ordering::Relaxed);
self.state.store(CircuitState::Closed as u8, Ordering::Release);
}
}
#[cfg(feature = "x509")]
pub struct ClusterSecurityGate {
circuit_breaker: Arc<ClusterCircuitBreaker>,
trust_store: Arc<dyn CertificateTrust>,
}
#[cfg(feature = "x509")]
impl ClusterSecurityGate {
pub fn new(circuit_breaker: Arc<ClusterCircuitBreaker>, trust_store: Arc<dyn CertificateTrust>) -> Self {
Self { circuit_breaker, trust_store }
}
}
#[cfg(feature = "x509")]
impl GatePolicy for ClusterSecurityGate {
fn evaluate(&self, frame: &Frame) -> TransitStatus {
if !self.circuit_breaker.allow_request() {
return TransitStatus::Forbidden;
}
let signer_info = match frame.nonrepudiation.as_ref() {
Some(info) => info,
None => {
self.circuit_breaker.record_auth_failure();
return TransitStatus::Unauthorized;
}
};
if frame.integrity.is_none() {
self.circuit_breaker.record_auth_failure();
return TransitStatus::Unauthorized;
}
let cert = match self.trust_store.find_by_signer_info(signer_info) {
Some(c) => c,
None => {
self.circuit_breaker.record_auth_failure();
return TransitStatus::Forbidden;
}
};
let algorithm_oid = signer_info.signature_algorithm.oid;
let signature = signer_info.signature.as_bytes();
let public_key_der = match cert.tbs_certificate.subject_public_key_info.to_der() {
Ok(der) => der,
Err(_) => {
self.circuit_breaker.record_auth_failure();
return TransitStatus::Forbidden;
}
};
let message = match frame.to_tbs() {
Ok(tbs) => tbs,
Err(_) => {
self.circuit_breaker.record_auth_failure();
return TransitStatus::Forbidden;
}
};
match self
.trust_store
.to_policy_ref()
.verify_signature(&algorithm_oid, &public_key_der, &message, signature)
{
Ok(()) => {
self.circuit_breaker.record_success();
TransitStatus::Accepted
}
Err(_) => {
self.circuit_breaker.record_auth_failure();
TransitStatus::Forbidden
}
}
}
}
pub struct BackpressureGate {
utilization: Arc<AtomicU16>,
threshold: BasisPoints,
}
impl BackpressureGate {
pub fn new(utilization: Arc<AtomicU16>, threshold: BasisPoints) -> Self {
Self { utilization, threshold }
}
pub fn current_utilization(&self) -> BasisPoints {
BasisPoints::new_saturating(self.utilization.load(Ordering::Relaxed))
}
}
impl GatePolicy for BackpressureGate {
fn evaluate(&self, frame: &Frame) -> TransitStatus {
if frame.metadata.priority == Some(crate::MessagePriority::Heartbeat) {
return TransitStatus::Accepted;
}
let current = self.utilization.load(Ordering::Relaxed);
if current >= self.threshold.get() {
TransitStatus::Busy
} else {
TransitStatus::Accepted
}
}
}