fastly 0.12.0

Fastly Compute API
Documentation
//! Low-level Compute ACL interface.
//!
//! The corresponding [`crate::acl`] wraps this module and provides
//! a higher-level interface to Compute ACLs.

use crate::abi;
use crate::handle::BodyHandle;
use fastly_shared::FastlyStatus;

use std::net::IpAddr;

/// A handle to an ACL.
pub(crate) struct AclHandle {
    handle: u32,
}

impl AclHandle {
    /// An invalid Acl handle.
    #[cfg_attr(
        not(target_env = "p1"),
        deprecated(
            since = "0.11.6",
            note = "This code will need to be updated for wasip2."
        )
    )]
    const INVALID: Self = Self {
        handle: fastly_shared::INVALID_ACL_HANDLE,
    };

    /// Open an ACL linked to the current service with the given link name.
    pub fn open(acl_name: &str) -> Result<Self, OpenError> {
        let mut handle = Self::INVALID;
        unsafe { abi::fastly_acl::open(acl_name.as_ptr(), acl_name.len(), handle.as_u32_mut()) }
            .result()
            .map(|_| handle)
            .map_err(|fastly_status| fastly_status.into())
    }

    /// Perform a lookup of the given IP address in the ACL.
    /// If no matches are found, then `Ok(None)` is returned.
    pub fn lookup(&self, ip: IpAddr) -> Result<Option<BodyHandle>, LookupError> {
        if self.is_invalid() {
            return Err(LookupError::InvalidHandle);
        }

        let ipv4_bytes;
        let ipv6_bytes;
        let ip_bytes = match ip {
            IpAddr::V4(v4) => {
                ipv4_bytes = v4.octets();
                &ipv4_bytes[..]
            }
            IpAddr::V6(v6) => {
                ipv6_bytes = v6.octets();
                &ipv6_bytes[..]
            }
        };
        let mut body_handle_out = fastly_shared::INVALID_BODY_HANDLE;
        let mut acl_error_out = abi::fastly_acl::AclError::Uninitialized;

        unsafe {
            // There are two errors that need to be handled in the following call to `lookup`:
            //  1: The `lookup` call returns a FastlyStatus. In general, this will fail
            //     when there's an error with the hostcall itself.
            //  2: The `lookup` call populates the given `AclError`. These errors are
            //     more specific ACL errors.
            abi::fastly_acl::lookup(
                self.as_u32(),
                ip_bytes.as_ptr(),
                ip_bytes.len(),
                &mut body_handle_out,
                &mut acl_error_out,
            )
        }
        .result()
        .map_err(|fastly_status| -> LookupError { fastly_status.into() })?;

        match acl_error_out {
            abi::fastly_acl::AclError::Ok => {
                if body_handle_out == fastly_shared::INVALID_BODY_HANDLE {
                    return Err(LookupError::InvalidResponseBody);
                }
                let body_handle = unsafe { BodyHandle::from_u32(body_handle_out) };
                Ok(Some(body_handle))
            }

            abi::fastly_acl::AclError::NoContent => Ok(None),

            other => Err(other.into()),
        }
    }

    /// Get the underlying representation of the handle.
    ///
    /// This should only be used when calling the raw ABI directly, and care should be taken not to
    /// reuse or alias handle values.
    fn as_u32(&self) -> u32 {
        return self.handle;
    }

    /// Get a mutable reference to the underlying `u32` representation of the handle.
    ///
    /// This should only be used when calling the raw ABI directly, and care should be taken not to
    /// reuse or alias handle values.
    fn as_u32_mut(&mut self) -> &mut u32 {
        &mut self.handle
    }

    /// Return true if the handle is invalid.
    #[cfg_attr(
        not(target_env = "p1"),
        deprecated(
            since = "0.11.6",
            note = "This code will need to be updated for wasip2."
        )
    )]
    pub fn is_invalid(&self) -> bool {
        self.handle == Self::INVALID.handle
    }
}

/// Errors thrown when calling open.
#[derive(Debug, thiserror::Error)]
pub enum OpenError {
    /// No acl exists with the given name.
    #[error("acl not found")]
    AclNotFound,

    /// An unexpected error occurred.
    #[error("unexpected error: {0:?}")]
    Unexpected(FastlyStatus),
}

impl From<FastlyStatus> for OpenError {
    fn from(fastly_status: FastlyStatus) -> Self {
        match fastly_status {
            FastlyStatus::NONE => Self::AclNotFound,
            unexpected => Self::Unexpected(unexpected),
        }
    }
}

/// Errors thrown on lookup failure.
#[derive(Debug, thiserror::Error)]
pub enum LookupError {
    /// The handle is invalid.
    #[error("invalid acl handle")]
    InvalidHandle,
    /// The response body is invalid.
    #[error("response body is invalid")]
    InvalidResponseBody,
    /// An argument was incorrect or invalid.
    #[error("a provided argument was invalid")]
    InvalidArgument,
    /// Too many requests were made.
    #[error("too many requests")]
    TooManyRequests,
    /// An unexpected FastlyStatus error occurred.
    #[error("unexpected error: {0:?}")]
    FastlyStatus(FastlyStatus),
    /// An unexpected ACL error occurred.
    #[error("unexpected error: {0:?}")]
    AclError(abi::fastly_acl::AclError),
}

impl From<FastlyStatus> for LookupError {
    fn from(fastly_status: FastlyStatus) -> Self {
        match fastly_status {
            FastlyStatus::BADF => Self::InvalidHandle,
            FastlyStatus::INVAL => Self::InvalidArgument,
            unexpected => Self::FastlyStatus(unexpected),
        }
    }
}

impl From<fastly_sys::fastly_acl::AclError> for LookupError {
    fn from(acl_error: fastly_sys::fastly_acl::AclError) -> Self {
        match acl_error {
            fastly_sys::fastly_acl::AclError::TooManyRequests => Self::TooManyRequests,
            other => Self::AclError(other),
        }
    }
}