wtx 0.44.3

A collection of different transport implementations and related tools focused primarily on web technologies.
Documentation
use crate::{
  calendar::{DateTime, Utc},
  collection::Vector,
  http::{
    SessionError, SessionManager, SessionStore,
    cookie::{SameSite, cookie_generic::CookieGeneric},
    session::SessionManagerInner,
  },
  misc::{Secret, SecretContext, sleep},
  rng::CryptoRng,
  sync::{Arc, AsyncMutex},
};
use alloc::string::String;
use core::{marker::PhantomData, time::Duration};

/// Default and optional parameters for the construction of a [`SessionManager`].
#[derive(Debug)]
pub struct SessionManagerBuilder {
  pub(crate) cookie_def: CookieGeneric<String, Vector<u8>>,
  pub(crate) inspection_interval: Duration,
}

impl SessionManagerBuilder {
  pub(crate) fn new() -> Self {
    Self {
      cookie_def: CookieGeneric {
        domain: "".into(),
        expires: None,
        http_only: true,
        max_age: None,
        name: "id".try_into().unwrap_or_default(),
        path: "/".into(),
        same_site: Some(SameSite::Strict),
        secure: true,
        value: Vector::new(),
      },
      inspection_interval: Duration::from_secs(60 * 30),
    }
  }

  /// Creates a new [`SessionManager`] with random generated keys. It is up to the caller to
  /// provide a good RNG.
  ///
  /// The returned [`Future`] is responsible for deleting expired sessions at an interval defined by
  /// [`Self::inspection_interval`] and should be called in a separated task.
  ///
  /// If the backing store already has a system that automatically removes outdated sessions like
  /// SQL triggers, then the [`Future`] can be ignored.
  #[inline]
  pub fn build_generating_key<CS, E, RNG, SS>(
    self,
    rng: &mut RNG,
    secret_context: SecretContext,
    session_store: SS,
  ) -> crate::Result<(
    impl Future<Output = Result<(), E>> + use<CS, E, RNG, SS>,
    SessionManager<CS, E>,
  )>
  where
    E: From<crate::Error>,
    RNG: CryptoRng,
    SS: Clone + SessionStore<CS, E>,
  {
    let mut session_secret = [0u8; 16];
    rng.fill_slice(&mut session_secret);
    Self::build_with_key(self, rng, secret_context, &mut session_secret, session_store)
  }

  /// Creates a new [`SessionManager`] with the provided key. It is up to the caller to
  /// provide a 16 bytes cryptographically secure secret.
  ///
  /// The returned [`Future`] is responsible for deleting expired sessions at an interval defined by
  /// [`Self::inspection_interval`] and should be called in a separated task.
  ///
  /// If the backing store already has a system that automatically removes outdated sessions like
  /// SQL triggers, then the [`Future`] can be ignored.
  #[inline]
  pub fn build_with_key<CS, E, RNG, SS>(
    self,
    rng: &mut RNG,
    secret_context: SecretContext,
    session_secret: &mut [u8],
    mut session_store: SS,
  ) -> crate::Result<(
    impl Future<Output = Result<(), E>> + use<CS, E, RNG, SS>,
    SessionManager<CS, E>,
  )>
  where
    E: From<crate::Error>,
    RNG: CryptoRng,
    SS: SessionStore<CS, E>,
  {
    if session_secret.len() != 16 {
      return Err(SessionError::InvalidSecretLength.into());
    }
    let Self { cookie_def, inspection_interval } = self;
    Ok((
      async move {
        loop {
          session_store.delete_expired().await?;
          sleep(inspection_interval).await?;
        }
      },
      SessionManager {
        inner: Arc::new((
          cookie_def.name,
          AsyncMutex::new(SessionManagerInner {
            cookie_def,
            phantom: PhantomData,
            session_secret: Secret::new(session_secret, rng, secret_context)?,
          }),
        )),
      },
    ))
  }

  /// Defines the host to which the cookie will be sent.
  #[inline]
  pub fn domain(mut self, elem: String) -> Self {
    self.cookie_def.domain = elem;
    self
  }

  /// Indicates the maximum lifetime of the cookie as an HTTP-date timestamp.
  ///
  /// If [Self::max_age] is set, then this parameter is ignored when setting the cookie in the
  /// header.
  #[inline]
  pub const fn expires(mut self, elem: Option<DateTime<Utc>>) -> Self {
    self.cookie_def.expires = elem;
    self
  }

  /// Forbids JavaScript from accessing the cookie.
  #[inline]
  pub const fn http_only(mut self, elem: bool) -> Self {
    self.cookie_def.http_only = elem;
    self
  }

  /// The amount of time the future returned by the building methods will wait before
  /// deleting expired sessions.
  #[inline]
  pub const fn inspection_interval(mut self, elem: Duration) -> Self {
    self.inspection_interval = elem;
    self
  }

  /// Indicates the number of seconds until the cookie expires.
  #[inline]
  pub const fn max_age(mut self, elem: Option<Duration>) -> Self {
    self.cookie_def.max_age = elem;
    self
  }

  /// Cookie name.
  #[inline]
  pub fn name(mut self, elem: &str) -> crate::Result<Self> {
    self.cookie_def.name = elem.try_into()?;
    Ok(self)
  }

  /// Indicates the path that must exist in the requested URL for the browser to send the Cookie
  /// header.
  #[inline]
  pub fn path(mut self, elem: String) -> Self {
    self.cookie_def.path = elem;
    self
  }

  /// Controls whether or not a cookie is sent with cross-site requests.
  #[inline]
  pub const fn same_site(mut self, elem: Option<SameSite>) -> Self {
    self.cookie_def.same_site = elem;
    self
  }

  /// Indicates that the cookie is sent to the server only when a request is made with the `https`
  /// scheme.
  #[inline]
  pub const fn secure(mut self, elem: bool) -> Self {
    self.cookie_def.secure = elem;
    self
  }
}