mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-15 07:28:30 +00:00
Respect 429 in SVR write requests.
This commit is contained in:
committed by
jeffrey-signal
parent
e3b569ca5b
commit
4b41989b30
@@ -9,6 +9,7 @@ import org.signal.core.util.logging.Log
|
|||||||
import org.thoughtcrime.securesms.BuildConfig
|
import org.thoughtcrime.securesms.BuildConfig
|
||||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Job.Result
|
||||||
import org.thoughtcrime.securesms.jobmanager.JsonJobData
|
import org.thoughtcrime.securesms.jobmanager.JsonJobData
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
@@ -20,6 +21,7 @@ import org.whispersystems.signalservice.api.svr.SecureValueRecovery.PinChangeSes
|
|||||||
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
||||||
import kotlin.concurrent.withLock
|
import kotlin.concurrent.withLock
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to reset the guess on the SVR PIN. Intended to be enqueued after a successful restore.
|
* Attempts to reset the guess on the SVR PIN. Intended to be enqueued after a successful restore.
|
||||||
@@ -163,6 +165,11 @@ class ResetSvrGuessCountJob private constructor(
|
|||||||
Log.w(TAG, "Failed to expose the backup. Giving up. $svr")
|
Log.w(TAG, "Failed to expose the backup. Giving up. $svr")
|
||||||
Result.success()
|
Result.success()
|
||||||
}
|
}
|
||||||
|
is BackupResponse.RateLimited -> {
|
||||||
|
val backoff = response.retryAfter ?: defaultBackoff().milliseconds
|
||||||
|
Log.w(TAG, "Hit rate limit. Retrying in $backoff")
|
||||||
|
Result.retry(backoff.inWholeMilliseconds)
|
||||||
|
}
|
||||||
is BackupResponse.NetworkError -> {
|
is BackupResponse.NetworkError -> {
|
||||||
Log.w(TAG, "Hit a network error. Retrying. $svr", response.exception)
|
Log.w(TAG, "Hit a network error. Retrying. $svr", response.exception)
|
||||||
Result.retry(defaultBackoff())
|
Result.retry(defaultBackoff())
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.whispersystems.signalservice.api.svr.SecureValueRecovery.PinChangeSes
|
|||||||
import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV2
|
import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV2
|
||||||
import kotlin.concurrent.withLock
|
import kotlin.concurrent.withLock
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures a user's SVR data is written to SVR2.
|
* Ensures a user's SVR data is written to SVR2.
|
||||||
@@ -109,6 +110,11 @@ class Svr2MirrorJob private constructor(parameters: Parameters, private var seri
|
|||||||
Log.w(TAG, "Failed to expose the backup. Giving up.")
|
Log.w(TAG, "Failed to expose the backup. Giving up.")
|
||||||
Result.success()
|
Result.success()
|
||||||
}
|
}
|
||||||
|
is BackupResponse.RateLimited -> {
|
||||||
|
val backoff = response.retryAfter ?: defaultBackoff().milliseconds
|
||||||
|
Log.w(TAG, "Hit rate limit. Retrying in $backoff")
|
||||||
|
Result.retry(backoff.inWholeMilliseconds)
|
||||||
|
}
|
||||||
is BackupResponse.NetworkError -> {
|
is BackupResponse.NetworkError -> {
|
||||||
Log.w(TAG, "Hit a network error. Retrying.", response.exception)
|
Log.w(TAG, "Hit a network error. Retrying.", response.exception)
|
||||||
Result.retry(defaultBackoff())
|
Result.retry(defaultBackoff())
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.whispersystems.signalservice.api.svr.SecureValueRecovery.PinChangeSes
|
|||||||
import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV3
|
import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV3
|
||||||
import kotlin.concurrent.withLock
|
import kotlin.concurrent.withLock
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures a user's SVR data is written to SVR3.
|
* Ensures a user's SVR data is written to SVR3.
|
||||||
@@ -93,6 +94,11 @@ class Svr3MirrorJob private constructor(parameters: Parameters, private var seri
|
|||||||
Result.retry(defaultBackoff())
|
Result.retry(defaultBackoff())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is BackupResponse.RateLimited -> {
|
||||||
|
val backoff = response.retryAfter ?: defaultBackoff().milliseconds
|
||||||
|
Log.w(TAG, "Hit rate limit. Retrying in $backoff")
|
||||||
|
Result.retry(backoff.inWholeMilliseconds)
|
||||||
|
}
|
||||||
BackupResponse.EnclaveNotFound -> {
|
BackupResponse.EnclaveNotFound -> {
|
||||||
Log.w(TAG, "Could not find the enclave. Giving up.")
|
Log.w(TAG, "Could not find the enclave. Giving up.")
|
||||||
Result.success()
|
Result.success()
|
||||||
|
|||||||
@@ -257,6 +257,7 @@ object SvrRepository {
|
|||||||
BackupResponse.ExposeFailure -> it
|
BackupResponse.ExposeFailure -> it
|
||||||
is BackupResponse.NetworkError -> it
|
is BackupResponse.NetworkError -> it
|
||||||
BackupResponse.ServerRejected -> it
|
BackupResponse.ServerRejected -> it
|
||||||
|
is BackupResponse.RateLimited -> it
|
||||||
BackupResponse.EnclaveNotFound -> null
|
BackupResponse.EnclaveNotFound -> null
|
||||||
is BackupResponse.Success -> null
|
is BackupResponse.Success -> null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.signal.registration.NetworkController.RegisterAccountError
|
|||||||
import org.signal.registration.NetworkController.RegisterAccountResponse
|
import org.signal.registration.NetworkController.RegisterAccountResponse
|
||||||
import org.signal.registration.NetworkController.RegistrationLockResponse
|
import org.signal.registration.NetworkController.RegistrationLockResponse
|
||||||
import org.signal.registration.NetworkController.RegistrationNetworkResult
|
import org.signal.registration.NetworkController.RegistrationNetworkResult
|
||||||
|
import org.signal.registration.NetworkController.RegistrationNetworkResult.*
|
||||||
import org.signal.registration.NetworkController.RequestVerificationCodeError
|
import org.signal.registration.NetworkController.RequestVerificationCodeError
|
||||||
import org.signal.registration.NetworkController.SessionMetadata
|
import org.signal.registration.NetworkController.SessionMetadata
|
||||||
import org.signal.registration.NetworkController.SubmitVerificationCodeError
|
import org.signal.registration.NetworkController.SubmitVerificationCodeError
|
||||||
@@ -465,27 +466,30 @@ class RealNetworkController(
|
|||||||
when (response) {
|
when (response) {
|
||||||
is BackupResponse.Success -> {
|
is BackupResponse.Success -> {
|
||||||
Log.i(TAG, "[backupMasterKeyToSvr] Successfully backed up master key to SVR2")
|
Log.i(TAG, "[backupMasterKeyToSvr] Successfully backed up master key to SVR2")
|
||||||
RegistrationNetworkResult.Success(Unit)
|
Success(Unit)
|
||||||
}
|
}
|
||||||
is BackupResponse.ApplicationError -> {
|
is BackupResponse.ApplicationError -> {
|
||||||
Log.w(TAG, "[backupMasterKeyToSvr] Application error", response.exception)
|
Log.w(TAG, "[backupMasterKeyToSvr] Application error", response.exception)
|
||||||
RegistrationNetworkResult.ApplicationError(response.exception)
|
ApplicationError(response.exception)
|
||||||
}
|
}
|
||||||
is BackupResponse.NetworkError -> {
|
is BackupResponse.NetworkError -> {
|
||||||
Log.w(TAG, "[backupMasterKeyToSvr] Network error", response.exception)
|
Log.w(TAG, "[backupMasterKeyToSvr] Network error", response.exception)
|
||||||
RegistrationNetworkResult.NetworkError(response.exception)
|
NetworkError(response.exception)
|
||||||
}
|
}
|
||||||
is BackupResponse.EnclaveNotFound -> {
|
is BackupResponse.EnclaveNotFound -> {
|
||||||
Log.w(TAG, "[backupMasterKeyToSvr] Enclave not found")
|
Log.w(TAG, "[backupMasterKeyToSvr] Enclave not found")
|
||||||
RegistrationNetworkResult.Failure(NetworkController.BackupMasterKeyError.EnclaveNotFound)
|
Failure(NetworkController.BackupMasterKeyError.EnclaveNotFound)
|
||||||
}
|
}
|
||||||
is BackupResponse.ExposeFailure -> {
|
is BackupResponse.ExposeFailure -> {
|
||||||
Log.w(TAG, "[backupMasterKeyToSvr] Expose failure -- per spec, treat as success.")
|
Log.w(TAG, "[backupMasterKeyToSvr] Expose failure -- per spec, treat as success.")
|
||||||
RegistrationNetworkResult.Success(Unit)
|
Success(Unit)
|
||||||
}
|
}
|
||||||
is BackupResponse.ServerRejected -> {
|
is BackupResponse.ServerRejected -> {
|
||||||
Log.w(TAG, "[backupMasterKeyToSvr] Server rejected")
|
Log.w(TAG, "[backupMasterKeyToSvr] Server rejected")
|
||||||
RegistrationNetworkResult.NetworkError(IOException("Server rejected backup request"))
|
NetworkError(IOException("Server rejected backup request"))
|
||||||
|
}
|
||||||
|
is BackupResponse.RateLimited -> {
|
||||||
|
NetworkError(IOException("Rate limited"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.signal.core.models.MasterKey
|
|||||||
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.jvm.Throws
|
import kotlin.jvm.Throws
|
||||||
|
import kotlin.time.Duration
|
||||||
|
|
||||||
interface SecureValueRecovery {
|
interface SecureValueRecovery {
|
||||||
|
|
||||||
@@ -88,6 +89,9 @@ interface SecureValueRecovery {
|
|||||||
/** There as a network error. Not a bad response, but rather interference or some other inability to make a network request. */
|
/** There as a network error. Not a bad response, but rather interference or some other inability to make a network request. */
|
||||||
data class NetworkError(val exception: IOException) : BackupResponse()
|
data class NetworkError(val exception: IOException) : BackupResponse()
|
||||||
|
|
||||||
|
/** The client request was rate-limited. */
|
||||||
|
data class RateLimited(val retryAfter: Duration?) : BackupResponse()
|
||||||
|
|
||||||
/** Something went wrong when making the request that is related to application logic. */
|
/** Something went wrong when making the request that is related to application logic. */
|
||||||
data class ApplicationError(val exception: Throwable) : BackupResponse()
|
data class ApplicationError(val exception: Throwable) : BackupResponse()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import org.whispersystems.signalservice.internal.push.AuthCredentials
|
|||||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||||
import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage
|
import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import org.signal.svr2.proto.BackupResponse as ProtoBackupResponse
|
import org.signal.svr2.proto.BackupResponse as ProtoBackupResponse
|
||||||
import org.signal.svr2.proto.ExposeResponse as ProtoExposeResponse
|
import org.signal.svr2.proto.ExposeResponse as ProtoExposeResponse
|
||||||
import org.signal.svr2.proto.RestoreResponse as ProtoRestoreResponse
|
import org.signal.svr2.proto.RestoreResponse as ProtoRestoreResponse
|
||||||
@@ -220,10 +221,10 @@ class SecureValueRecoveryV2(
|
|||||||
}
|
}
|
||||||
} catch (e: NonSuccessfulResponseCodeException) {
|
} catch (e: NonSuccessfulResponseCodeException) {
|
||||||
Log.w(TAG, "[Set] Failed with a non-successful response code exception!", e)
|
Log.w(TAG, "[Set] Failed with a non-successful response code exception!", e)
|
||||||
if (e.code == 404) {
|
when (e.code) {
|
||||||
BackupResponse.EnclaveNotFound
|
404 -> BackupResponse.EnclaveNotFound
|
||||||
} else {
|
429 -> BackupResponse.RateLimited(e.headers["retry-after"]?.toLongOrNull()?.seconds)
|
||||||
BackupResponse.ApplicationError(e)
|
else -> BackupResponse.ApplicationError(e)
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.w(TAG, "[Set] Failed with a network exception!", e)
|
Log.w(TAG, "[Set] Failed with a network exception!", e)
|
||||||
|
|||||||
Reference in New Issue
Block a user