You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rsh/src/cap/fail.rs

103 lines
2.7 KiB

//! Failure counting/capping
use super::*;
use std::{
fmt, error,
};
/// A measure of failures, used to track or to check failures.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Copy)]
pub struct Failures
{
/// Number of failures happened back-to-back.
pub seq: usize,
/// Total number of failures over the socket's lifetime.
///
/// Set allowed to `0` for unlimited.
pub total: usize,
/// Window of time to keep failures.
pub window: Duration,
/// Number of failures happened in the last `window` of time.
pub last_window: usize,
}
/// When a failure cap is exceeded, which one is exceeded; and what is the limit that is exceeded?
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FailureCapExceeded
{
/// Too many sequential errors
Sequential(usize),
/// Too many total errors
Total(usize),
/// Too many errors in the refresh window
Windowed(usize, Duration),
}
impl error::Error for FailureCapExceeded{}
impl fmt::Display for FailureCapExceeded
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Sequential(_) => write!(f, "too many sequential errors"),
Self::Total(_) => write!(f, "too many total errors"),
Self::Windowed(_, _) => write!(f, "too many errors in refresh window"),
}
}
}
impl FailureCapExceeded
{
/// Convert into a detailed report. (This shouldn't be shared with peer, probably.)
pub fn into_detailed_report(self) -> eyre::Report
{
let rep = eyre::Report::from(&self);
match self {
Self::Sequential(cap) => rep.with_section(|| cap.header("Exceeded limit")),
Self::Total(cap) => rep.with_section(|| cap.header("Exceeded limit")),
Self::Windowed(cap, w) => rep.with_section(|| cap.header("Exceeded limit"))
.with_section(|| format!("{:?}", w).header("Refresh window was"))
}
}
}
impl Failures
{
/// Default allowed failure parameters.
pub const DEFAULT_ALLOWED: Self = Self {
seq: 10,
total: 65536,
window: Duration::from_secs(10),
last_window: 5,
};
/// Has this `Failures` exceeded failure cap `other`?
pub fn cap_check(&self, other: &Self) -> Result<(), FailureCapExceeded>
{
macro_rules! chk {
($name:ident, $err:expr) => {
if other.$name != 0 && (self.$name >= other.$name) {
return Err($err)?;
}
}
}
chk!(seq, FailureCapExceeded::Sequential(other.seq));
chk!(total, FailureCapExceeded::Total(other.total));
//debug_assert!(other.window == self.window); //TODO: Should we disallow this?
chk!(last_window, FailureCapExceeded::Windowed(other.last_window, self.window));
Ok(())
}
}
impl Default for Failures
{
#[inline]
fn default() -> Self
{
Self::DEFAULT_ALLOWED
}
}