keep_connection.rs

  1// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7//! Keep a connection alive
  8//!
  9//! This example demonstrates that tokio_xmpp will keep a connection alive
 10//! as good as it can, transparently reconnecting on interruptions of the TCP
 11//! stream.
 12
 13use core::str::FromStr;
 14use core::time::Duration;
 15use std::env::args;
 16use std::process::exit;
 17
 18use futures::StreamExt;
 19
 20#[cfg(feature = "rustls-any-backend")]
 21use tokio_xmpp::rustls;
 22use tokio_xmpp::{
 23    connect::{DnsConfig, StartTlsServerConnector},
 24    parsers::{
 25        iq::Iq,
 26        jid::{BareJid, Jid},
 27        ping,
 28    },
 29    stanzastream::StanzaStream,
 30    xmlstream::Timeouts,
 31};
 32
 33#[cfg(all(
 34    feature = "rustls-any-backend",
 35    not(any(feature = "aws_lc_rs", feature = "ring"))
 36))]
 37compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
 38
 39#[tokio::main]
 40async fn main() {
 41    env_logger::init();
 42
 43    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
 44    rustls::crypto::aws_lc_rs::default_provider()
 45        .install_default()
 46        .expect("failed to install rustls crypto provider");
 47
 48    #[cfg(all(feature = "ring"))]
 49    rustls::crypto::ring::default_provider()
 50        .install_default()
 51        .expect("failed to install rustls crypto provider");
 52
 53    let args: Vec<String> = args().collect();
 54    if args.len() != 3 {
 55        println!("Usage: {} <jid> <password>", args[0]);
 56        exit(1);
 57    }
 58    let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
 59    let password = &args[2];
 60
 61    let mut timeouts = Timeouts::tight();
 62    timeouts.read_timeout = Duration::new(5, 0);
 63
 64    let mut stream = StanzaStream::new_c2s(
 65        StartTlsServerConnector::from(DnsConfig::UseSrv {
 66            host: jid.domain().as_str().to_owned(),
 67            srv: "_xmpp-client._tcp".to_owned(),
 68            fallback_port: 5222,
 69        }),
 70        jid.clone().into(),
 71        password.clone(),
 72        timeouts,
 73        16,
 74    );
 75    let domain: Jid = jid.domain().to_owned().into();
 76    let mut ping_timer = tokio::time::interval(Duration::new(5, 0));
 77    let mut ping_ctr: u64 = rand::random();
 78    let signal = tokio::signal::ctrl_c();
 79    tokio::pin!(signal);
 80    loop {
 81        tokio::select! {
 82            _ = &mut signal => {
 83                log::info!("Ctrl+C pressed, shutting down cleanly.");
 84                break;
 85            }
 86            _ = ping_timer.tick() => {
 87                log::info!("sending ping for fun & profit");
 88                ping_ctr = ping_ctr.wrapping_add(1);
 89                let iq = Iq::from_get(format!("ping-{}", ping_ctr), ping::Ping).with_to(domain.clone());
 90                stream.send(Box::new(iq.into())).await;
 91            }
 92            ev = stream.next() => match ev {
 93                Some(ev) => {
 94                    log::info!("{:?}", ev);
 95                }
 96                None => {
 97                    panic!("stream terminated unexpectedly!");
 98                }
 99            }
100        }
101    }
102    stream.close().await;
103}