librespot/core/src/session.rs

343 lines
9.7 KiB
Rust
Raw Normal View History

use std::future::Future;
use std::io;
use std::pin::Pin;
2019-07-08 08:08:32 +00:00
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, RwLock, Weak};
use std::task::Context;
2021-01-21 20:49:39 +00:00
use std::task::Poll;
2019-03-24 14:15:14 +00:00
use std::time::{SystemTime, UNIX_EPOCH};
use byteorder::{BigEndian, ByteOrder};
use bytes::Bytes;
use futures_core::TryStream;
2021-04-17 09:55:57 +00:00
use futures_util::{future, ready, StreamExt, TryStreamExt};
use num_traits::FromPrimitive;
use once_cell::sync::OnceCell;
2021-02-13 10:53:23 +00:00
use thiserror::Error;
use tokio::sync::mpsc;
use tokio_stream::wrappers::UnboundedReceiverStream;
2015-06-23 14:38:29 +00:00
use crate::apresolve::ApResolver;
use crate::audio_key::AudioKeyManager;
use crate::authentication::Credentials;
use crate::cache::Cache;
use crate::channel::ChannelManager;
use crate::config::SessionConfig;
2021-02-13 10:53:23 +00:00
use crate::connection::{self, AuthenticationError};
2021-06-20 21:09:27 +00:00
use crate::http_client::HttpClient;
use crate::mercury::MercuryManager;
use crate::packet::PacketType;
2021-06-19 20:47:39 +00:00
use crate::token::TokenProvider;
2021-02-13 10:53:23 +00:00
#[derive(Debug, Error)]
pub enum SessionError {
#[error(transparent)]
AuthenticationError(#[from] AuthenticationError),
#[error("Cannot create session: {0}")]
IoError(#[from] io::Error),
}
2018-02-12 19:10:36 +00:00
struct SessionData {
country: String,
time_delta: i64,
canonical_username: String,
2018-04-21 15:46:29 +00:00
invalid: bool,
}
2018-02-12 19:13:37 +00:00
struct SessionInternal {
config: SessionConfig,
data: RwLock<SessionData>,
2015-06-23 14:38:29 +00:00
2021-06-20 21:09:27 +00:00
http_client: HttpClient,
2017-01-20 02:37:02 +00:00
tx_connection: mpsc::UnboundedSender<(u8, Vec<u8>)>,
2017-01-18 20:39:46 +00:00
apresolver: OnceCell<ApResolver>,
2021-01-21 20:49:39 +00:00
audio_key: OnceCell<AudioKeyManager>,
channel: OnceCell<ChannelManager>,
mercury: OnceCell<MercuryManager>,
2021-06-19 20:47:39 +00:00
token_provider: OnceCell<TokenProvider>,
cache: Option<Arc<Cache>>,
2017-01-19 22:45:24 +00:00
handle: tokio::runtime::Handle,
session_id: usize,
2015-04-25 20:32:07 +00:00
}
2019-07-08 08:08:32 +00:00
static SESSION_COUNTER: AtomicUsize = AtomicUsize::new(0);
2017-01-18 20:39:46 +00:00
#[derive(Clone)]
2018-02-12 19:13:37 +00:00
pub struct Session(Arc<SessionInternal>);
2017-01-18 20:39:46 +00:00
2015-04-25 20:32:07 +00:00
impl Session {
2021-01-21 20:49:39 +00:00
pub async fn connect(
2018-02-11 11:37:08 +00:00
config: SessionConfig,
credentials: Credentials,
cache: Option<Cache>,
2021-02-13 10:53:23 +00:00
) -> Result<Session, SessionError> {
2021-06-20 21:09:27 +00:00
let http_client = HttpClient::new(config.proxy.as_ref());
let (sender_tx, sender_rx) = mpsc::unbounded_channel();
let session_id = SESSION_COUNTER.fetch_add(1, Ordering::Relaxed);
2017-01-20 02:37:02 +00:00
debug!("new Session[{}]", session_id);
2017-01-18 18:41:22 +00:00
let session = Session(Arc::new(SessionInternal {
2021-03-01 02:37:22 +00:00
config,
2016-01-01 23:16:12 +00:00
data: RwLock::new(SessionData {
country: String::new(),
canonical_username: String::new(),
2018-04-21 15:46:29 +00:00
invalid: false,
2019-03-24 14:15:14 +00:00
time_delta: 0,
2016-01-01 23:16:12 +00:00
}),
2021-06-20 21:09:27 +00:00
http_client,
2017-01-20 02:37:02 +00:00
tx_connection: sender_tx,
cache: cache.map(Arc::new),
apresolver: OnceCell::new(),
2021-01-21 20:49:39 +00:00
audio_key: OnceCell::new(),
channel: OnceCell::new(),
mercury: OnceCell::new(),
2021-06-19 20:47:39 +00:00
token_provider: OnceCell::new(),
handle: tokio::runtime::Handle::current(),
2021-03-01 02:37:22 +00:00
session_id,
2017-01-18 18:41:22 +00:00
}));
let ap = session.apresolver().resolve("accesspoint").await;
info!("Connecting to AP \"{}:{}\"", ap.0, ap.1);
let mut transport =
connection::connect(&ap.0, ap.1, session.config().proxy.as_ref()).await?;
let reusable_credentials =
connection::authenticate(&mut transport, credentials, &session.config().device_id)
.await?;
info!("Authenticated as \"{}\" !", reusable_credentials.username);
session.0.data.write().unwrap().canonical_username = reusable_credentials.username.clone();
if let Some(cache) = session.cache() {
cache.save_credentials(&reusable_credentials);
}
let (sink, stream) = transport.split();
let sender_task = UnboundedReceiverStream::new(sender_rx)
.map(Ok)
.forward(sink);
let receiver_task = DispatchTask(stream, session.weak());
2017-01-20 02:37:02 +00:00
2021-04-12 20:54:32 +00:00
tokio::spawn(async move {
let result = future::try_join(sender_task, receiver_task).await;
if let Err(e) = result {
error!("{}", e);
}
});
Ok(session)
}
pub fn apresolver(&self) -> &ApResolver {
self.0
.apresolver
.get_or_init(|| ApResolver::new(self.weak()))
2015-06-23 14:38:29 +00:00
}
2017-01-18 20:39:46 +00:00
pub fn audio_key(&self) -> &AudioKeyManager {
2021-01-21 20:49:39 +00:00
self.0
.audio_key
.get_or_init(|| AudioKeyManager::new(self.weak()))
2017-01-18 20:39:46 +00:00
}
2017-01-18 18:41:22 +00:00
2017-01-19 22:45:24 +00:00
pub fn channel(&self) -> &ChannelManager {
2021-01-21 20:49:39 +00:00
self.0
.channel
.get_or_init(|| ChannelManager::new(self.weak()))
2017-01-19 22:45:24 +00:00
}
pub fn http_client(&self) -> &HttpClient {
&self.0.http_client
}
2017-01-18 21:33:52 +00:00
pub fn mercury(&self) -> &MercuryManager {
2021-01-21 20:49:39 +00:00
self.0
.mercury
.get_or_init(|| MercuryManager::new(self.weak()))
2017-01-18 21:33:52 +00:00
}
2021-06-19 20:47:39 +00:00
pub fn token_provider(&self) -> &TokenProvider {
self.0
.token_provider
.get_or_init(|| TokenProvider::new(self.weak()))
}
pub fn time_delta(&self) -> i64 {
2019-03-24 14:15:14 +00:00
self.0.data.read().unwrap().time_delta
}
2021-01-21 20:49:39 +00:00
pub fn spawn<T>(&self, task: T)
2018-02-11 11:37:08 +00:00
where
2021-01-21 20:49:39 +00:00
T: Future + Send + 'static,
T::Output: Send + 'static,
2017-01-19 22:45:24 +00:00
{
self.0.handle.spawn(task);
2017-01-19 22:45:24 +00:00
}
fn debug_info(&self) {
2018-02-11 11:37:08 +00:00
debug!(
"Session[{}] strong={} weak={}",
self.0.session_id,
Arc::strong_count(&self.0),
Arc::weak_count(&self.0)
);
}
fn dispatch(&self, cmd: u8, data: Bytes) {
use PacketType::*;
let packet_type = FromPrimitive::from_u8(cmd);
match packet_type {
Some(Ping) => {
let server_timestamp = BigEndian::read_u32(data.as_ref()) as i64;
2019-03-24 14:15:14 +00:00
let timestamp = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(dur) => dur,
Err(err) => err.duration(),
}
.as_secs() as i64;
2019-03-24 14:15:14 +00:00
self.0.data.write().unwrap().time_delta = server_timestamp - timestamp;
self.debug_info();
self.send_packet(Pong, vec![0, 0, 0, 0]);
2018-02-11 11:37:08 +00:00
}
Some(CountryCode) => {
2017-01-29 17:54:32 +00:00
let country = String::from_utf8(data.as_ref().to_owned()).unwrap();
2017-04-29 14:18:16 +00:00
info!("Country: {:?}", country);
2017-01-29 17:54:32 +00:00
self.0.data.write().unwrap().country = country;
2016-01-02 15:19:39 +00:00
}
Some(StreamChunkRes) | Some(ChannelError) => {
2021-06-23 19:26:52 +00:00
self.channel().dispatch(packet_type.unwrap(), data);
}
Some(AesKey) | Some(AesKeyError) => {
2021-06-23 19:26:52 +00:00
self.audio_key().dispatch(packet_type.unwrap(), data);
}
Some(MercuryReq) | Some(MercurySub) | Some(MercuryUnsub) | Some(MercuryEvent) => {
2021-06-23 19:26:52 +00:00
self.mercury().dispatch(packet_type.unwrap(), data);
}
2021-06-23 19:26:52 +00:00
Some(PongAck)
| Some(SecretBlock)
| Some(LegacyWelcome)
| Some(UnknownDataAllZeros)
| Some(ProductInfo)
| Some(LicenseVersion) => {}
_ => {
if let Some(packet_type) = PacketType::from_u8(cmd) {
2021-06-23 19:26:52 +00:00
trace!("Ignoring {:?} packet with data {:?}", packet_type, data);
} else {
trace!("Ignoring unknown packet {:x}", cmd);
}
}
}
}
pub fn send_packet(&self, cmd: PacketType, data: Vec<u8>) {
self.0.tx_connection.send((cmd as u8, data)).unwrap();
}
pub fn cache(&self) -> Option<&Arc<Cache>> {
self.0.cache.as_ref()
}
pub fn config(&self) -> &SessionConfig {
&self.0.config
}
pub fn username(&self) -> String {
self.0.data.read().unwrap().canonical_username.clone()
}
pub fn country(&self) -> String {
self.0.data.read().unwrap().country.clone()
}
pub fn device_id(&self) -> &str {
2017-01-18 17:07:20 +00:00
&self.config().device_id
}
2017-01-18 20:39:46 +00:00
2018-02-10 09:10:26 +00:00
fn weak(&self) -> SessionWeak {
2017-01-18 20:39:46 +00:00
SessionWeak(Arc::downgrade(&self.0))
}
pub fn session_id(&self) -> usize {
self.0.session_id
}
2018-04-21 15:46:29 +00:00
pub fn shutdown(&self) {
debug!("Invalidating session[{}]", self.0.session_id);
self.0.data.write().unwrap().invalid = true;
self.mercury().shutdown();
self.channel().shutdown();
2018-04-21 15:46:29 +00:00
}
pub fn is_invalid(&self) -> bool {
self.0.data.read().unwrap().invalid
}
2017-01-18 20:39:46 +00:00
}
#[derive(Clone)]
2018-02-12 19:13:37 +00:00
pub struct SessionWeak(Weak<SessionInternal>);
2017-01-18 20:39:46 +00:00
impl SessionWeak {
fn try_upgrade(&self) -> Option<Session> {
self.0.upgrade().map(Session)
}
pub(crate) fn upgrade(&self) -> Session {
self.try_upgrade().expect("Session died")
}
}
impl Drop for SessionInternal {
fn drop(&mut self) {
debug!("drop Session[{}]", self.session_id);
}
}
struct DispatchTask<S>(S, SessionWeak)
2018-02-11 11:37:08 +00:00
where
2021-01-21 20:49:39 +00:00
S: TryStream<Ok = (u8, Bytes)> + Unpin;
2018-02-11 11:37:08 +00:00
impl<S> Future for DispatchTask<S>
where
2021-01-21 20:49:39 +00:00
S: TryStream<Ok = (u8, Bytes)> + Unpin,
<S as TryStream>::Ok: std::fmt::Debug,
{
2021-01-21 20:49:39 +00:00
type Output = Result<(), S::Error>;
2021-01-21 20:49:39 +00:00
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let session = match self.1.try_upgrade() {
Some(session) => session,
2021-01-21 20:49:39 +00:00
None => return Poll::Ready(Ok(())),
};
loop {
2021-04-17 09:55:57 +00:00
let (cmd, data) = match ready!(self.0.try_poll_next_unpin(cx)) {
Some(Ok(t)) => t,
None => {
warn!("Connection to server closed.");
session.shutdown();
2021-01-21 20:49:39 +00:00
return Poll::Ready(Ok(()));
2020-01-24 01:26:16 +00:00
}
2021-04-17 09:55:57 +00:00
Some(Err(e)) => {
2018-04-21 15:46:29 +00:00
session.shutdown();
2021-01-21 20:49:39 +00:00
return Poll::Ready(Err(e));
2018-04-21 15:46:29 +00:00
}
};
2018-04-21 15:46:29 +00:00
session.dispatch(cmd, data);
}
}
}
2018-02-11 11:37:08 +00:00
impl<S> Drop for DispatchTask<S>
where
2021-01-21 20:49:39 +00:00
S: TryStream<Ok = (u8, Bytes)> + Unpin,
{
fn drop(&mut self) {
debug!("drop Dispatch");
2017-01-18 20:39:46 +00:00
}
2015-04-25 20:32:07 +00:00
}