librespot/core/src/session.rs

320 lines
8.6 KiB
Rust
Raw Normal View History

2017-01-19 22:45:24 +00:00
use std::io;
2019-07-08 08:08:32 +00:00
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, RwLock, Weak};
2019-03-24 14:15:14 +00:00
use std::time::{SystemTime, UNIX_EPOCH};
use byteorder::{BigEndian, ByteOrder};
use bytes::Bytes;
use futures::sync::mpsc;
use futures::{Async, Future, IntoFuture, Poll, Stream};
2017-01-19 22:45:24 +00:00
use tokio_core::reactor::{Handle, Remote};
2015-06-23 14:38:29 +00:00
use crate::apresolve::apresolve_or_fallback;
use crate::audio_key::AudioKeyManager;
use crate::authentication::Credentials;
use crate::cache::Cache;
use crate::channel::ChannelManager;
use crate::component::Lazy;
use crate::config::SessionConfig;
use crate::connection;
use crate::mercury::MercuryManager;
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
2017-01-20 02:37:02 +00:00
tx_connection: mpsc::UnboundedSender<(u8, Vec<u8>)>,
2017-01-18 20:39:46 +00:00
audio_key: Lazy<AudioKeyManager>,
2017-01-19 22:45:24 +00:00
channel: Lazy<ChannelManager>,
2017-01-18 21:33:52 +00:00
mercury: Lazy<MercuryManager>,
cache: Option<Arc<Cache>>,
2017-01-19 22:45:24 +00:00
handle: Remote,
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 {
2018-02-11 11:37:08 +00:00
pub fn connect(
config: SessionConfig,
credentials: Credentials,
cache: Option<Cache>,
handle: Handle,
) -> Box<dyn Future<Item = Session, Error = io::Error>> {
let access_point =
apresolve_or_fallback::<io::Error>(&handle, &config.proxy, &config.ap_port);
2017-01-18 18:41:22 +00:00
2017-01-19 22:45:24 +00:00
let handle_ = handle.clone();
let proxy = config.proxy.clone();
2017-01-18 18:41:22 +00:00
let connection = access_point.and_then(move |addr| {
info!("Connecting to AP \"{}\"", addr);
2018-03-23 15:52:24 +00:00
connection::connect(addr, &handle_, &proxy)
2017-01-18 18:41:22 +00:00
});
let device_id = config.device_id.clone();
let authentication = connection.and_then(move |connection| {
connection::authenticate(connection, credentials, device_id)
});
2017-01-18 18:41:22 +00:00
let result = authentication.map(move |(transport, reusable_credentials)| {
info!("Authenticated as \"{}\" !", reusable_credentials.username);
if let Some(ref cache) = cache {
cache.save_credentials(&reusable_credentials);
}
2017-01-18 18:41:22 +00:00
2017-01-19 22:45:24 +00:00
let (session, task) = Session::create(
2018-02-11 11:37:08 +00:00
&handle,
transport,
config,
cache,
reusable_credentials.username.clone(),
2017-01-19 22:45:24 +00:00
);
2018-04-21 15:46:29 +00:00
handle.spawn(task.map_err(|e| {
error!("{:?}", e);
}));
2017-01-20 12:56:42 +00:00
session
2017-01-18 18:41:22 +00:00
});
2018-02-11 11:37:08 +00:00
2017-01-18 18:41:22 +00:00
Box::new(result)
}
2018-02-11 11:37:08 +00:00
fn create(
handle: &Handle,
transport: connection::Transport,
config: SessionConfig,
cache: Option<Cache>,
username: String,
) -> (Session, Box<dyn Future<Item = (), Error = io::Error>>) {
2017-01-20 02:37:02 +00:00
let (sink, stream) = transport.split();
let (sender_tx, sender_rx) = mpsc::unbounded();
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 {
2016-01-01 23:16:12 +00:00
config: config,
data: RwLock::new(SessionData {
country: String::new(),
2017-01-18 18:41:22 +00:00
canonical_username: username,
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
}),
2017-01-20 02:37:02 +00:00
tx_connection: sender_tx,
2016-01-01 23:16:12 +00:00
cache: cache.map(Arc::new),
2017-01-18 20:39:46 +00:00
audio_key: Lazy::new(),
2017-01-19 22:45:24 +00:00
channel: Lazy::new(),
2017-01-18 21:33:52 +00:00
mercury: Lazy::new(),
2017-01-19 22:45:24 +00:00
handle: handle.remote().clone(),
session_id: session_id,
2017-01-18 18:41:22 +00:00
}));
let sender_task = sender_rx
.map_err(|e| -> io::Error { panic!(e) })
2018-02-11 11:37:08 +00:00
.forward(sink)
.map(|_| ());
let receiver_task = DispatchTask(stream, session.weak());
2017-01-20 02:37:02 +00:00
let task = Box::new(
(receiver_task, sender_task)
.into_future()
.map(|((), ())| ()),
);
2017-01-20 02:37:02 +00:00
2017-01-18 18:41:22 +00:00
(session, task)
2015-06-23 14:38:29 +00:00
}
2017-01-18 20:39:46 +00:00
pub fn audio_key(&self) -> &AudioKeyManager {
self.0.audio_key.get(|| AudioKeyManager::new(self.weak()))
}
2017-01-18 18:41:22 +00:00
2017-01-19 22:45:24 +00:00
pub fn channel(&self) -> &ChannelManager {
self.0.channel.get(|| ChannelManager::new(self.weak()))
}
2017-01-18 21:33:52 +00:00
pub fn mercury(&self) -> &MercuryManager {
self.0.mercury.get(|| MercuryManager::new(self.weak()))
}
pub fn time_delta(&self) -> i64 {
2019-03-24 14:15:14 +00:00
self.0.data.read().unwrap().time_delta
}
2017-01-19 22:45:24 +00:00
pub fn spawn<F, R>(&self, f: F)
2018-02-11 11:37:08 +00:00
where
F: FnOnce(&Handle) -> R + Send + 'static,
R: IntoFuture<Item = (), Error = ()>,
R::Future: 'static,
2017-01-19 22:45:24 +00:00
{
self.0.handle.spawn(f)
}
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)
);
}
2017-01-29 16:25:09 +00:00
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
fn dispatch(&self, cmd: u8, data: Bytes) {
match cmd {
0x4 => {
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();
2019-03-24 14:15:14 +00:00
self.send_packet(0x49, vec![0, 0, 0, 0]);
2018-02-11 11:37:08 +00:00
}
0x4a => (),
0x1b => {
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
}
2017-01-18 21:33:52 +00:00
2017-01-19 22:45:24 +00:00
0x9 | 0xa => self.channel().dispatch(cmd, data),
2017-01-18 21:33:52 +00:00
0xd | 0xe => self.audio_key().dispatch(cmd, data),
0xb2..=0xb6 => self.mercury().dispatch(cmd, data),
2016-01-02 15:19:39 +00:00
_ => (),
}
}
2017-01-18 05:57:36 +00:00
pub fn send_packet(&self, cmd: u8, data: Vec<u8>) {
self.0.tx_connection.unbounded_send((cmd, data)).unwrap();
}
pub fn cache(&self) -> Option<&Arc<Cache>> {
self.0.cache.as_ref()
}
2018-02-10 08:52:30 +00:00
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
S: Stream<Item = (u8, Bytes)>;
2018-02-11 11:37:08 +00:00
impl<S> Future for DispatchTask<S>
where
S: Stream<Item = (u8, Bytes)>,
2018-04-21 15:46:29 +00:00
<S as Stream>::Error: ::std::fmt::Debug,
{
type Item = ();
type Error = S::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let session = match self.1.try_upgrade() {
Some(session) => session,
2018-02-11 11:37:08 +00:00
None => return Ok(Async::Ready(())),
};
loop {
2018-04-21 15:46:29 +00:00
let (cmd, data) = match self.0.poll() {
Ok(Async::Ready(Some(t))) => t,
Ok(Async::Ready(None)) => {
warn!("Connection to server closed.");
session.shutdown();
return Ok(Async::Ready(()));
},
2018-04-21 15:46:29 +00:00
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(e) => {
session.shutdown();
return Err(From::from(e));
}
};
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
S: Stream<Item = (u8, Bytes)>,
{
fn drop(&mut self) {
debug!("drop Dispatch");
2017-01-18 20:39:46 +00:00
}
2015-04-25 20:32:07 +00:00
}