atagen

macltok

public
Star0
Watch0
3e5a785a7273c52abdc58fb07ceb86b59eb59727
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: src/engine/session.rs

f12377bce9b9atagen2026-09-26 12:32 UTC1
use std::sync::Arc;
f12377bce9b9atagen2026-09-26 12:32 UTC2
f12377bce9b9atagen2026-09-26 12:32 UTC3
use matrix_sdk::{BoxFuture, Client, SessionChange};
f12377bce9b9atagen2026-09-26 12:32 UTC4
use ruma::OwnedDeviceId;
f12377bce9b9atagen2026-09-26 12:32 UTC5
use tokio::sync::{
f12377bce9b9atagen2026-09-26 12:32 UTC6
    broadcast::{self, error::RecvError},
f12377bce9b9atagen2026-09-26 12:32 UTC7
    mpsc, watch,
f12377bce9b9atagen2026-09-26 12:32 UTC8
};
f12377bce9b9atagen2026-09-26 12:32 UTC9
f12377bce9b9atagen2026-09-26 12:32 UTC10
use super::Logouts;
f12377bce9b9atagen2026-09-26 12:32 UTC11
use crate::{Error, Event, Termination, transport::EventSender};
f12377bce9b9atagen2026-09-26 12:32 UTC12
f12377bce9b9atagen2026-09-26 12:32 UTC13
#[cfg(not(target_family = "wasm"))]
f12377bce9b9atagen2026-09-26 12:32 UTC14
pub(crate) type SaveRecord = dyn Fn(Client) -> BoxFuture<'static, Result<(), Error>> + Send + Sync;
f12377bce9b9atagen2026-09-26 12:32 UTC15
#[cfg(target_family = "wasm")]
f12377bce9b9atagen2026-09-26 12:32 UTC16
pub(crate) type SaveRecord = dyn Fn(Client) -> BoxFuture<'static, Result<(), Error>>;
f12377bce9b9atagen2026-09-26 12:32 UTC17
f12377bce9b9atagen2026-09-26 12:32 UTC18
/// how the engine keeps the saved record current, handed over by `Session::build`
f12377bce9b9atagen2026-09-26 12:32 UTC19
pub(crate) struct Persistence {
f12377bce9b9atagen2026-09-26 12:32 UTC20
    pub(crate) save: Box<SaveRecord>,
f12377bce9b9atagen2026-09-26 12:32 UTC21
    /// what the sdk's own save callback made of each refresh, when the store could write
f12377bce9b9atagen2026-09-26 12:32 UTC22
    /// there and then
f12377bce9b9atagen2026-09-26 12:32 UTC23
    pub(crate) inline: mpsc::UnboundedReceiver<Result<(), Error>>,
f12377bce9b9atagen2026-09-26 12:32 UTC24
}
f12377bce9b9atagen2026-09-26 12:32 UTC25
f12377bce9b9atagen2026-09-26 12:32 UTC26
impl Persistence {
f12377bce9b9atagen2026-09-26 12:32 UTC27
    async fn persist(&mut self, client: &Client) -> Result<(), Error> {
f12377bce9b9atagen2026-09-26 12:32 UTC28
        let mut inline = None;
f12377bce9b9atagen2026-09-26 12:32 UTC29
        while let Ok(result) = self.inline.try_recv() {
f12377bce9b9atagen2026-09-26 12:32 UTC30
            inline = Some(result);
f12377bce9b9atagen2026-09-26 12:32 UTC31
        }
f12377bce9b9atagen2026-09-26 12:32 UTC32
        if let Some(Ok(())) = inline {
f12377bce9b9atagen2026-09-26 12:32 UTC33
            return Ok(());
f12377bce9b9atagen2026-09-26 12:32 UTC34
        }
f12377bce9b9atagen2026-09-26 12:32 UTC35
        // an inline save for a newer refresh can land while this one writes, so go again
f12377bce9b9atagen2026-09-26 12:32 UTC36
        // until what's on disk is what the client holds
f12377bce9b9atagen2026-09-26 12:32 UTC37
        loop {
f12377bce9b9atagen2026-09-26 12:32 UTC38
            let tokens = client.session_tokens();
f12377bce9b9atagen2026-09-26 12:32 UTC39
            (self.save)(client.clone()).await?;
f12377bce9b9atagen2026-09-26 12:32 UTC40
            if client.session_tokens() == tokens {
f12377bce9b9atagen2026-09-26 12:32 UTC41
                return Ok(());
f12377bce9b9atagen2026-09-26 12:32 UTC42
            }
f12377bce9b9atagen2026-09-26 12:32 UTC43
        }
f12377bce9b9atagen2026-09-26 12:32 UTC44
    }
f12377bce9b9atagen2026-09-26 12:32 UTC45
}
f12377bce9b9atagen2026-09-26 12:32 UTC46
f12377bce9b9atagen2026-09-26 12:32 UTC47
pub(super) enum Notice {
f12377bce9b9atagen2026-09-26 12:32 UTC48
    Event(Event),
f12377bce9b9atagen2026-09-26 12:32 UTC49
    Rejected(Option<OwnedDeviceId>),
f12377bce9b9atagen2026-09-26 12:32 UTC50
}
f12377bce9b9atagen2026-09-26 12:32 UTC51
f12377bce9b9atagen2026-09-26 12:32 UTC52
/// saves and notices are split, so a full event stream never holds a save back
f12377bce9b9atagen2026-09-26 12:32 UTC53
pub(super) async fn session(
f12377bce9b9atagen2026-09-26 12:32 UTC54
    client: Client,
f12377bce9b9atagen2026-09-26 12:32 UTC55
    mut changes: broadcast::Receiver<SessionChange>,
f12377bce9b9atagen2026-09-26 12:32 UTC56
    mut persistence: Option<Persistence>,
f12377bce9b9atagen2026-09-26 12:32 UTC57
    notices: mpsc::UnboundedSender<Notice>,
f12377bce9b9atagen2026-09-26 12:32 UTC58
    device_id: OwnedDeviceId,
f12377bce9b9atagen2026-09-26 12:32 UTC59
    logouts: Arc<watch::Sender<Logouts>>,
f12377bce9b9atagen2026-09-26 12:32 UTC60
) {
f12377bce9b9atagen2026-09-26 12:32 UTC61
    loop {
f12377bce9b9atagen2026-09-26 12:32 UTC62
        let event = match changes.recv().await {
f12377bce9b9atagen2026-09-26 12:32 UTC63
            // a missed refresh costs one needless save, a missed rejection comes round again
f12377bce9b9atagen2026-09-26 12:32 UTC64
            Ok(SessionChange::TokensRefreshed) | Err(RecvError::Lagged(_)) => {
f12377bce9b9atagen2026-09-26 12:32 UTC65
                match persistence.as_mut() {
f12377bce9b9atagen2026-09-26 12:32 UTC66
                    None => Event::SessionTokensChanged,
f12377bce9b9atagen2026-09-26 12:32 UTC67
                    Some(persistence) => match persistence.persist(&client).await {
f12377bce9b9atagen2026-09-26 12:32 UTC68
                        Ok(()) => Event::SessionTokensChanged,
f12377bce9b9atagen2026-09-26 12:32 UTC69
                        Err(error) => Event::SessionSaveFailed {
f12377bce9b9atagen2026-09-26 12:32 UTC70
                            message: error.message,
f12377bce9b9atagen2026-09-26 12:32 UTC71
                        },
f12377bce9b9atagen2026-09-26 12:32 UTC72
                    },
f12377bce9b9atagen2026-09-26 12:32 UTC73
                }
f12377bce9b9atagen2026-09-26 12:32 UTC74
            }
f12377bce9b9atagen2026-09-26 12:32 UTC75
            Ok(SessionChange::UnknownToken(data)) => {
f12377bce9b9atagen2026-09-26 12:32 UTC76
                let soft_logout = data.soft_logout.then_some(device_id);
f12377bce9b9atagen2026-09-26 12:32 UTC77
                let mut held = false;
f12377bce9b9atagen2026-09-26 12:32 UTC78
                // our own logout revokes the token, so a rejection meanwhile is its doing
f12377bce9b9atagen2026-09-26 12:32 UTC79
                logouts.send_if_modified(|logouts| {
f12377bce9b9atagen2026-09-26 12:32 UTC80
                    held = logouts.in_flight > 0;
f12377bce9b9atagen2026-09-26 12:32 UTC81
                    if held {
f12377bce9b9atagen2026-09-26 12:32 UTC82
                        logouts.rejected = Some(soft_logout.clone());
f12377bce9b9atagen2026-09-26 12:32 UTC83
                    }
f12377bce9b9atagen2026-09-26 12:32 UTC84
                    held
f12377bce9b9atagen2026-09-26 12:32 UTC85
                });
f12377bce9b9atagen2026-09-26 12:32 UTC86
                if !held {
f12377bce9b9atagen2026-09-26 12:32 UTC87
                    let _ = notices.send(Notice::Rejected(soft_logout));
f12377bce9b9atagen2026-09-26 12:32 UTC88
                }
f12377bce9b9atagen2026-09-26 12:32 UTC89
                return;
f12377bce9b9atagen2026-09-26 12:32 UTC90
            }
f12377bce9b9atagen2026-09-26 12:32 UTC91
            Err(RecvError::Closed) => return,
f12377bce9b9atagen2026-09-26 12:32 UTC92
        };
f12377bce9b9atagen2026-09-26 12:32 UTC93
        if notices.send(Notice::Event(event)).is_err() {
f12377bce9b9atagen2026-09-26 12:32 UTC94
            return;
f12377bce9b9atagen2026-09-26 12:32 UTC95
        }
f12377bce9b9atagen2026-09-26 12:32 UTC96
    }
f12377bce9b9atagen2026-09-26 12:32 UTC97
}
f12377bce9b9atagen2026-09-26 12:32 UTC98
f12377bce9b9atagen2026-09-26 12:32 UTC99
pub(super) async fn session_notices(
f12377bce9b9atagen2026-09-26 12:32 UTC100
    mut notices: mpsc::UnboundedReceiver<Notice>,
f12377bce9b9atagen2026-09-26 12:32 UTC101
    events: EventSender,
f12377bce9b9atagen2026-09-26 12:32 UTC102
    stop: mpsc::UnboundedSender<Termination>,
f12377bce9b9atagen2026-09-26 12:32 UTC103
) {
f12377bce9b9atagen2026-09-26 12:32 UTC104
    while let Some(notice) = notices.recv().await {
f12377bce9b9atagen2026-09-26 12:32 UTC105
        match notice {
f12377bce9b9atagen2026-09-26 12:32 UTC106
            Notice::Event(event) => {
f12377bce9b9atagen2026-09-26 12:32 UTC107
                if events.send(event).await.is_err() {
f12377bce9b9atagen2026-09-26 12:32 UTC108
                    return;
f12377bce9b9atagen2026-09-26 12:32 UTC109
                }
f12377bce9b9atagen2026-09-26 12:32 UTC110
            }
f12377bce9b9atagen2026-09-26 12:32 UTC111
            Notice::Rejected(soft_logout) => {
f12377bce9b9atagen2026-09-26 12:32 UTC112
                return reject_session(&events, &stop, soft_logout).await;
f12377bce9b9atagen2026-09-26 12:32 UTC113
            }
f12377bce9b9atagen2026-09-26 12:32 UTC114
        }
f12377bce9b9atagen2026-09-26 12:32 UTC115
    }
f12377bce9b9atagen2026-09-26 12:32 UTC116
}
f12377bce9b9atagen2026-09-26 12:32 UTC117
f12377bce9b9atagen2026-09-26 12:32 UTC118
pub(super) async fn reject_session(
f12377bce9b9atagen2026-09-26 12:32 UTC119
    events: &EventSender,
f12377bce9b9atagen2026-09-26 12:32 UTC120
    stop: &mpsc::UnboundedSender<Termination>,
f12377bce9b9atagen2026-09-26 12:32 UTC121
    soft_logout: Option<OwnedDeviceId>,
f12377bce9b9atagen2026-09-26 12:32 UTC122
) {
f12377bce9b9atagen2026-09-26 12:32 UTC123
    let _ = events
f12377bce9b9atagen2026-09-26 12:32 UTC124
        .send(Event::SessionAccessRejected {
f12377bce9b9atagen2026-09-26 12:32 UTC125
            soft_logout: soft_logout.clone(),
f12377bce9b9atagen2026-09-26 12:32 UTC126
        })
f12377bce9b9atagen2026-09-26 12:32 UTC127
        .await;
f12377bce9b9atagen2026-09-26 12:32 UTC128
    let _ = stop.send(match soft_logout {
f12377bce9b9atagen2026-09-26 12:32 UTC129
        Some(device_id) => Termination::SoftLoggedOut { device_id },
f12377bce9b9atagen2026-09-26 12:32 UTC130
        None => Termination::AccessRevoked,
f12377bce9b9atagen2026-09-26 12:32 UTC131
    });
f12377bce9b9atagen2026-09-26 12:32 UTC132
}