tokio-xmpp: add option to split client
Adds an option to split the `tokio-xmpp::Client` into a Sender and Receiver. To enable this, a client worker is added which drives the stream in the background.
This commit is contained in:
parent
d84cb261e4
commit
10f1b3663c
8 changed files with 287 additions and 63 deletions
|
|
@ -22,6 +22,7 @@ Version NEXT:
|
||||||
* Changed:
|
* Changed:
|
||||||
- Update hickory-dns to 0.26 (!671)
|
- Update hickory-dns to 0.26 (!671)
|
||||||
- Drive the XMPP client stream in the background (!631)
|
- Drive the XMPP client stream in the background (!631)
|
||||||
|
- Add option to split XMPP client into read and write half (!631)
|
||||||
|
|
||||||
Version 5.0.0:
|
Version 5.0.0:
|
||||||
2025-10-28 pep <pep@bouah.net>
|
2025-10-28 pep <pep@bouah.net>
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,7 @@ impl IqResponseSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility struct to track IQ responses.
|
/// Utility struct to track IQ responses.
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct IqResponseTracker {
|
pub struct IqResponseTracker {
|
||||||
map: Arc<Mutex<IqMap>>,
|
map: Arc<Mutex<IqMap>>,
|
||||||
account_jid: Arc<Mutex<Option<BareJid>>>,
|
account_jid: Arc<Mutex<Option<BareJid>>>,
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,19 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use crate::client::{receiver::ClientReceiver, sender::ClientSender};
|
||||||
|
use crate::connect::ServerConnector;
|
||||||
|
use crate::error::Error;
|
||||||
|
use crate::event::Event;
|
||||||
|
use crate::stanzastream::{self, StanzaStage, StanzaState, StanzaStream, StanzaToken};
|
||||||
|
use crate::xmlstream::Timeouts;
|
||||||
|
use crate::Stanza;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::{mpsc, oneshot, Mutex};
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
use xmpp_parsers::{jid::Jid, stream_features::StreamFeatures};
|
use xmpp_parsers::{jid::Jid, stream_features::StreamFeatures};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
connect::ServerConnector,
|
|
||||||
error::Error,
|
|
||||||
stanzastream::{StanzaStage, StanzaState, StanzaStream, StanzaToken},
|
|
||||||
xmlstream::Timeouts,
|
|
||||||
Stanza,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "direct-tls")]
|
#[cfg(feature = "direct-tls")]
|
||||||
use crate::connect::DirectTlsServerConnector;
|
use crate::connect::DirectTlsServerConnector;
|
||||||
#[cfg(any(feature = "direct-tls", feature = "starttls", feature = "insecure-tcp"))]
|
#[cfg(any(feature = "direct-tls", feature = "starttls", feature = "insecure-tcp"))]
|
||||||
|
|
@ -27,10 +28,13 @@ use crate::connect::TcpServerConnector;
|
||||||
|
|
||||||
mod iq;
|
mod iq;
|
||||||
pub(crate) mod login;
|
pub(crate) mod login;
|
||||||
|
pub(crate) mod receiver;
|
||||||
|
pub(crate) mod sender;
|
||||||
mod stream;
|
mod stream;
|
||||||
pub use login::auth;
|
mod worker;
|
||||||
|
|
||||||
pub use iq::{IqFailure, IqRequest, IqResponse, IqResponseToken};
|
pub use iq::{IqFailure, IqRequest, IqResponse, IqResponseToken};
|
||||||
|
pub use login::auth;
|
||||||
|
|
||||||
/// XMPP client connection and state
|
/// XMPP client connection and state
|
||||||
///
|
///
|
||||||
|
|
@ -41,9 +45,19 @@ pub use iq::{IqFailure, IqRequest, IqResponse, IqResponseToken};
|
||||||
/// used.
|
/// used.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
stream: StanzaStream,
|
// Stanza receiver from the client worker
|
||||||
|
stanza_rx: mpsc::Receiver<Event>,
|
||||||
|
// Stanza sender to the StanzaStream
|
||||||
|
stream_tx: stanzastream::StanzaSender,
|
||||||
|
// Shutdown handle for the client worker
|
||||||
|
shutdown_tx: oneshot::Sender<()>,
|
||||||
|
// Client worker task
|
||||||
|
worker: JoinHandle<stanzastream::StanzaReceiver>,
|
||||||
|
// JID of the logged-in client
|
||||||
bound_jid: Option<Jid>,
|
bound_jid: Option<Jid>,
|
||||||
|
// Stream features of the currently connected stream
|
||||||
features: Option<StreamFeatures>,
|
features: Option<StreamFeatures>,
|
||||||
|
// Response tracker for IQs
|
||||||
iq_response_tracker: iq::IqResponseTracker,
|
iq_response_tracker: iq::IqResponseTracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,7 +86,8 @@ impl Client {
|
||||||
/// [`send_iq`][`Self::send_iq`], which allows awaiting the response.
|
/// [`send_iq`][`Self::send_iq`], which allows awaiting the response.
|
||||||
pub async fn send_stanza(&mut self, mut stanza: Stanza) -> Result<StanzaToken, io::Error> {
|
pub async fn send_stanza(&mut self, mut stanza: Stanza) -> Result<StanzaToken, io::Error> {
|
||||||
stanza.ensure_id();
|
stanza.ensure_id();
|
||||||
let mut token = self.stream.send(Box::new(stanza)).await;
|
let mut token = self.stream_tx.send(Box::new(stanza)).await;
|
||||||
|
|
||||||
match token.wait_for(StanzaStage::Sent).await {
|
match token.wait_for(StanzaStage::Sent).await {
|
||||||
// Queued < Sent, so it cannot be reached.
|
// Queued < Sent, so it cannot be reached.
|
||||||
Some(StanzaState::Queued) => unreachable!(),
|
Some(StanzaState::Queued) => unreachable!(),
|
||||||
|
|
@ -93,13 +108,6 @@ impl Client {
|
||||||
/// response. See also the documentation of [`IqResponseToken`] for more
|
/// response. See also the documentation of [`IqResponseToken`] for more
|
||||||
/// information on the behaviour of these tokens.
|
/// information on the behaviour of these tokens.
|
||||||
///
|
///
|
||||||
/// **Important**: Even though IQ responses are delivered through the
|
|
||||||
/// returned token (and never through the `Stream`), the
|
|
||||||
/// [`Stream`][`futures::Stream`]
|
|
||||||
/// implementation of the [`Client`] **must be polled** to make progress
|
|
||||||
/// on the stream and to process incoming stanzas and thus to deliver them
|
|
||||||
/// to the returned token.
|
|
||||||
///
|
|
||||||
/// **Note**: If an IQ response arrives after the `token` has been
|
/// **Note**: If an IQ response arrives after the `token` has been
|
||||||
/// dropped (e.g. due to a timeout), it will be delivered through the
|
/// dropped (e.g. due to a timeout), it will be delivered through the
|
||||||
/// `Stream` like any other stanza.
|
/// `Stream` like any other stanza.
|
||||||
|
|
@ -108,7 +116,8 @@ impl Client {
|
||||||
// from is always None for a client
|
// from is always None for a client
|
||||||
None, to, req,
|
None, to, req,
|
||||||
);
|
);
|
||||||
let stanza_token = self.stream.send(Box::new(iq.into())).await;
|
let stanza_token = self.stream_tx.send(Box::new(iq.into())).await;
|
||||||
|
|
||||||
token.set_stanza_token(stanza_token);
|
token.set_stanza_token(stanza_token);
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
|
|
@ -128,9 +137,42 @@ impl Client {
|
||||||
/// This performs an orderly stream shutdown, ensuring that all resources
|
/// This performs an orderly stream shutdown, ensuring that all resources
|
||||||
/// are correctly cleaned up.
|
/// are correctly cleaned up.
|
||||||
pub async fn send_end(self) -> Result<(), Error> {
|
pub async fn send_end(self) -> Result<(), Error> {
|
||||||
self.stream.close().await;
|
self.shutdown_tx.send(()).expect("ClientWorker crashed.");
|
||||||
|
|
||||||
|
let stream_rx = self.worker.await.unwrap();
|
||||||
|
let stream = StanzaStream::reunite(self.stream_tx, stream_rx);
|
||||||
|
stream.close().await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Split the client into [`ClientSender`] and [`ClientReceiver`].
|
||||||
|
pub fn split(self) -> (ClientSender, ClientReceiver) {
|
||||||
|
let client = Arc::new(Mutex::new(self));
|
||||||
|
|
||||||
|
let sender = ClientSender(client.clone());
|
||||||
|
let receiver = ClientReceiver(client);
|
||||||
|
|
||||||
|
(sender, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reunite a [`ClientSender`] and [`ClientReceiver`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This functions returns an error if the [`ClientSender`] and
|
||||||
|
/// [`ClientReceiver`] don't come from the same [`Client`].
|
||||||
|
pub fn reunite(sender: ClientSender, receiver: ClientReceiver) -> Self {
|
||||||
|
assert!(
|
||||||
|
Arc::ptr_eq(&sender.0, &receiver.0),
|
||||||
|
"Unrelated ClientSender and ClientReceiver passed to reunite."
|
||||||
|
);
|
||||||
|
|
||||||
|
drop(sender);
|
||||||
|
|
||||||
|
let inner = Arc::try_unwrap(receiver.0).expect("Failed to unwrap ClientReceiver Arc");
|
||||||
|
inner.into_inner()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "direct-tls")]
|
#[cfg(feature = "direct-tls")]
|
||||||
|
|
@ -220,11 +262,23 @@ impl Client {
|
||||||
connector: C,
|
connector: C,
|
||||||
timeouts: Timeouts,
|
timeouts: Timeouts,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let stream = StanzaStream::new_c2s(connector, jid.into(), password.into(), timeouts, 16);
|
||||||
|
let (stream_tx, stream_rx) = stream.split();
|
||||||
|
|
||||||
|
let iq_response_tracker = iq::IqResponseTracker::new();
|
||||||
|
let (worker, shutdown_tx, stanza_rx) =
|
||||||
|
worker::ClientWorker::new(stream_rx, iq_response_tracker.clone(), 16);
|
||||||
|
|
||||||
|
let worker = tokio::task::spawn(async move { worker.run().await });
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
stream: StanzaStream::new_c2s(connector, jid.into(), password.into(), timeouts, 16),
|
stream_tx,
|
||||||
|
stanza_rx,
|
||||||
|
worker,
|
||||||
|
shutdown_tx,
|
||||||
|
iq_response_tracker,
|
||||||
bound_jid: None,
|
bound_jid: None,
|
||||||
features: None,
|
features: None,
|
||||||
iq_response_tracker: iq::IqResponseTracker::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
tokio-xmpp/src/client/receiver.rs
Normal file
48
tokio-xmpp/src/client/receiver.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright (c) 2025 xmpp-rs contributors.
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use crate::Client;
|
||||||
|
use crate::Event;
|
||||||
|
use core::{pin::Pin, task::Context};
|
||||||
|
use futures::StreamExt;
|
||||||
|
use futures::{task::Poll, Stream};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use xmpp_parsers::{jid::Jid, stream_features::StreamFeatures};
|
||||||
|
|
||||||
|
/// Read half of a [`Client`](crate::Client).
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ClientReceiver(pub(super) Arc<Mutex<Client>>);
|
||||||
|
|
||||||
|
impl ClientReceiver {
|
||||||
|
/// Return the bound JID.
|
||||||
|
///
|
||||||
|
/// See the documentation of [`Client::bound_jid`](crate::Client::bound_jid) for more
|
||||||
|
/// information.
|
||||||
|
pub async fn bound_jid(&self) -> Option<Jid> {
|
||||||
|
self.0.lock().await.bound_jid.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the received stream features.
|
||||||
|
///
|
||||||
|
/// See the documentation of [`Client::get_stream_features`](crate::Client::get_stream_features)
|
||||||
|
/// for more information.
|
||||||
|
pub async fn get_stream_features(&self) -> Option<StreamFeatures> {
|
||||||
|
self.0.lock().await.features.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for ClientReceiver {
|
||||||
|
type Item = Event;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
||||||
|
let Ok(mut client) = self.0.try_lock() else {
|
||||||
|
return Poll::Pending;
|
||||||
|
};
|
||||||
|
|
||||||
|
client.poll_next_unpin(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
35
tokio-xmpp/src/client/sender.rs
Normal file
35
tokio-xmpp/src/client/sender.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (c) 2025 xmpp-rs contributors.
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use crate::stanzastream::StanzaToken;
|
||||||
|
use crate::IqRequest;
|
||||||
|
use crate::IqResponseToken;
|
||||||
|
use crate::Stanza;
|
||||||
|
use std::io;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use xmpp_parsers::jid::Jid;
|
||||||
|
|
||||||
|
/// Write half of a [`Client`](crate::Client).
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ClientSender(pub(super) Arc<Mutex<super::Client>>);
|
||||||
|
|
||||||
|
impl ClientSender {
|
||||||
|
/// Send a stanza.
|
||||||
|
///
|
||||||
|
/// See the documentation of [`Client::send_stanza`](crate::Client::send_stanza) for more
|
||||||
|
/// information.
|
||||||
|
pub async fn send_stanza(&self, stanza: Stanza) -> Result<StanzaToken, io::Error> {
|
||||||
|
self.0.lock().await.send_stanza(stanza).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send in iq.
|
||||||
|
///
|
||||||
|
/// See the documentation of [`Client::send_iq`](crate::Client::send_iq) for more information.
|
||||||
|
pub async fn send_iq(&self, to: Option<Jid>, req: IqRequest) -> IqResponseToken {
|
||||||
|
self.0.lock().await.send_iq(to, req).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,15 +4,10 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use core::ops::ControlFlow;
|
|
||||||
use core::{pin::Pin, task::Context};
|
use core::{pin::Pin, task::Context};
|
||||||
use futures::{ready, task::Poll, Stream};
|
use futures::{ready, task::Poll, Stream};
|
||||||
|
|
||||||
use crate::{
|
use crate::{client::Client, Event};
|
||||||
client::Client,
|
|
||||||
stanzastream::{Event as StanzaStreamEvent, StreamEvent},
|
|
||||||
Event, Stanza,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Incoming XMPP events
|
/// Incoming XMPP events
|
||||||
///
|
///
|
||||||
|
|
@ -32,39 +27,21 @@ impl Stream for Client {
|
||||||
///
|
///
|
||||||
/// ...for your client
|
/// ...for your client
|
||||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
||||||
loop {
|
Poll::Ready(match ready!(self.stanza_rx.poll_recv(cx)) {
|
||||||
return Poll::Ready(match ready!(Pin::new(&mut self.stream).poll_next(cx)) {
|
None => None,
|
||||||
None => None,
|
Some(event) => {
|
||||||
Some(StanzaStreamEvent::Stanza(st)) => match st {
|
if let Event::Online {
|
||||||
Stanza::Iq(iq) => match self.iq_response_tracker.handle_iq(iq) {
|
ref bound_jid,
|
||||||
ControlFlow::Break(()) => continue,
|
ref features,
|
||||||
ControlFlow::Continue(iq) => Some(Event::Stanza(Stanza::Iq(iq))),
|
..
|
||||||
},
|
} = event
|
||||||
other => Some(Event::Stanza(other)),
|
{
|
||||||
},
|
|
||||||
Some(StanzaStreamEvent::Stream(StreamEvent::Reset {
|
|
||||||
bound_jid,
|
|
||||||
features,
|
|
||||||
})) => {
|
|
||||||
self.features = Some(features.clone());
|
|
||||||
self.bound_jid = Some(bound_jid.clone());
|
self.bound_jid = Some(bound_jid.clone());
|
||||||
|
self.features = Some(features.clone());
|
||||||
self.iq_response_tracker
|
|
||||||
.set_account_jid(bound_jid.to_bare());
|
|
||||||
|
|
||||||
Some(Event::Online {
|
|
||||||
bound_jid,
|
|
||||||
features,
|
|
||||||
resumed: false,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Some(StanzaStreamEvent::Stream(StreamEvent::Resumed)) => Some(Event::Online {
|
|
||||||
bound_jid: self.bound_jid.as_ref().unwrap().clone(),
|
Some(event)
|
||||||
features: self.features.as_ref().unwrap().clone(),
|
}
|
||||||
resumed: true,
|
})
|
||||||
}),
|
|
||||||
Some(StanzaStreamEvent::Stream(StreamEvent::Suspended)) => continue,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
105
tokio-xmpp/src/client/worker.rs
Normal file
105
tokio-xmpp/src/client/worker.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright (c) 2025 xmpp-rs contributors.
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use crate::client::iq;
|
||||||
|
use crate::stanzastream::StanzaReceiver;
|
||||||
|
use crate::stanzastream::{Event as StanzaStreamEvent, StreamEvent};
|
||||||
|
use crate::{Event, Stanza};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
use xmpp_parsers::jid::Jid;
|
||||||
|
use xmpp_parsers::stream_features::StreamFeatures;
|
||||||
|
|
||||||
|
/// Worker to drive the [`crate::stanzastream`] of a client in the background and continue to
|
||||||
|
/// acknowledge IQs, even when the client is not polled.
|
||||||
|
pub struct ClientWorker {
|
||||||
|
// Receiver from the StanzaStream
|
||||||
|
stream_rx: StanzaReceiver,
|
||||||
|
// Sender to the client (worker-to-frontend)
|
||||||
|
stanza_w2f_tx: mpsc::Sender<Event>,
|
||||||
|
// Shutdown signal receiver from frontend
|
||||||
|
shutdown_rx: oneshot::Receiver<()>,
|
||||||
|
// JID of the logged-in client
|
||||||
|
bound_jid: Option<Jid>,
|
||||||
|
// Stream features of the currently connected stream
|
||||||
|
features: Option<StreamFeatures>,
|
||||||
|
// Response tracker for IQs
|
||||||
|
iq_response_tracker: iq::IqResponseTracker,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientWorker {
|
||||||
|
pub fn new(
|
||||||
|
stream_rx: StanzaReceiver,
|
||||||
|
iq_response_tracker: iq::IqResponseTracker,
|
||||||
|
depth: usize,
|
||||||
|
) -> (Self, oneshot::Sender<()>, mpsc::Receiver<Event>) {
|
||||||
|
let (shutdown_tx, shutdown_rx) = oneshot::channel();
|
||||||
|
|
||||||
|
// worker-to-frontend connection
|
||||||
|
let (stanza_w2f_tx, stanza_w2f_rx) = mpsc::channel(depth);
|
||||||
|
|
||||||
|
let worker = Self {
|
||||||
|
stream_rx,
|
||||||
|
stanza_w2f_tx,
|
||||||
|
iq_response_tracker,
|
||||||
|
shutdown_rx,
|
||||||
|
bound_jid: None,
|
||||||
|
features: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
(worker, shutdown_tx, stanza_w2f_rx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(mut self) -> StanzaReceiver {
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
_ = &mut self.shutdown_rx => {
|
||||||
|
return self.stream_rx;
|
||||||
|
}
|
||||||
|
Some(event) = self.stream_rx.next() => {
|
||||||
|
self.handle_event(event).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_event(&mut self, event: StanzaStreamEvent) {
|
||||||
|
let send_event = match event {
|
||||||
|
StanzaStreamEvent::Stanza(st) => match st {
|
||||||
|
Stanza::Iq(iq) => match self.iq_response_tracker.handle_iq(iq) {
|
||||||
|
ControlFlow::Break(()) => return,
|
||||||
|
ControlFlow::Continue(iq) => Event::Stanza(Stanza::Iq(iq)),
|
||||||
|
},
|
||||||
|
other => Event::Stanza(other),
|
||||||
|
},
|
||||||
|
StanzaStreamEvent::Stream(StreamEvent::Reset {
|
||||||
|
bound_jid,
|
||||||
|
features,
|
||||||
|
}) => {
|
||||||
|
self.iq_response_tracker
|
||||||
|
.set_account_jid(bound_jid.to_bare());
|
||||||
|
|
||||||
|
Event::Online {
|
||||||
|
bound_jid,
|
||||||
|
features,
|
||||||
|
resumed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StanzaStreamEvent::Stream(StreamEvent::Resumed) => Event::Online {
|
||||||
|
bound_jid: self.bound_jid.as_ref().unwrap().clone(),
|
||||||
|
features: self.features.as_ref().unwrap().clone(),
|
||||||
|
resumed: true,
|
||||||
|
},
|
||||||
|
StanzaStreamEvent::Stream(StreamEvent::Suspended) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(()) = self.stanza_w2f_tx.send(send_event).await else {
|
||||||
|
panic!("All clients have been dropped.");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -128,7 +128,11 @@ pub use xso::{asxml::PrintRawXml, error::FromElementError};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
/// Generic tokio_xmpp Error
|
/// Generic tokio_xmpp Error
|
||||||
pub use crate::error::Error;
|
pub use crate::error::Error;
|
||||||
pub use client::{auth as client_login, Client, IqFailure, IqRequest, IqResponse, IqResponseToken};
|
pub use client::{
|
||||||
|
auth as client_login, receiver::ClientReceiver, sender::ClientSender, Client, IqFailure,
|
||||||
|
IqRequest, IqResponse, IqResponseToken,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "insecure-tcp")]
|
#[cfg(feature = "insecure-tcp")]
|
||||||
pub use component::Component;
|
pub use component::Component;
|
||||||
pub use event::{Event, Stanza};
|
pub use event::{Event, Stanza};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue