Return Retry-After time to clients when they are rate limited (#421)

* Return Retry-After time to clients when they are rate limited

* Update based on feedback

- New exception type that is mapped differently
- Always report time until allowed on rate limits
- Consume and transform into a differnt exception if we think it will be
  allowed later
This commit is contained in:
brock-signal
2021-03-05 10:23:03 -07:00
committed by GitHub
parent f57a4171ba
commit 1faedd3870
7 changed files with 109 additions and 17 deletions

View File

@@ -187,7 +187,7 @@ public class AccountController {
@QueryParam("client") Optional<String> client,
@QueryParam("captcha") Optional<String> captcha,
@QueryParam("challenge") Optional<String> pushChallenge)
throws RateLimitExceededException
throws RateLimitExceededException, RetryLaterException
{
if (!Util.isValidNumber(number)) {
logger.info("Invalid number: " + number);
@@ -217,16 +217,24 @@ public class AccountController {
return Response.status(402).build();
}
switch (transport) {
case "sms":
rateLimiters.getSmsDestinationLimiter().validate(number);
break;
case "voice":
rateLimiters.getVoiceDestinationLimiter().validate(number);
rateLimiters.getVoiceDestinationDailyLimiter().validate(number);
break;
default:
throw new WebApplicationException(Response.status(422).build());
try {
switch (transport) {
case "sms":
rateLimiters.getSmsDestinationLimiter().validate(number);
break;
case "voice":
rateLimiters.getVoiceDestinationLimiter().validate(number);
rateLimiters.getVoiceDestinationDailyLimiter().validate(number);
break;
default:
throw new WebApplicationException(Response.status(422).build());
}
} catch (RateLimitExceededException e) {
if (!e.getRetryDuration().isNegative()) {
throw new RetryLaterException(e);
} else {
throw e;
}
}
VerificationCode verificationCode = generateVerificationCode(number);

View File

@@ -4,12 +4,26 @@
*/
package org.whispersystems.textsecuregcm.controllers;
import java.time.Duration;
public class RateLimitExceededException extends Exception {
private final Duration retryDuration;
public RateLimitExceededException() {
super();
retryDuration = Duration.ZERO;
}
public RateLimitExceededException(String number) {
super(number);
public RateLimitExceededException(String message) {
super(message);
retryDuration = Duration.ZERO;
}
public RateLimitExceededException(String message, long retryAfterMillis) {
super(message);
retryDuration = Duration.ofMillis(retryAfterMillis);
}
public Duration getRetryDuration() { return retryDuration; }
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.controllers;
import java.time.Duration;
public class RetryLaterException extends Exception {
private final Duration backoffDuration;
public RetryLaterException() {
backoffDuration = Duration.ZERO;
}
public RetryLaterException(int retryLaterMillis) {
backoffDuration = Duration.ofMillis(retryLaterMillis);
}
public RetryLaterException(RateLimitExceededException e) {
this.backoffDuration = e.getRetryDuration();
}
public Duration getBackoffDuration() { return backoffDuration; }
}