mirror of
https://github.com/librespot-org/librespot.git
synced 2025-10-04 10:19:27 +02:00
Discovery: Refactor and add Avahi DBus backend (#1347)
* discovery: use opaque error type for DnsSdError This helps to decouple discovery and core by not leaking implementation details of the zeroconf backend into Error conversion impls in core. * discovery: map all MDNS/DNS-SD errors to DiscoveryError::DnsSdError previously, libmdns errors would use a generic conversion from std::io::Error to core::Error * discovery: use an opaque type for the handle to the DNS-SD service * discovery: make features additive i.e. add with-libmdns instead of using not(with-dns-sd). The logic is such that enabling with-dns-sd in addition to the default with-libmdns will still end up using dns-sd, as before. If only with-libmdns is enabled, that will be the default. If none of the features is enabled, attempting to build a `Discovery` will yield an error. * discovery: add --zeroconf-backend CLI flag * discovery: Add minimal Avahi zeroconf backend * bump MSRV to 1.75 required by zbus >= 4 * discovery: ensure that server and dns-sd backend shutdown gracefully Previously, on drop the the shutdown_tx/close_tx, it wasn't guaranteed the corresponding tasks would continue to be polled until they actually completed their shutdown. Since dns_sd::Service is not Send and non-async, and because libmdns is non-async, put them on their own threads. * discovery: use a shared channel for server and zeroconf status messages * discovery: add Avahi reconnection logic This deals gracefully with the case where the Avahi daemon is restarted or not running initially. * discovery: allow running when compiled without zeroconf backend... ...but exit with an error if there's no way to authenticate * better error messages for invalid options with no short flag
This commit is contained in:
parent
d2324ddd1b
commit
94d174c33d
15 changed files with 1156 additions and 129 deletions
|
@ -1,18 +1,14 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
collections::BTreeMap,
|
||||
convert::Infallible,
|
||||
net::{Ipv4Addr, Ipv6Addr, SocketAddr, TcpListener},
|
||||
pin::Pin,
|
||||
sync::{Arc, Mutex},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use aes::cipher::{KeyIvInit, StreamCipher};
|
||||
use base64::engine::general_purpose::STANDARD as BASE64;
|
||||
use base64::engine::Engine as _;
|
||||
use bytes::Bytes;
|
||||
use futures_core::Stream;
|
||||
use futures_util::{FutureExt, TryFutureExt};
|
||||
use hmac::{Hmac, Mac};
|
||||
use http_body_util::{BodyExt, Full};
|
||||
|
@ -24,7 +20,7 @@ use serde_json::json;
|
|||
use sha1::{Digest, Sha1};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
use super::DiscoveryError;
|
||||
use super::{DiscoveryError, DiscoveryEvent};
|
||||
|
||||
use crate::{
|
||||
core::config::DeviceType,
|
||||
|
@ -47,21 +43,17 @@ struct RequestHandler {
|
|||
config: Config,
|
||||
username: Mutex<Option<String>>,
|
||||
keys: DhLocalKeys,
|
||||
tx: mpsc::UnboundedSender<Credentials>,
|
||||
event_tx: mpsc::UnboundedSender<DiscoveryEvent>,
|
||||
}
|
||||
|
||||
impl RequestHandler {
|
||||
fn new(config: Config) -> (Self, mpsc::UnboundedReceiver<Credentials>) {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
|
||||
let discovery = Self {
|
||||
fn new(config: Config, event_tx: mpsc::UnboundedSender<DiscoveryEvent>) -> Self {
|
||||
Self {
|
||||
config,
|
||||
username: Mutex::new(None),
|
||||
keys: DhLocalKeys::random(&mut rand::thread_rng()),
|
||||
tx,
|
||||
};
|
||||
|
||||
(discovery, rx)
|
||||
event_tx,
|
||||
}
|
||||
}
|
||||
|
||||
fn active_user(&self) -> String {
|
||||
|
@ -202,7 +194,8 @@ impl RequestHandler {
|
|||
|
||||
{
|
||||
let maybe_username = self.username.lock();
|
||||
self.tx.send(credentials)?;
|
||||
self.event_tx
|
||||
.send(DiscoveryEvent::Credentials(credentials))?;
|
||||
if let Ok(mut username_field) = maybe_username {
|
||||
*username_field = Some(String::from(username));
|
||||
} else {
|
||||
|
@ -258,14 +251,22 @@ impl RequestHandler {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) enum DiscoveryServerCmd {
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
pub struct DiscoveryServer {
|
||||
cred_rx: mpsc::UnboundedReceiver<Credentials>,
|
||||
_close_tx: oneshot::Sender<Infallible>,
|
||||
close_tx: oneshot::Sender<DiscoveryServerCmd>,
|
||||
task_handle: tokio::task::JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl DiscoveryServer {
|
||||
pub fn new(config: Config, port: &mut u16) -> Result<Self, Error> {
|
||||
let (discovery, cred_rx) = RequestHandler::new(config);
|
||||
pub fn new(
|
||||
config: Config,
|
||||
port: &mut u16,
|
||||
event_tx: mpsc::UnboundedSender<DiscoveryEvent>,
|
||||
) -> Result<Self, Error> {
|
||||
let discovery = RequestHandler::new(config, event_tx);
|
||||
let address = if cfg!(windows) {
|
||||
SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), *port)
|
||||
} else {
|
||||
|
@ -297,7 +298,7 @@ impl DiscoveryServer {
|
|||
}
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
let task_handle = tokio::spawn(async move {
|
||||
let discovery = Arc::new(discovery);
|
||||
|
||||
let server = hyper::server::conn::http1::Builder::new();
|
||||
|
@ -326,27 +327,32 @@ impl DiscoveryServer {
|
|||
});
|
||||
}
|
||||
_ = &mut close_rx => {
|
||||
debug!("Shutting down discovery server");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graceful.shutdown().await;
|
||||
debug!("Discovery server stopped");
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
cred_rx,
|
||||
_close_tx: close_tx,
|
||||
close_tx,
|
||||
task_handle,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for DiscoveryServer {
|
||||
type Item = Credentials;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Credentials>> {
|
||||
self.cred_rx.poll_recv(cx)
|
||||
pub async fn shutdown(self) {
|
||||
let Self {
|
||||
close_tx,
|
||||
task_handle,
|
||||
..
|
||||
} = self;
|
||||
log::debug!("Shutting down discovery server");
|
||||
if close_tx.send(DiscoveryServerCmd::Shutdown).is_err() {
|
||||
log::warn!("Discovery server unexpectedly disappeared");
|
||||
} else {
|
||||
let _ = task_handle.await;
|
||||
log::debug!("Discovery server stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue