dope-tls 0.19.0

TCP TLS streams for dope runtime, shin-backed
Documentation
use core::{marker, mem};
use std::{cell, io};

use o3::collections::slab;
use shin::client::{self, workspace};
use shin::connection;
use shin::wire::handshake;

use crate::clock;
use crate::state::sessions;
use crate::tls;

#[doc(hidden)]
pub struct Storage<const ID: u8 = 0> {
    capacity: slab::Capacity,
    initialized: cell::OnceCell<Initialized<ID>>,
}

pub(crate) struct Initialized<const ID: u8> {
    pool: workspace::Pool<clock::Clock>,
    clock: clock::Clock,
    _route: marker::PhantomData<fn() -> Route<ID>>,
}

struct Route<const ID: u8>;

impl<const ID: u8> Storage<ID> {
    pub(crate) fn new(capacity: slab::Capacity) -> Self {
        Self {
            capacity,
            initialized: cell::OnceCell::new(),
        }
    }

    pub(crate) fn bind(&self, plan: tls::ClientPlan) -> io::Result<&Initialized<ID>> {
        if self.initialized.get().is_some() {
            return Err(already_bound());
        }
        let tls::Dial {
            prepared,
            identity,
            clock,
        } = plan.into_dial();
        let initialized = Initialized {
            pool: prepared.into_pool(identity, self.capacity),
            clock,
            _route: marker::PhantomData,
        };
        self.initialized
            .set(initialized)
            .map_err(|_| already_bound())?;
        self.initialized.get().ok_or_else(already_bound)
    }
}

fn already_bound() -> io::Error {
    io::Error::new(
        io::ErrorKind::InvalidInput,
        "TLS client session storage is already bound",
    )
}

#[doc(hidden)]
pub struct Pooled<'d, const ID: u8 = 0>(
    client::PooledConnection<'d, clock::Clock>,
    marker::PhantomData<fn() -> Route<ID>>,
);

impl<const ID: u8> sessions::Peer for Pooled<'_, ID> {
    fn send_pending_key_update_response_into<S: connection::EventSink + ?Sized>(
        &mut self,
        events: &mut S,
    ) -> Result<(), connection::DriveError<S::Error>> {
        self.0.key_updates().send_pending_into(events)
    }

    fn send_key_update_into<S: connection::EventSink + ?Sized>(
        &mut self,
        request: handshake::KeyUpdateRequest,
        events: &mut S,
    ) -> Result<(), connection::DriveError<S::Error>> {
        self.0.key_updates().send_into(request, events)
    }

    fn note_application_data(&mut self) {
        self.0.key_updates().note_application_data();
    }

    fn selected_alpn(&self) -> Option<&[u8]> {
        self.0.selected_alpn()
    }
}

impl<const ID: u8> sessions::ClientPeer for Pooled<'_, ID> {
    fn start_into<S: connection::EventSink + ?Sized>(
        &mut self,
        events: &mut S,
    ) -> Result<(), connection::DriveError<S::Error>> {
        self.0.start_into(events)
    }

    fn read_into<S: connection::EventSink + ?Sized>(
        &mut self,
        epoch: connection::Epoch,
        data: &[u8],
        events: &mut S,
    ) -> Result<(), connection::DriveError<S::Error>> {
        self.0.read_into(epoch, data, events)
    }
}

impl<'d, const ID: u8> Pooled<'d, ID> {
    pub(crate) fn connect(initialized: &'d Initialized<ID>) -> Option<Self> {
        initialized
            .pool
            .connect(initialized.clock)
            .map(|client| Self(client, marker::PhantomData))
    }
}

const _: () = assert!(mem::size_of::<Pooled<'static, 0>>() == 2 * mem::size_of::<usize>());