2021-12-26 20:18:42 +00:00
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
future::Future,
|
|
|
|
io,
|
|
|
|
pin::Pin,
|
|
|
|
process::exit,
|
|
|
|
sync::{
|
|
|
|
atomic::{AtomicUsize, Ordering},
|
2021-12-26 21:55:45 +00:00
|
|
|
Arc, Weak,
|
2021-12-26 20:18:42 +00:00
|
|
|
},
|
|
|
|
task::{Context, Poll},
|
|
|
|
time::{SystemTime, UNIX_EPOCH},
|
|
|
|
};
|
2019-03-24 14:15:14 +00:00
|
|
|
|
|
|
|
use byteorder::{BigEndian, ByteOrder};
|
|
|
|
use bytes::Bytes;
|
2021-02-10 21:54:35 +00:00
|
|
|
use futures_core::TryStream;
|
2021-04-17 09:55:57 +00:00
|
|
|
use futures_util::{future, ready, StreamExt, TryStreamExt};
|
2021-06-22 21:57:38 +00:00
|
|
|
use num_traits::FromPrimitive;
|
2021-02-10 21:54:35 +00:00
|
|
|
use once_cell::sync::OnceCell;
|
2021-12-26 21:55:45 +00:00
|
|
|
use parking_lot::RwLock;
|
2021-12-10 23:03:35 +00:00
|
|
|
use quick_xml::events::Event;
|
2021-02-13 10:53:23 +00:00
|
|
|
use thiserror::Error;
|
2021-02-10 21:54:35 +00:00
|
|
|
use tokio::sync::mpsc;
|
|
|
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2021-12-26 20:18:42 +00:00
|
|
|
use crate::{
|
|
|
|
apresolve::ApResolver,
|
|
|
|
audio_key::AudioKeyManager,
|
|
|
|
authentication::Credentials,
|
|
|
|
cache::Cache,
|
|
|
|
channel::ChannelManager,
|
|
|
|
config::SessionConfig,
|
|
|
|
connection::{self, AuthenticationError},
|
|
|
|
http_client::HttpClient,
|
|
|
|
mercury::MercuryManager,
|
|
|
|
packet::PacketType,
|
|
|
|
spclient::SpClient,
|
|
|
|
token::TokenProvider,
|
|
|
|
Error,
|
|
|
|
};
|
2016-05-09 11:22:51 +00:00
|
|
|
|
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),
|
2022-01-16 00:14:00 +00:00
|
|
|
#[error("Session is not connected")]
|
|
|
|
NotConnected,
|
2021-12-26 20:18:42 +00:00
|
|
|
#[error("packet {0} unknown")]
|
|
|
|
Packet(u8),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<SessionError> for Error {
|
|
|
|
fn from(err: SessionError) -> Self {
|
|
|
|
match err {
|
|
|
|
SessionError::AuthenticationError(_) => Error::unauthenticated(err),
|
|
|
|
SessionError::IoError(_) => Error::unavailable(err),
|
2022-01-16 00:14:00 +00:00
|
|
|
SessionError::NotConnected => Error::unavailable(err),
|
2021-12-26 20:18:42 +00:00
|
|
|
SessionError::Packet(_) => Error::unimplemented(err),
|
|
|
|
}
|
|
|
|
}
|
2021-02-13 10:53:23 +00:00
|
|
|
}
|
2021-01-18 13:30:24 +00:00
|
|
|
|
2021-12-10 23:03:35 +00:00
|
|
|
pub type UserAttributes = HashMap<String, String>;
|
|
|
|
|
2021-12-11 19:22:44 +00:00
|
|
|
#[derive(Debug, Clone, Default)]
|
|
|
|
pub struct UserData {
|
|
|
|
pub country: String,
|
|
|
|
pub canonical_username: String,
|
|
|
|
pub attributes: UserAttributes,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Default)]
|
2018-02-12 19:10:36 +00:00
|
|
|
struct SessionData {
|
2022-01-12 21:09:57 +00:00
|
|
|
client_id: String,
|
2021-12-11 22:06:58 +00:00
|
|
|
connection_id: String,
|
2019-03-25 19:27:30 +00:00
|
|
|
time_delta: i64,
|
2018-04-21 15:46:29 +00:00
|
|
|
invalid: bool,
|
2021-12-11 19:22:44 +00:00
|
|
|
user_data: UserData,
|
2015-12-29 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 19:13:37 +00:00
|
|
|
struct SessionInternal {
|
2017-08-03 18:31:15 +00:00
|
|
|
config: SessionConfig,
|
2016-01-26 23:01:45 +00:00
|
|
|
data: RwLock<SessionData>,
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2021-06-20 21:09:27 +00:00
|
|
|
http_client: HttpClient,
|
2022-01-16 00:14:00 +00:00
|
|
|
tx_connection: OnceCell<mpsc::UnboundedSender<(u8, Vec<u8>)>>,
|
2017-01-18 20:39:46 +00:00
|
|
|
|
2021-06-21 21:49:37 +00:00
|
|
|
apresolver: OnceCell<ApResolver>,
|
2021-01-21 20:49:39 +00:00
|
|
|
audio_key: OnceCell<AudioKeyManager>,
|
|
|
|
channel: OnceCell<ChannelManager>,
|
|
|
|
mercury: OnceCell<MercuryManager>,
|
2021-11-26 22:21:27 +00:00
|
|
|
spclient: OnceCell<SpClient>,
|
2021-06-19 20:47:39 +00:00
|
|
|
token_provider: OnceCell<TokenProvider>,
|
2017-01-29 15:36:39 +00:00
|
|
|
cache: Option<Arc<Cache>>,
|
2017-01-19 22:45:24 +00:00
|
|
|
|
2021-02-20 21:14:15 +00:00
|
|
|
handle: tokio::runtime::Handle,
|
|
|
|
|
2017-02-22 04:17:04 +00:00
|
|
|
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);
|
2015-07-02 17:24:25 +00:00
|
|
|
|
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 {
|
2022-01-16 00:14:00 +00:00
|
|
|
pub fn new(config: SessionConfig, cache: Option<Cache>) -> Self {
|
2021-06-20 21:09:27 +00:00
|
|
|
let http_client = HttpClient::new(config.proxy.as_ref());
|
2017-01-20 02:37:02 +00:00
|
|
|
|
2022-01-16 00:14:00 +00:00
|
|
|
let session_id = SESSION_COUNTER.fetch_add(1, Ordering::AcqRel);
|
2017-02-22 04:17:04 +00:00
|
|
|
debug!("new Session[{}]", session_id);
|
2017-01-18 18:41:22 +00:00
|
|
|
|
2022-01-16 00:14:00 +00:00
|
|
|
Self(Arc::new(SessionInternal {
|
2021-03-01 02:37:22 +00:00
|
|
|
config,
|
2021-12-11 19:22:44 +00:00
|
|
|
data: RwLock::new(SessionData::default()),
|
2021-06-20 21:09:27 +00:00
|
|
|
http_client,
|
2022-01-16 00:14:00 +00:00
|
|
|
tx_connection: OnceCell::new(),
|
2017-01-29 15:36:39 +00:00
|
|
|
cache: cache.map(Arc::new),
|
2021-06-21 21:49:37 +00:00
|
|
|
apresolver: OnceCell::new(),
|
2021-01-21 20:49:39 +00:00
|
|
|
audio_key: OnceCell::new(),
|
|
|
|
channel: OnceCell::new(),
|
|
|
|
mercury: OnceCell::new(),
|
2021-11-26 22:21:27 +00:00
|
|
|
spclient: OnceCell::new(),
|
2021-06-19 20:47:39 +00:00
|
|
|
token_provider: OnceCell::new(),
|
2021-06-21 21:49:37 +00:00
|
|
|
handle: tokio::runtime::Handle::current(),
|
2021-03-01 02:37:22 +00:00
|
|
|
session_id,
|
2022-01-16 00:14:00 +00:00
|
|
|
}))
|
|
|
|
}
|
2016-03-13 20:45:31 +00:00
|
|
|
|
2022-01-16 00:14:00 +00:00
|
|
|
pub async fn connect(&self, credentials: Credentials) -> Result<(), Error> {
|
|
|
|
let ap = self.apresolver().resolve("accesspoint").await?;
|
2021-06-21 21:49:37 +00:00
|
|
|
info!("Connecting to AP \"{}:{}\"", ap.0, ap.1);
|
2022-01-16 00:14:00 +00:00
|
|
|
let mut transport = connection::connect(&ap.0, ap.1, self.config().proxy.as_ref()).await?;
|
2021-06-21 21:49:37 +00:00
|
|
|
|
|
|
|
let reusable_credentials =
|
2022-01-16 00:14:00 +00:00
|
|
|
connection::authenticate(&mut transport, credentials, &self.config().device_id).await?;
|
2021-06-21 21:49:37 +00:00
|
|
|
info!("Authenticated as \"{}\" !", reusable_credentials.username);
|
2022-01-16 00:14:00 +00:00
|
|
|
self.set_username(&reusable_credentials.username);
|
|
|
|
if let Some(cache) = self.cache() {
|
2021-06-21 21:49:37 +00:00
|
|
|
cache.save_credentials(&reusable_credentials);
|
|
|
|
}
|
|
|
|
|
2022-01-16 00:14:00 +00:00
|
|
|
let (tx_connection, rx_connection) = mpsc::unbounded_channel();
|
|
|
|
self.0
|
|
|
|
.tx_connection
|
|
|
|
.set(tx_connection)
|
|
|
|
.map_err(|_| SessionError::NotConnected)?;
|
|
|
|
|
2021-06-21 21:49:37 +00:00
|
|
|
let (sink, stream) = transport.split();
|
2022-01-16 00:14:00 +00:00
|
|
|
let sender_task = UnboundedReceiverStream::new(rx_connection)
|
2021-02-10 21:54:35 +00:00
|
|
|
.map(Ok)
|
|
|
|
.forward(sink);
|
2022-01-16 00:14:00 +00:00
|
|
|
let receiver_task = DispatchTask(stream, self.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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-01-16 00:14:00 +00:00
|
|
|
Ok(())
|
2021-06-21 21:49:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn apresolver(&self) -> &ApResolver {
|
|
|
|
self.0
|
|
|
|
.apresolver
|
|
|
|
.get_or_init(|| ApResolver::new(self.weak()))
|
2015-06-23 14:38:29 +00:00
|
|
|
}
|
2015-05-09 10:07:24 +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
|
|
|
}
|
|
|
|
|
2021-06-21 21:49:37 +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-11-26 22:21:27 +00:00
|
|
|
pub fn spclient(&self) -> &SpClient {
|
|
|
|
self.0.spclient.get_or_init(|| SpClient::new(self.weak()))
|
|
|
|
}
|
|
|
|
|
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()))
|
|
|
|
}
|
|
|
|
|
2019-03-25 19:27:30 +00:00
|
|
|
pub fn time_delta(&self) -> i64 {
|
2021-12-26 21:55:45 +00:00
|
|
|
self.0.data.read().time_delta
|
2019-03-24 14:15:14 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2021-02-20 21:14:15 +00:00
|
|
|
self.0.handle.spawn(task);
|
2017-01-19 22:45:24 +00:00
|
|
|
}
|
|
|
|
|
2017-02-22 04:17:04 +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)
|
|
|
|
);
|
2017-02-22 04:17:04 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 19:22:44 +00:00
|
|
|
fn check_catalogue(attributes: &UserAttributes) {
|
|
|
|
if let Some(account_type) = attributes.get("type") {
|
|
|
|
if account_type != "premium" {
|
|
|
|
error!("librespot does not support {:?} accounts.", account_type);
|
|
|
|
info!("Please support Spotify and your artists and sign up for a premium account.");
|
|
|
|
|
|
|
|
// TODO: logout instead of exiting
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-26 20:18:42 +00:00
|
|
|
fn dispatch(&self, cmd: u8, data: Bytes) -> Result<(), Error> {
|
2021-06-22 21:57:38 +00:00
|
|
|
use PacketType::*;
|
2021-12-26 20:18:42 +00:00
|
|
|
|
2021-06-22 21:57:38 +00:00
|
|
|
let packet_type = FromPrimitive::from_u8(cmd);
|
2021-12-26 20:18:42 +00:00
|
|
|
let cmd = match packet_type {
|
|
|
|
Some(cmd) => cmd,
|
|
|
|
None => {
|
|
|
|
trace!("Ignoring unknown packet {:x}", cmd);
|
|
|
|
return Err(SessionError::Packet(cmd).into());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-06-22 21:57:38 +00:00
|
|
|
match packet_type {
|
|
|
|
Some(Ping) => {
|
2019-03-25 19:27:30 +00:00
|
|
|
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(),
|
2019-03-25 19:27:30 +00:00
|
|
|
}
|
|
|
|
.as_secs() as i64;
|
2019-03-24 14:15:14 +00:00
|
|
|
|
2021-12-26 21:55:45 +00:00
|
|
|
self.0.data.write().time_delta = server_timestamp - timestamp;
|
2019-03-24 14:15:14 +00:00
|
|
|
|
2017-02-22 04:17:04 +00:00
|
|
|
self.debug_info();
|
2021-12-26 20:18:42 +00:00
|
|
|
self.send_packet(Pong, vec![0, 0, 0, 0])
|
2018-02-11 11:37:08 +00:00
|
|
|
}
|
2021-06-22 21:57:38 +00:00
|
|
|
Some(CountryCode) => {
|
2021-12-26 20:18:42 +00:00
|
|
|
let country = String::from_utf8(data.as_ref().to_owned())?;
|
2017-04-29 14:18:16 +00:00
|
|
|
info!("Country: {:?}", country);
|
2021-12-26 21:55:45 +00:00
|
|
|
self.0.data.write().user_data.country = country;
|
2021-12-26 20:18:42 +00:00
|
|
|
Ok(())
|
2016-01-02 15:19:39 +00:00
|
|
|
}
|
2021-12-26 20:18:42 +00:00
|
|
|
Some(StreamChunkRes) | Some(ChannelError) => self.channel().dispatch(cmd, data),
|
|
|
|
Some(AesKey) | Some(AesKeyError) => self.audio_key().dispatch(cmd, data),
|
2021-06-22 21:57:38 +00:00
|
|
|
Some(MercuryReq) | Some(MercurySub) | Some(MercuryUnsub) | Some(MercuryEvent) => {
|
2021-12-26 20:18:42 +00:00
|
|
|
self.mercury().dispatch(cmd, data)
|
2021-06-22 21:57:38 +00:00
|
|
|
}
|
2021-12-10 23:03:35 +00:00
|
|
|
Some(ProductInfo) => {
|
2021-12-26 20:18:42 +00:00
|
|
|
let data = std::str::from_utf8(&data)?;
|
2021-12-10 23:03:35 +00:00
|
|
|
let mut reader = quick_xml::Reader::from_str(data);
|
|
|
|
|
|
|
|
let mut buf = Vec::new();
|
|
|
|
let mut current_element = String::new();
|
|
|
|
let mut user_attributes: UserAttributes = HashMap::new();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
match reader.read_event(&mut buf) {
|
|
|
|
Ok(Event::Start(ref element)) => {
|
2021-12-26 20:18:42 +00:00
|
|
|
current_element = std::str::from_utf8(element.name())?.to_owned()
|
2021-12-10 23:03:35 +00:00
|
|
|
}
|
|
|
|
Ok(Event::End(_)) => {
|
|
|
|
current_element = String::new();
|
|
|
|
}
|
|
|
|
Ok(Event::Text(ref value)) => {
|
|
|
|
if !current_element.is_empty() {
|
|
|
|
let _ = user_attributes.insert(
|
|
|
|
current_element.clone(),
|
2021-12-26 20:18:42 +00:00
|
|
|
value.unescape_and_decode(&reader)?,
|
2021-12-10 23:03:35 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(Event::Eof) => break,
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => error!(
|
|
|
|
"Error parsing XML at position {}: {:?}",
|
|
|
|
reader.buffer_position(),
|
|
|
|
e
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-11 19:45:08 +00:00
|
|
|
trace!("Received product info: {:#?}", user_attributes);
|
2021-12-11 19:22:44 +00:00
|
|
|
Self::check_catalogue(&user_attributes);
|
|
|
|
|
2021-12-26 21:55:45 +00:00
|
|
|
self.0.data.write().user_data.attributes = user_attributes;
|
2021-12-26 20:18:42 +00:00
|
|
|
Ok(())
|
2021-12-10 23:03:35 +00:00
|
|
|
}
|
2021-06-23 19:26:52 +00:00
|
|
|
Some(PongAck)
|
|
|
|
| Some(SecretBlock)
|
|
|
|
| Some(LegacyWelcome)
|
|
|
|
| Some(UnknownDataAllZeros)
|
2021-12-26 20:18:42 +00:00
|
|
|
| Some(LicenseVersion) => Ok(()),
|
2021-06-22 21:57:38 +00:00
|
|
|
_ => {
|
2021-12-26 20:18:42 +00:00
|
|
|
trace!("Ignoring {:?} packet with data {:#?}", cmd, data);
|
|
|
|
Err(SessionError::Packet(cmd as u8).into())
|
2021-06-22 21:57:38 +00:00
|
|
|
}
|
2015-07-02 17:24:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-26 20:18:42 +00:00
|
|
|
pub fn send_packet(&self, cmd: PacketType, data: Vec<u8>) -> Result<(), Error> {
|
2022-01-16 00:14:00 +00:00
|
|
|
match self.0.tx_connection.get() {
|
|
|
|
Some(tx) => Ok(tx.send((cmd as u8, data))?),
|
|
|
|
None => Err(SessionError::NotConnected.into()),
|
|
|
|
}
|
2015-07-02 17:24:25 +00:00
|
|
|
}
|
2015-12-29 12:13:26 +00:00
|
|
|
|
2017-01-29 15:36:39 +00:00
|
|
|
pub fn cache(&self) -> Option<&Arc<Cache>> {
|
2016-03-16 04:07:04 +00:00
|
|
|
self.0.cache.as_ref()
|
|
|
|
}
|
|
|
|
|
2021-06-21 21:49:37 +00:00
|
|
|
pub fn config(&self) -> &SessionConfig {
|
2016-01-26 22:34:57 +00:00
|
|
|
&self.0.config
|
|
|
|
}
|
|
|
|
|
2021-12-30 21:36:38 +00:00
|
|
|
// This clones a fairly large struct, so use a specific getter or setter unless
|
|
|
|
// you need more fields at once, in which case this can spare multiple `read`
|
|
|
|
// locks.
|
2021-12-11 19:22:44 +00:00
|
|
|
pub fn user_data(&self) -> UserData {
|
2021-12-26 21:55:45 +00:00
|
|
|
self.0.data.read().user_data.clone()
|
2016-01-26 22:34:57 +00:00
|
|
|
}
|
|
|
|
|
2016-03-17 03:06:56 +00:00
|
|
|
pub fn device_id(&self) -> &str {
|
2017-01-18 17:07:20 +00:00
|
|
|
&self.config().device_id
|
2016-01-26 22:34:57 +00:00
|
|
|
}
|
2017-01-18 20:39:46 +00:00
|
|
|
|
2022-01-12 21:09:57 +00:00
|
|
|
pub fn client_id(&self) -> String {
|
|
|
|
self.0.data.read().client_id.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_client_id(&self, client_id: &str) {
|
|
|
|
self.0.data.write().client_id = client_id.to_owned();
|
|
|
|
}
|
|
|
|
|
2021-12-11 22:06:58 +00:00
|
|
|
pub fn connection_id(&self) -> String {
|
2021-12-26 21:55:45 +00:00
|
|
|
self.0.data.read().connection_id.clone()
|
2021-12-11 22:06:58 +00:00
|
|
|
}
|
|
|
|
|
2022-01-12 21:09:57 +00:00
|
|
|
pub fn set_connection_id(&self, connection_id: &str) {
|
|
|
|
self.0.data.write().connection_id = connection_id.to_owned();
|
2021-12-11 22:06:58 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 19:22:44 +00:00
|
|
|
pub fn username(&self) -> String {
|
2021-12-26 21:55:45 +00:00
|
|
|
self.0.data.read().user_data.canonical_username.clone()
|
2021-12-10 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2022-01-16 00:14:00 +00:00
|
|
|
pub fn set_username(&self, username: &str) {
|
|
|
|
self.0.data.write().user_data.canonical_username = username.to_owned();
|
|
|
|
}
|
|
|
|
|
2021-12-30 21:36:38 +00:00
|
|
|
pub fn country(&self) -> String {
|
|
|
|
self.0.data.read().user_data.country.clone()
|
|
|
|
}
|
|
|
|
|
2021-12-10 23:03:35 +00:00
|
|
|
pub fn set_user_attribute(&self, key: &str, value: &str) -> Option<String> {
|
2021-12-11 19:22:44 +00:00
|
|
|
let mut dummy_attributes = UserAttributes::new();
|
|
|
|
dummy_attributes.insert(key.to_owned(), value.to_owned());
|
|
|
|
Self::check_catalogue(&dummy_attributes);
|
|
|
|
|
2021-12-10 23:03:35 +00:00
|
|
|
self.0
|
|
|
|
.data
|
|
|
|
.write()
|
2021-12-11 19:22:44 +00:00
|
|
|
.user_data
|
|
|
|
.attributes
|
2021-12-10 23:03:35 +00:00
|
|
|
.insert(key.to_owned(), value.to_owned())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_user_attributes(&self, attributes: UserAttributes) {
|
2021-12-11 19:22:44 +00:00
|
|
|
Self::check_catalogue(&attributes);
|
|
|
|
|
2021-12-26 21:55:45 +00:00
|
|
|
self.0.data.write().user_data.attributes.extend(attributes)
|
2021-12-10 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2021-12-30 19:52:49 +00:00
|
|
|
pub fn get_user_attribute(&self, key: &str) -> Option<String> {
|
|
|
|
self.0
|
|
|
|
.data
|
|
|
|
.read()
|
|
|
|
.user_data
|
|
|
|
.attributes
|
|
|
|
.get(key)
|
|
|
|
.map(Clone::clone)
|
|
|
|
}
|
|
|
|
|
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))
|
|
|
|
}
|
2017-02-22 04:17:04 +00:00
|
|
|
|
|
|
|
pub fn session_id(&self) -> usize {
|
|
|
|
self.0.session_id
|
|
|
|
}
|
2018-04-21 15:46:29 +00:00
|
|
|
|
|
|
|
pub fn shutdown(&self) {
|
2021-12-26 21:55:45 +00:00
|
|
|
debug!("Invalidating session [{}]", self.0.session_id);
|
|
|
|
self.0.data.write().invalid = true;
|
2020-01-22 14:14:43 +00:00
|
|
|
self.mercury().shutdown();
|
2020-01-22 14:23:34 +00:00
|
|
|
self.channel().shutdown();
|
2018-04-21 15:46:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_invalid(&self) -> bool {
|
2021-12-26 21:55:45 +00:00
|
|
|
self.0.data.read().invalid
|
2018-04-21 15:46:29 +00:00
|
|
|
}
|
2017-01-18 20:39:46 +00:00
|
|
|
}
|
|
|
|
|
2017-02-22 04:17:04 +00:00
|
|
|
#[derive(Clone)]
|
2018-02-12 19:13:37 +00:00
|
|
|
pub struct SessionWeak(Weak<SessionInternal>);
|
2017-02-22 04:17:04 +00:00
|
|
|
|
2017-01-18 20:39:46 +00:00
|
|
|
impl SessionWeak {
|
2018-02-10 09:22:03 +00:00
|
|
|
fn try_upgrade(&self) -> Option<Session> {
|
2017-02-22 04:17:04 +00:00
|
|
|
self.0.upgrade().map(Session)
|
|
|
|
}
|
|
|
|
|
2018-02-10 09:22:03 +00:00
|
|
|
pub(crate) fn upgrade(&self) -> Session {
|
2021-12-26 21:55:45 +00:00
|
|
|
self.try_upgrade()
|
|
|
|
.expect("session was dropped and so should have this component")
|
2017-02-22 04:17:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2017-02-22 04:17:04 +00:00
|
|
|
|
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,
|
2017-02-22 04:17:04 +00:00
|
|
|
{
|
2021-01-21 20:49:39 +00:00
|
|
|
type Output = Result<(), S::Error>;
|
2017-02-22 04:17:04 +00:00
|
|
|
|
2021-01-21 20:49:39 +00:00
|
|
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
2017-02-22 04:17:04 +00:00
|
|
|
let session = match self.1.try_upgrade() {
|
|
|
|
Some(session) => session,
|
2021-01-21 20:49:39 +00:00
|
|
|
None => return Poll::Ready(Ok(())),
|
2017-02-22 04:17:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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 => {
|
2020-01-22 10:55:45 +00:00
|
|
|
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
|
|
|
}
|
2020-01-22 10:55:45 +00:00
|
|
|
};
|
2018-04-21 15:46:29 +00:00
|
|
|
|
2021-12-26 20:18:42 +00:00
|
|
|
if let Err(e) = session.dispatch(cmd, data) {
|
|
|
|
error!("could not dispatch command: {}", e);
|
|
|
|
}
|
2017-02-22 04:17:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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,
|
2017-02-22 04:17:04 +00:00
|
|
|
{
|
|
|
|
fn drop(&mut self) {
|
|
|
|
debug!("drop Dispatch");
|
2017-01-18 20:39:46 +00:00
|
|
|
}
|
2015-04-25 20:32:07 +00:00
|
|
|
}
|