openmoat_proxy/server/
capacity.rs1use std::time::Instant;
12
13use openmoat_core::{Decision, Verdict};
14
15use crate::audit::{Connection, Recorder};
16pub(super) use crate::coalesce::INTERVAL;
17use crate::coalesce::{Coalesce, counted};
18
19pub const RULE_CAPACITY: &str = "proxy-capacity";
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub(super) enum Full {
26 Slots,
28 Pending,
30}
31
32impl Full {
33 const ALL: [Self; 2] = [Self::Slots, Self::Pending];
34
35 pub(super) fn reply(self) -> &'static str {
37 match self {
38 Self::Slots => "too many connections",
39 Self::Pending => "too many connections waiting to authenticate",
40 }
41 }
42}
43
44#[derive(Debug, Default)]
46pub(super) struct Refusals {
47 counts: Coalesce<2>,
48}
49
50impl Refusals {
51 pub(super) fn refused(&self, full: Full, recorder: &dyn Recorder, now: Instant) {
54 if let Some(count) = self.counts.refused(full as usize, now) {
55 write(full, count, recorder);
56 }
57 }
58
59 pub(super) fn flush(&self, recorder: &dyn Recorder, now: Instant) {
63 for (kind, count) in self.counts.due(now) {
64 if let Some(full) = Full::ALL.get(kind) {
65 write(*full, count, recorder);
66 }
67 }
68 }
69}
70
71fn write(full: Full, count: u64, recorder: &dyn Recorder) {
72 let decision = Decision::single(
73 Verdict::Deny,
74 RULE_CAPACITY,
75 format!(
76 "{} refused before a request was read: {}; later ones within {} s are counted in \
77 the next row",
78 counted(count, "connection", "connections"),
79 full.reply(),
80 INTERVAL.as_secs()
81 ),
82 );
83 let connection = Connection {
84 session: None,
85 method: None,
86 destination: None,
87 terminated: false,
88 decision: &decision,
89 latency_us: 0,
90 };
91 if let Err(error) = recorder.record(&connection) {
94 recorder.unrecorded(&error);
95 }
96}
97
98#[cfg(test)]
99mod tests;