rama-net 0.3.0

rama network types and utilities
Documentation
use rama_core::extensions::Extensions;

#[derive(Debug, Clone)]
/// Matcher based on the ip part of the [`SocketAddr`] of the peer,
/// matching only if the ip is a loopback address.
///
/// [`SocketAddr`]: core::net::SocketAddr
pub struct LoopbackMatcher {
    optional: bool,
}

impl LoopbackMatcher {
    /// create a new loopback matcher to match on the ip part a [`SocketAddr`],
    /// matching only if the ip is a loopback address.
    ///
    /// This matcher will not match in case socket address could not be found,
    /// if you want to match in case socket address could not be found,
    /// use the [`LoopbackMatcher::optional`] constructor..
    ///
    /// [`SocketAddr`]: core::net::SocketAddr
    #[must_use]
    pub const fn new() -> Self {
        Self { optional: false }
    }

    /// create a new loopback matcher to match on the ip part a [`SocketAddr`],
    /// matching only if the ip is a loopback address or no socket address could be found.
    ///
    /// This matcher will match in case socket address could not be found.
    /// Use the [`LoopbackMatcher::new`] constructor if you want do not want
    /// to match in case socket address could not be found.
    ///
    /// [`SocketAddr`]: core::net::SocketAddr
    #[must_use]
    pub const fn optional() -> Self {
        Self { optional: true }
    }
}

impl Default for LoopbackMatcher {
    fn default() -> Self {
        Self::new()
    }
}

impl<Socket> rama_core::matcher::Matcher<Socket> for LoopbackMatcher
where
    Socket: crate::stream::Socket,
{
    fn matches(&self, _ext: Option<&Extensions>, stream: &Socket) -> bool {
        stream
            .peer_addr()
            .map(|addr| addr.ip_addr.is_loopback())
            .unwrap_or(self.optional)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::address::SocketAddress;

    use rama_core::matcher::Matcher;

    #[test]
    fn test_loopback_matcher_socket_trait() {
        let matcher = LoopbackMatcher::new();

        struct FakeSocket {
            local_addr: Option<SocketAddress>,
            peer_addr: Option<SocketAddress>,
        }

        impl crate::stream::Socket for FakeSocket {
            fn local_addr(&self) -> std::io::Result<SocketAddress> {
                match &self.local_addr {
                    Some(addr) => Ok(*addr),
                    None => Err(std::io::Error::from(std::io::ErrorKind::AddrNotAvailable)),
                }
            }

            fn peer_addr(&self) -> std::io::Result<SocketAddress> {
                match &self.peer_addr {
                    Some(addr) => Ok(*addr),
                    None => Err(std::io::Error::from(std::io::ErrorKind::AddrNotAvailable)),
                }
            }
        }

        let mut socket = FakeSocket {
            local_addr: None,
            peer_addr: None,
        };

        // test #1: no match: test with no socket info registered
        assert!(!matcher.matches(None, &socket));

        // test #2: no match: test with network address (ipv4)
        socket.peer_addr = Some(([192, 168, 0, 1], 8080).into());
        assert!(!matcher.matches(None, &socket));

        // test #3: no match: test with network address (ipv6)
        socket.peer_addr = Some(([1, 1, 1, 1, 1, 1, 1, 1], 8080).into());
        assert!(!matcher.matches(None, &socket));

        // test #4: match: test with loopback address (ipv4)
        socket.peer_addr = Some(([127, 0, 0, 1], 8080).into());
        assert!(matcher.matches(None, &socket));

        // test #5: match: test with another loopback address (ipv4)
        socket.peer_addr = Some(([127, 3, 2, 1], 8080).into());
        assert!(matcher.matches(None, &socket));

        // test #6: match: test with loopback address (ipv6)
        socket.peer_addr = Some(([0, 0, 0, 0, 0, 0, 0, 1], 8080).into());
        assert!(matcher.matches(None, &socket));

        // test #7: match: test with missing socket info, but it's seen as optional
        let matcher = LoopbackMatcher::optional();
        socket.peer_addr = None;
        assert!(matcher.matches(None, &socket));
    }
}