use std::time::Instant;
use openmoat_core::{Decision, Verdict};
use crate::audit::{Connection, Recorder};
pub(super) use crate::coalesce::INTERVAL;
use crate::coalesce::{Coalesce, counted};
pub const RULE_CAPACITY: &str = "proxy-capacity";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Full {
Slots,
Pending,
}
impl Full {
const ALL: [Self; 2] = [Self::Slots, Self::Pending];
pub(super) fn reply(self) -> &'static str {
match self {
Self::Slots => "too many connections",
Self::Pending => "too many connections waiting to authenticate",
}
}
}
#[derive(Debug, Default)]
pub(super) struct Refusals {
counts: Coalesce<2>,
}
impl Refusals {
pub(super) fn refused(&self, full: Full, recorder: &dyn Recorder, now: Instant) {
if let Some(count) = self.counts.refused(full as usize, now) {
write(full, count, recorder);
}
}
pub(super) fn flush(&self, recorder: &dyn Recorder, now: Instant) {
for (kind, count) in self.counts.due(now) {
if let Some(full) = Full::ALL.get(kind) {
write(*full, count, recorder);
}
}
}
}
fn write(full: Full, count: u64, recorder: &dyn Recorder) {
let decision = Decision::single(
Verdict::Deny,
RULE_CAPACITY,
format!(
"{} refused before a request was read: {}; later ones within {} s are counted in \
the next row",
counted(count, "connection", "connections"),
full.reply(),
INTERVAL.as_secs()
),
);
let connection = Connection {
session: None,
method: None,
destination: None,
terminated: false,
decision: &decision,
latency_us: 0,
};
if let Err(error) = recorder.record(&connection) {
recorder.unrecorded(&error);
}
}
#[cfg(test)]
mod tests;