mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
core: retry Session access-point connection (#1345)
Tries multiple access-points with a retry for each one, unless there was a login failure.
This commit is contained in:
parent
67d31959f5
commit
e02e4ae224
3 changed files with 48 additions and 7 deletions
|
@ -56,7 +56,8 @@ https://github.com/librespot-org/librespot
|
||||||
- [core] `FileId` is moved out of `SpotifyId`. For now it will be re-exported.
|
- [core] `FileId` is moved out of `SpotifyId`. For now it will be re-exported.
|
||||||
- [core] Report actual platform data on login
|
- [core] Report actual platform data on login
|
||||||
- [core] Support `Session` authentication with a Spotify access token
|
- [core] Support `Session` authentication with a Spotify access token
|
||||||
- [core] `Credentials.username` is now an `Option` (breaking)
|
- [core] `Credentials.username` is now an `Option` (breaking)
|
||||||
|
- [core] `Session::connect` tries multiple access points, retrying each one.
|
||||||
- [main] `autoplay {on|off}` now acts as an override. If unspecified, `librespot`
|
- [main] `autoplay {on|off}` now acts as an override. If unspecified, `librespot`
|
||||||
now follows the setting in the Connect client that controls it. (breaking)
|
now follows the setting in the Connect client that controls it. (breaking)
|
||||||
- [metadata] Most metadata is now retrieved with the `spclient` (breaking)
|
- [metadata] Most metadata is now retrieved with the `spclient` (breaking)
|
||||||
|
|
|
@ -68,6 +68,29 @@ pub async fn connect(host: &str, port: u16, proxy: Option<&Url>) -> io::Result<T
|
||||||
handshake(socket).await
|
handshake(socket).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn connect_with_retry(
|
||||||
|
host: &str,
|
||||||
|
port: u16,
|
||||||
|
proxy: Option<&Url>,
|
||||||
|
max_retries: u8,
|
||||||
|
) -> io::Result<Transport> {
|
||||||
|
let mut num_retries = 0;
|
||||||
|
loop {
|
||||||
|
match connect(host, port, proxy).await {
|
||||||
|
Ok(f) => return Ok(f),
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Connection failed: {e}");
|
||||||
|
if num_retries < max_retries {
|
||||||
|
num_retries += 1;
|
||||||
|
debug!("Retry access point...");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn authenticate(
|
pub async fn authenticate(
|
||||||
transport: &mut Transport,
|
transport: &mut Transport,
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
|
|
|
@ -144,13 +144,15 @@ impl Session {
|
||||||
|
|
||||||
async fn connect_inner(
|
async fn connect_inner(
|
||||||
&self,
|
&self,
|
||||||
access_point: SocketAddress,
|
access_point: &SocketAddress,
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
) -> Result<(Credentials, Transport), Error> {
|
) -> Result<(Credentials, Transport), Error> {
|
||||||
let mut transport = connection::connect(
|
const MAX_RETRIES: u8 = 1;
|
||||||
|
let mut transport = connection::connect_with_retry(
|
||||||
&access_point.0,
|
&access_point.0,
|
||||||
access_point.1,
|
access_point.1,
|
||||||
self.config().proxy.as_ref(),
|
self.config().proxy.as_ref(),
|
||||||
|
MAX_RETRIES,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let mut reusable_credentials = connection::authenticate(
|
let mut reusable_credentials = connection::authenticate(
|
||||||
|
@ -165,10 +167,11 @@ impl Session {
|
||||||
trace!(
|
trace!(
|
||||||
"Reconnect using stored credentials as token authed sessions cannot use keymaster."
|
"Reconnect using stored credentials as token authed sessions cannot use keymaster."
|
||||||
);
|
);
|
||||||
transport = connection::connect(
|
transport = connection::connect_with_retry(
|
||||||
&access_point.0,
|
&access_point.0,
|
||||||
access_point.1,
|
access_point.1,
|
||||||
self.config().proxy.as_ref(),
|
self.config().proxy.as_ref(),
|
||||||
|
MAX_RETRIES,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
reusable_credentials = connection::authenticate(
|
reusable_credentials = connection::authenticate(
|
||||||
|
@ -187,19 +190,32 @@ impl Session {
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
store_credentials: bool,
|
store_credentials: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
// There currently happen to be 6 APs but anything will do to avoid an infinite loop.
|
||||||
|
const MAX_AP_TRIES: u8 = 6;
|
||||||
|
let mut num_ap_tries = 0;
|
||||||
let (reusable_credentials, transport) = loop {
|
let (reusable_credentials, transport) = loop {
|
||||||
let ap = self.apresolver().resolve("accesspoint").await?;
|
let ap = self.apresolver().resolve("accesspoint").await?;
|
||||||
info!("Connecting to AP \"{}:{}\"", ap.0, ap.1);
|
info!("Connecting to AP \"{}:{}\"", ap.0, ap.1);
|
||||||
match self.connect_inner(ap, credentials.clone()).await {
|
match self.connect_inner(&ap, credentials.clone()).await {
|
||||||
Ok(ct) => break ct,
|
Ok(ct) => break ct,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
num_ap_tries += 1;
|
||||||
|
if MAX_AP_TRIES == num_ap_tries {
|
||||||
|
error!("Tried too many access points");
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
if let Some(AuthenticationError::LoginFailed(ErrorCode::TryAnotherAP)) =
|
if let Some(AuthenticationError::LoginFailed(ErrorCode::TryAnotherAP)) =
|
||||||
e.error.downcast_ref::<AuthenticationError>()
|
e.error.downcast_ref::<AuthenticationError>()
|
||||||
{
|
{
|
||||||
warn!("Instructed to try another access point...");
|
warn!("Instructed to try another access point...");
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else if let Some(AuthenticationError::LoginFailed(..)) =
|
||||||
|
e.error.downcast_ref::<AuthenticationError>()
|
||||||
|
{
|
||||||
return Err(e);
|
return Err(e);
|
||||||
|
} else {
|
||||||
|
warn!("Try another access point...");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -567,7 +583,7 @@ impl Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shutdown(&self) {
|
pub fn shutdown(&self) {
|
||||||
debug!("Invalidating session");
|
debug!("Shutdown: Invalidating session");
|
||||||
self.0.data.write().invalid = true;
|
self.0.data.write().invalid = true;
|
||||||
self.mercury().shutdown();
|
self.mercury().shutdown();
|
||||||
self.channel().shutdown();
|
self.channel().shutdown();
|
||||||
|
@ -624,6 +640,7 @@ where
|
||||||
return Poll::Ready(Ok(()));
|
return Poll::Ready(Ok(()));
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
|
error!("Connection to server closed.");
|
||||||
session.shutdown();
|
session.shutdown();
|
||||||
return Poll::Ready(Err(e));
|
return Poll::Ready(Err(e));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue