Skip to content

Commit e02e4ae

Browse files
authored
core: retry Session access-point connection (#1345)
Tries multiple access-points with a retry for each one, unless there was a login failure.
1 parent 67d3195 commit e02e4ae

3 files changed

Lines changed: 48 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ https://github.com/librespot-org/librespot
5656
- [core] `FileId` is moved out of `SpotifyId`. For now it will be re-exported.
5757
- [core] Report actual platform data on login
5858
- [core] Support `Session` authentication with a Spotify access token
59-
- [core] `Credentials.username` is now an `Option` (breaking)
59+
- [core] `Credentials.username` is now an `Option` (breaking)
60+
- [core] `Session::connect` tries multiple access points, retrying each one.
6061
- [main] `autoplay {on|off}` now acts as an override. If unspecified, `librespot`
6162
now follows the setting in the Connect client that controls it. (breaking)
6263
- [metadata] Most metadata is now retrieved with the `spclient` (breaking)

core/src/connection/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,29 @@ pub async fn connect(host: &str, port: u16, proxy: Option<&Url>) -> io::Result<T
6868
handshake(socket).await
6969
}
7070

71+
pub async fn connect_with_retry(
72+
host: &str,
73+
port: u16,
74+
proxy: Option<&Url>,
75+
max_retries: u8,
76+
) -> io::Result<Transport> {
77+
let mut num_retries = 0;
78+
loop {
79+
match connect(host, port, proxy).await {
80+
Ok(f) => return Ok(f),
81+
Err(e) => {
82+
debug!("Connection failed: {e}");
83+
if num_retries < max_retries {
84+
num_retries += 1;
85+
debug!("Retry access point...");
86+
continue;
87+
}
88+
return Err(e);
89+
}
90+
}
91+
}
92+
}
93+
7194
pub async fn authenticate(
7295
transport: &mut Transport,
7396
credentials: Credentials,

core/src/session.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,15 @@ impl Session {
144144

145145
async fn connect_inner(
146146
&self,
147-
access_point: SocketAddress,
147+
access_point: &SocketAddress,
148148
credentials: Credentials,
149149
) -> Result<(Credentials, Transport), Error> {
150-
let mut transport = connection::connect(
150+
const MAX_RETRIES: u8 = 1;
151+
let mut transport = connection::connect_with_retry(
151152
&access_point.0,
152153
access_point.1,
153154
self.config().proxy.as_ref(),
155+
MAX_RETRIES,
154156
)
155157
.await?;
156158
let mut reusable_credentials = connection::authenticate(
@@ -165,10 +167,11 @@ impl Session {
165167
trace!(
166168
"Reconnect using stored credentials as token authed sessions cannot use keymaster."
167169
);
168-
transport = connection::connect(
170+
transport = connection::connect_with_retry(
169171
&access_point.0,
170172
access_point.1,
171173
self.config().proxy.as_ref(),
174+
MAX_RETRIES,
172175
)
173176
.await?;
174177
reusable_credentials = connection::authenticate(
@@ -187,19 +190,32 @@ impl Session {
187190
credentials: Credentials,
188191
store_credentials: bool,
189192
) -> Result<(), Error> {
193+
// There currently happen to be 6 APs but anything will do to avoid an infinite loop.
194+
const MAX_AP_TRIES: u8 = 6;
195+
let mut num_ap_tries = 0;
190196
let (reusable_credentials, transport) = loop {
191197
let ap = self.apresolver().resolve("accesspoint").await?;
192198
info!("Connecting to AP \"{}:{}\"", ap.0, ap.1);
193-
match self.connect_inner(ap, credentials.clone()).await {
199+
match self.connect_inner(&ap, credentials.clone()).await {
194200
Ok(ct) => break ct,
195201
Err(e) => {
202+
num_ap_tries += 1;
203+
if MAX_AP_TRIES == num_ap_tries {
204+
error!("Tried too many access points");
205+
return Err(e);
206+
}
196207
if let Some(AuthenticationError::LoginFailed(ErrorCode::TryAnotherAP)) =
197208
e.error.downcast_ref::<AuthenticationError>()
198209
{
199210
warn!("Instructed to try another access point...");
200211
continue;
201-
} else {
212+
} else if let Some(AuthenticationError::LoginFailed(..)) =
213+
e.error.downcast_ref::<AuthenticationError>()
214+
{
202215
return Err(e);
216+
} else {
217+
warn!("Try another access point...");
218+
continue;
203219
}
204220
}
205221
}
@@ -567,7 +583,7 @@ impl Session {
567583
}
568584

569585
pub fn shutdown(&self) {
570-
debug!("Invalidating session");
586+
debug!("Shutdown: Invalidating session");
571587
self.0.data.write().invalid = true;
572588
self.mercury().shutdown();
573589
self.channel().shutdown();
@@ -624,6 +640,7 @@ where
624640
return Poll::Ready(Ok(()));
625641
}
626642
Some(Err(e)) => {
643+
error!("Connection to server closed.");
627644
session.shutdown();
628645
return Poll::Ready(Err(e));
629646
}

0 commit comments

Comments
 (0)