parent
538080a6f1
commit
d271f164fa
@ -0,0 +1,40 @@
|
|||||||
|
//! Capabilities (permissions) of a connection.
|
||||||
|
use super::*;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub mod fail;
|
||||||
|
pub use fail::Failures;
|
||||||
|
|
||||||
|
/// How lenient to be with a certain operation
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Copy)]
|
||||||
|
pub enum Leniency
|
||||||
|
{
|
||||||
|
/// Ignore **all** malformed/missed messages.
|
||||||
|
Ignore,
|
||||||
|
/// Allow `n` failures before disconnecting the socket.
|
||||||
|
Specific(Failures),
|
||||||
|
/// Allow
|
||||||
|
Normal,
|
||||||
|
/// Immediately disconnect the socket on **any** malformed/missed message.
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A capability (permission) for a raw socket's data transmission.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum RawSockCapability
|
||||||
|
{
|
||||||
|
/// Process
|
||||||
|
AllowUnsignedMessages,
|
||||||
|
|
||||||
|
/// Do not disconnect the socket when a malformed message is received, just ignore the message.
|
||||||
|
SoftFail,
|
||||||
|
|
||||||
|
/// Throttle the number of messages to process
|
||||||
|
RateLimit{ tx: usize, rx: usize },
|
||||||
|
|
||||||
|
/// The request response timeout for messages with an expected response.
|
||||||
|
RecvRespTimeout { tx: Duration, rx: Duration },
|
||||||
|
}
|
||||||
|
/// A collection of `RawSockCapability`s for a specific connection.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct RawSockCapabilities;
|
@ -0,0 +1,102 @@
|
|||||||
|
//! 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in new issue