Driver: Automate (re)connection logic (#81)

This PR adds several enhancements to Driver connection logic:
* Driver (re)connection attempts now have a default timeout of around 10s.
* The driver will now attempt to retry full connection attempts using a user-provided strategy: currently, this defaults to 5 attempts under an exponential backoff strategy.
* The driver will now fire `DriverDisconnect` events at the end of any session -- this unifies (re)connection failure events with session expiry as seen in #76, which should provide users with enough detail to know *which* voice channel to reconnect to. Users still need to be careful to read the session/channel IDs to ensure that they aren't overwriting another join.

This has been tested using `cargo make ready`, and by setting low timeouts to force failures in the voice receive example (with some additional error handlers).

Closes #68.
This commit is contained in:
Kyle Simpson
2021-06-23 17:11:14 +01:00
parent 8381f8c461
commit 210e3ae584
17 changed files with 672 additions and 90 deletions

View File

@@ -21,9 +21,9 @@ use error::{Error, Result};
use flume::Sender;
use std::{net::IpAddr, str::FromStr, sync::Arc};
#[cfg(not(feature = "tokio-02-marker"))]
use tokio::{net::UdpSocket, spawn};
use tokio::{net::UdpSocket, spawn, time::timeout};
#[cfg(feature = "tokio-02-marker")]
use tokio_compat::{net::UdpSocket, spawn};
use tokio_compat::{net::UdpSocket, spawn, time::timeout};
use tracing::{debug, info, instrument};
use url::Url;
use xsalsa20poly1305::{aead::NewAead, XSalsa20Poly1305 as Cipher};
@@ -42,9 +42,23 @@ pub(crate) struct Connection {
impl Connection {
pub(crate) async fn new(
info: ConnectionInfo,
interconnect: &Interconnect,
config: &Config,
idx: usize,
) -> Result<Connection> {
if let Some(t) = config.driver_timeout {
timeout(t, Connection::new_inner(info, interconnect, config, idx)).await?
} else {
Connection::new_inner(info, interconnect, config, idx).await
}
}
pub(crate) async fn new_inner(
mut info: ConnectionInfo,
interconnect: &Interconnect,
config: &Config,
idx: usize,
) -> Result<Connection> {
let url = generate_url(&mut info.endpoint)?;
@@ -207,6 +221,8 @@ impl Connection {
client,
ssrc,
hello.heartbeat_interval,
idx,
info.clone(),
));
spawn(udp_rx::runner(
@@ -226,7 +242,16 @@ impl Connection {
}
#[instrument(skip(self))]
pub async fn reconnect(&mut self) -> Result<()> {
pub async fn reconnect(&mut self, config: &Config) -> Result<()> {
if let Some(t) = config.driver_timeout {
timeout(t, self.reconnect_inner()).await?
} else {
self.reconnect_inner().await
}
}
#[instrument(skip(self))]
pub async fn reconnect_inner(&mut self) -> Result<()> {
let url = generate_url(&mut self.info.endpoint)?;
// Thread may have died, we want to send to prompt a clean exit