1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
    event::{api::SocketAddress, IntoEvent, Timestamp},
    inet,
};

/// Outcome describes how the library should proceed on a connection attempt. The implementor will
/// use information from the ConnectionAttempt object to determine how the library should handle
/// the connection attempt
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Outcome {
    /// Allow the connection to continue
    ///
    /// Use `Outcome::allow()` to construct this variant
    #[non_exhaustive]
    Allow,

    /// Defer the connection by sending a Retry packet
    ///
    /// Use `Outcome::retry()` to construct this variant
    #[non_exhaustive]
    Retry,

    /// Silently drop the connection attempt
    ///
    /// Use `Outcome::drop()` to construct this variant
    #[non_exhaustive]
    Drop,

    /// Cleanly close the connection
    ///
    /// Use `Outcome::close()` to construct this variant
    #[non_exhaustive]
    Close,
}

impl Outcome {
    /// Allow the connection to continue
    pub fn allow() -> Self {
        Self::Allow
    }

    /// Defer the connection by sending a Retry packet
    pub fn retry() -> Self {
        Self::Retry
    }

    /// Silently drop the connection attempt
    pub fn drop() -> Self {
        Self::Drop
    }

    /// Cleanly close the connection
    pub fn close() -> Self {
        Self::Close
    }
}

/// A ConnectionAttempt holds information about the state of endpoint receiving a connect, along
/// with information about the connection. This can be used to make decisions about the Outcome of
/// an attempted connection
#[non_exhaustive]
#[derive(Debug)]
pub struct ConnectionAttempt<'a> {
    /// Number of handshakes the have begun but not completed
    pub inflight_handshakes: usize,

    /// Number of open connections
    pub connection_count: usize,

    /// The unverified address of the connecting peer
    /// This address comes from the datagram
    pub remote_address: SocketAddress<'a>,
    pub timestamp: Timestamp,
}

impl<'a> ConnectionAttempt<'a> {
    #[doc(hidden)]
    pub fn new(
        inflight_handshakes: usize,
        connection_count: usize,
        remote_address: &'a inet::SocketAddress,
        timestamp: Timestamp,
    ) -> Self {
        Self {
            inflight_handshakes,
            connection_count,
            remote_address: remote_address.into_event(),
            timestamp,
        }
    }
}

pub trait Limiter: 'static + Send {
    /// This trait is used to determine the outcome of connection attempts on an endpoint. The
    /// implementor returns an Outcome based on the ConnectionAttempt, or other information that the
    /// implementor may have.
    ///
    /// ```rust
    /// # mod s2n_quic { pub mod provider { pub mod endpoint_limits { pub use s2n_quic_core::endpoint::limits::*; } } }
    /// use s2n_quic::provider::endpoint_limits::{Limiter, ConnectionAttempt, Outcome};
    ///
    /// struct MyEndpointLimits {
    ///    handshake_limit: usize,
    ///    delay: core::time::Duration,
    /// }
    ///
    /// impl Limiter for MyEndpointLimits {
    ///    fn on_connection_attempt(&mut self, info: &ConnectionAttempt) -> Outcome {
    ///        if info.inflight_handshakes > self.handshake_limit {
    ///            Outcome::retry()
    ///        } else {
    ///            Outcome::allow()
    ///        }
    ///    }
    /// }
    /// ```
    fn on_connection_attempt(&mut self, info: &ConnectionAttempt) -> Outcome;
}