Ensure client reconnects if an error occurs during authentication (#35629)
In #35471, we added a new `AuthenticationError` variant to the client enum `Status`, but the reconnection logic was ignoring it when determining whether to reconnect. This pull request fixes that regression and introduces test coverage for this case. Release Notes: - N/A
This commit is contained in:
parent
6c83a3bcde
commit
efba4364fd
1 changed files with 64 additions and 17 deletions
|
@ -687,7 +687,10 @@ impl Client {
|
|||
}
|
||||
}
|
||||
|
||||
if matches!(*client.status().borrow(), Status::ConnectionError) {
|
||||
if matches!(
|
||||
*client.status().borrow(),
|
||||
Status::AuthenticationError | Status::ConnectionError
|
||||
) {
|
||||
client.set_status(
|
||||
Status::ReconnectionError {
|
||||
next_reconnection: Instant::now() + delay,
|
||||
|
@ -856,28 +859,14 @@ impl Client {
|
|||
|
||||
let old_credentials = self.state.read().credentials.clone();
|
||||
if let Some(old_credentials) = old_credentials {
|
||||
if self
|
||||
.cloud_client
|
||||
.validate_credentials(
|
||||
old_credentials.user_id as u32,
|
||||
&old_credentials.access_token,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
if self.validate_credentials(&old_credentials, cx).await? {
|
||||
credentials = Some(old_credentials);
|
||||
}
|
||||
}
|
||||
|
||||
if credentials.is_none() && try_provider {
|
||||
if let Some(stored_credentials) = self.credentials_provider.read_credentials(cx).await {
|
||||
if self
|
||||
.cloud_client
|
||||
.validate_credentials(
|
||||
stored_credentials.user_id as u32,
|
||||
&stored_credentials.access_token,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
if self.validate_credentials(&stored_credentials, cx).await? {
|
||||
credentials = Some(stored_credentials);
|
||||
} else {
|
||||
self.credentials_provider
|
||||
|
@ -926,6 +915,24 @@ impl Client {
|
|||
Ok(credentials)
|
||||
}
|
||||
|
||||
async fn validate_credentials(
|
||||
self: &Arc<Self>,
|
||||
credentials: &Credentials,
|
||||
cx: &AsyncApp,
|
||||
) -> Result<bool> {
|
||||
match self
|
||||
.cloud_client
|
||||
.validate_credentials(credentials.user_id as u32, &credentials.access_token)
|
||||
.await
|
||||
{
|
||||
Ok(valid) => Ok(valid),
|
||||
Err(err) => {
|
||||
self.set_status(Status::AuthenticationError, cx);
|
||||
Err(anyhow!("failed to validate credentials: {}", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a sign-in and also connects to Collab.
|
||||
///
|
||||
/// This is called in places where we *don't* need to connect in the future. We will replace these calls with calls
|
||||
|
@ -1733,6 +1740,46 @@ mod tests {
|
|||
assert_eq!(server.auth_count(), 2); // Client re-authenticated due to an invalid token
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_auth_failure_during_reconnection(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let http_client = FakeHttpClient::with_200_response();
|
||||
let client =
|
||||
cx.update(|cx| Client::new(Arc::new(FakeSystemClock::new()), http_client.clone(), cx));
|
||||
let server = FakeServer::for_client(42, &client, cx).await;
|
||||
let mut status = client.status();
|
||||
assert!(matches!(
|
||||
status.next().await,
|
||||
Some(Status::Connected { .. })
|
||||
));
|
||||
assert_eq!(server.auth_count(), 1);
|
||||
|
||||
// Simulate an auth failure during reconnection.
|
||||
http_client
|
||||
.as_fake()
|
||||
.replace_handler(|_, _request| async move {
|
||||
Ok(http_client::Response::builder()
|
||||
.status(503)
|
||||
.body("".into())
|
||||
.unwrap())
|
||||
});
|
||||
server.disconnect();
|
||||
while !matches!(status.next().await, Some(Status::ReconnectionError { .. })) {}
|
||||
|
||||
// Restore the ability to authenticate.
|
||||
http_client
|
||||
.as_fake()
|
||||
.replace_handler(|_, _request| async move {
|
||||
Ok(http_client::Response::builder()
|
||||
.status(200)
|
||||
.body("".into())
|
||||
.unwrap())
|
||||
});
|
||||
cx.executor().advance_clock(Duration::from_secs(10));
|
||||
while !matches!(status.next().await, Some(Status::Connected { .. })) {}
|
||||
assert_eq!(server.auth_count(), 1); // Client reused the cached credentials when reconnecting
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_connection_timeout(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue