liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
use super::{
    Crossing, DialedLink, LINK_CROSSING_REFUSED_CODE, PendingDelivery, ServerError, Wake, failure,
};
use crate::link_carrier::{CarrierError, CarrierReceive, LinkCarrier};
use crate::link_handshake::{NOISE_MAX_MESSAGE, NOISE_OVERHEAD};
use liminal::protocol::{CausalContext, MessageEnvelope};

pub(super) fn cross(
    link: &mut DialedLink,
    source: &mut PendingDelivery,
    wake: &Wake,
) -> Result<Crossing, ServerError> {
    let envelope = MessageEnvelope::new(
        source.schema,
        CausalContext::independent(),
        std::mem::take(&mut source.payload),
    );
    let position = source
        .position
        .ok_or_else(|| failure("exported message has no stable subscription position"))?;
    let Some(sealed) = seal(position, &envelope, |plain, sealed| {
        link.transport
            .prepare(plain, sealed)
            .map_err(|error| failure(error.to_string()))
    })?
    else {
        return Ok(Crossing::TooLarge);
    };
    exchange(link, &source.subject, &sealed, wake)
}

pub(super) fn seal(
    position: u64,
    envelope: &MessageEnvelope,
    prepare: impl FnOnce(&[u8], &mut [u8]) -> Result<usize, ServerError>,
) -> Result<Option<Vec<u8>>, ServerError> {
    let mut plain = position.to_be_bytes().to_vec();
    plain.extend(
        envelope
            .to_wire_bytes()
            .map_err(|error| failure(error.to_string()))?,
    );
    if plain.len().saturating_add(NOISE_OVERHEAD) > NOISE_MAX_MESSAGE {
        return Ok(None);
    }
    let mut sealed = vec![0; plain.len() + NOISE_OVERHEAD];
    let written = prepare(&plain, &mut sealed)?;
    sealed.truncate(written);
    Ok(Some(sealed))
}

pub(super) fn exchange(
    link: &mut DialedLink,
    subject: &str,
    sealed: &[u8],
    wake: &Wake,
) -> Result<Crossing, ServerError> {
    if let Err(error) = link.carrier.send(subject, sealed) {
        return interrupted(&error, wake, link.carrier.life());
    }
    let answer = match link.carrier.receive() {
        Ok(answer) => answer,
        Err(error) => return interrupted(&error, wake, link.carrier.life()),
    };
    match answer {
        CarrierReceive::Acknowledged { .. } => Ok(Crossing::Acknowledged),
        CarrierReceive::Refused {
            reason_code: LINK_CROSSING_REFUSED_CODE,
            reason,
        } => Ok(Crossing::Refused(reason)),
        CarrierReceive::End { reason } if wake.stopping() || reason == "peer_gone" => {
            wake.check()?;
            tracing::info!(%reason, "bridge carrier interrupted; subscription position retained");
            Ok(Crossing::Stopped)
        }
        CarrierReceive::End { reason } => Err(failure(format!(
            "link '{}' ended before its crossing answer: {reason}",
            link.link_id
        ))),
        CarrierReceive::Refused {
            reason_code,
            reason,
        } => Err(failure(format!(
            "link '{}' publish error {reason_code}: {reason:?}",
            link.link_id
        ))),
        CarrierReceive::Sealed { .. } => Err(failure(
            "export carrier received sealed traffic instead of a crossing answer",
        )),
    }
}

fn interrupted(
    error: &CarrierError,
    wake: &Wake,
    life: Option<&crate::link::LinkLife>,
) -> Result<Crossing, ServerError> {
    let peer_gone = life
        .map(crate::link::LinkLife::reason)
        .transpose()
        .map_err(failure)?
        .flatten()
        .as_deref()
        == Some("peer_gone");
    if (matches!(error, CarrierError::Io(_) | CarrierError::Ended) && wake.stopping())
        || (matches!(
            error,
            CarrierError::Io(_) | CarrierError::Ended | CarrierError::Lifecycle(_)
        ) && peer_gone)
    {
        wake.check()?;
        tracing::info!(%error, "bridge carrier interrupted; subscription position retained");
        Ok(Crossing::Stopped)
    } else {
        Err(failure(error.to_string()))
    }
}