Remove GCMSender

This commit is contained in:
Jon Chambers
2022-08-02 11:22:15 -04:00
committed by Jon Chambers
parent 8956e1e0cf
commit 0a6d724f2c
11 changed files with 37 additions and 415 deletions

View File

@@ -20,7 +20,6 @@ import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguratio
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.configuration.BoostConfiguration;
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
import org.whispersystems.textsecuregcm.configuration.DatabaseConfiguration;
import org.whispersystems.textsecuregcm.configuration.DatadogConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration;
@@ -28,7 +27,6 @@ import org.whispersystems.textsecuregcm.configuration.DonationConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
import org.whispersystems.textsecuregcm.configuration.FcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.GcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration;
import org.whispersystems.textsecuregcm.configuration.GiftConfiguration;
import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration;
@@ -164,11 +162,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private WebSocketConfiguration webSocket = new WebSocketConfiguration();
@Valid
@NotNull
@JsonProperty
private GcmConfiguration gcm;
@Valid
@NotNull
@JsonProperty
@@ -342,10 +335,6 @@ public class WhisperServerConfiguration extends Configuration {
return limits;
}
public GcmConfiguration getGcmConfiguration() {
return gcm;
}
public FcmConfiguration getFcmConfiguration() {
return fcm;
}

View File

@@ -145,7 +145,6 @@ import org.whispersystems.textsecuregcm.push.APNSender;
import org.whispersystems.textsecuregcm.push.ApnFallbackManager;
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
import org.whispersystems.textsecuregcm.push.FcmSender;
import org.whispersystems.textsecuregcm.push.GCMSender;
import org.whispersystems.textsecuregcm.push.MessageSender;
import org.whispersystems.textsecuregcm.push.ProvisioningManager;
import org.whispersystems.textsecuregcm.push.ReceiptSender;
@@ -449,7 +448,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
PubSubManager pubSubManager = new PubSubManager(pubsubClient, dispatchManager);
APNSender apnSender = new APNSender(apnSenderExecutor, accountsManager, config.getApnConfiguration());
FcmSender fcmSender = new FcmSender(gcmSenderExecutor, accountsManager, config.getFcmConfiguration().credentials());
GCMSender gcmSender = new GCMSender(gcmSenderExecutor, accountsManager, config.getGcmConfiguration().getApiKey(), experimentEnrollmentManager, fcmSender);
RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), rateLimitersCluster);
DynamicRateLimiters dynamicRateLimiters = new DynamicRateLimiters(rateLimitersCluster, dynamicConfigurationManager);
ProvisioningManager provisioningManager = new ProvisioningManager(pubSubManager);
@@ -475,14 +473,14 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
ApnFallbackManager apnFallbackManager = new ApnFallbackManager(pushSchedulerCluster, apnSender, accountsManager);
TwilioSmsSender twilioSmsSender = new TwilioSmsSender(config.getTwilioConfiguration(), dynamicConfigurationManager);
SmsSender smsSender = new SmsSender(twilioSmsSender);
MessageSender messageSender = new MessageSender(apnFallbackManager, clientPresenceManager, messagesManager, gcmSender, apnSender, pushLatencyManager);
MessageSender messageSender = new MessageSender(apnFallbackManager, clientPresenceManager, messagesManager, fcmSender, apnSender, pushLatencyManager);
ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender, receiptSenderExecutor);
TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(dynamicConfigurationManager);
RecaptchaClient recaptchaClient = new RecaptchaClient(
config.getRecaptchaConfiguration().getProjectPath(),
config.getRecaptchaConfiguration().getCredentialConfigurationJson(),
dynamicConfigurationManager);
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, gcmSender, pushChallengeDynamoDb);
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, fcmSender, pushChallengeDynamoDb);
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
recaptchaClient, dynamicRateLimiters);
RateLimitChallengeOptionManager rateLimitChallengeOptionManager =
@@ -621,7 +619,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
environment.jersey().register(
new AccountController(pendingAccountsManager, accountsManager, abusiveHostRules, rateLimiters,
smsSender, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(),
recaptchaClient, gcmSender, apnSender, verifyExperimentEnrollmentManager,
recaptchaClient, fcmSender, apnSender, verifyExperimentEnrollmentManager,
changeNumberManager, backupCredentialsGenerator));
environment.jersey().register(new KeysController(rateLimiters, keys, accountsManager));

View File

@@ -1,29 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
public class GcmConfiguration {
@NotNull
@JsonProperty
private long senderId;
@NotEmpty
@JsonProperty
private String apiKey;
public String getApiKey() {
return apiKey;
}
public long getSenderId() {
return senderId;
}
}

View File

@@ -76,7 +76,7 @@ import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.metrics.UserAgentTagUtil;
import org.whispersystems.textsecuregcm.push.APNSender;
import org.whispersystems.textsecuregcm.push.ApnMessage;
import org.whispersystems.textsecuregcm.push.GCMSender;
import org.whispersystems.textsecuregcm.push.FcmSender;
import org.whispersystems.textsecuregcm.push.GcmMessage;
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
import org.whispersystems.textsecuregcm.sms.SmsSender;
@@ -137,7 +137,7 @@ public class AccountController {
private final TurnTokenGenerator turnTokenGenerator;
private final Map<String, Integer> testDevices;
private final RecaptchaClient recaptchaClient;
private final GCMSender gcmSender;
private final FcmSender fcmSender;
private final APNSender apnSender;
private final ExternalServiceCredentialGenerator backupServiceCredentialGenerator;
@@ -153,7 +153,7 @@ public class AccountController {
TurnTokenGenerator turnTokenGenerator,
Map<String, Integer> testDevices,
RecaptchaClient recaptchaClient,
GCMSender gcmSender,
FcmSender fcmSender,
APNSender apnSender,
TwilioVerifyExperimentEnrollmentManager verifyExperimentEnrollmentManager,
ChangeNumberManager changeNumberManager,
@@ -168,7 +168,7 @@ public class AccountController {
this.testDevices = testDevices;
this.turnTokenGenerator = turnTokenGenerator;
this.recaptchaClient = recaptchaClient;
this.gcmSender = gcmSender;
this.fcmSender = fcmSender;
this.apnSender = apnSender;
this.verifyExperimentEnrollmentManager = verifyExperimentEnrollmentManager;
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
@@ -200,7 +200,7 @@ public class AccountController {
pendingAccounts.store(number, storedVerificationCode);
if ("fcm".equals(pushType)) {
gcmSender.sendMessage(new GcmMessage(pushToken, null, 0, GcmMessage.Type.CHALLENGE, Optional.of(storedVerificationCode.getPushCode())));
fcmSender.sendMessage(new GcmMessage(pushToken, null, 0, GcmMessage.Type.CHALLENGE, Optional.of(storedVerificationCode.getPushCode())));
} else if ("apn".equals(pushType)) {
apnSender.sendMessage(new ApnMessage(pushToken, null, 0, useVoip.orElse(true), ApnMessage.Type.CHALLENGE, Optional.of(storedVerificationCode.getPushCode())));
} else {

View File

@@ -17,7 +17,7 @@ import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.push.APNSender;
import org.whispersystems.textsecuregcm.push.ApnMessage;
import org.whispersystems.textsecuregcm.push.ApnMessage.Type;
import org.whispersystems.textsecuregcm.push.GCMSender;
import org.whispersystems.textsecuregcm.push.FcmSender;
import org.whispersystems.textsecuregcm.push.GcmMessage;
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
import org.whispersystems.textsecuregcm.storage.Account;
@@ -28,7 +28,7 @@ import org.whispersystems.textsecuregcm.util.ua.ClientPlatform;
public class PushChallengeManager {
private final APNSender apnSender;
private final GCMSender gcmSender;
private final FcmSender fcmSender;
private final PushChallengeDynamoDb pushChallengeDynamoDb;
@@ -45,11 +45,11 @@ public class PushChallengeManager {
private static final String SUCCESS_TAG_NAME = "success";
private static final String SOURCE_COUNTRY_TAG_NAME = "sourceCountry";
public PushChallengeManager(final APNSender apnSender, final GCMSender gcmSender,
public PushChallengeManager(final APNSender apnSender, final FcmSender fcmSender,
final PushChallengeDynamoDb pushChallengeDynamoDb) {
this.apnSender = apnSender;
this.gcmSender = gcmSender;
this.fcmSender = fcmSender;
this.pushChallengeDynamoDb = pushChallengeDynamoDb;
}
@@ -71,7 +71,7 @@ public class PushChallengeManager {
sent = true;
if (StringUtils.isNotBlank(masterDevice.getGcmId())) {
gcmSender.sendMessage(new GcmMessage(masterDevice.getGcmId(), account.getUuid(), 0, GcmMessage.Type.RATE_LIMIT_CHALLENGE, Optional.of(tokenHex)));
fcmSender.sendMessage(new GcmMessage(masterDevice.getGcmId(), account.getUuid(), 0, GcmMessage.Type.RATE_LIMIT_CHALLENGE, Optional.of(tokenHex)));
platform = ClientPlatform.ANDROID.name().toLowerCase();
} else if (StringUtils.isNotBlank(masterDevice.getApnId())) {
apnSender.sendMessage(new ApnMessage(masterDevice.getApnId(), account.getUuid(), 0, false, Type.RATE_LIMIT_CHALLENGE, Optional.of(tokenHex)));

View File

@@ -1,191 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.push;
import static com.codahale.metrics.MetricRegistry.name;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.google.common.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import io.micrometer.core.instrument.Metrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.gcm.server.Message;
import org.whispersystems.gcm.server.Result;
import org.whispersystems.gcm.server.Sender;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.util.CircuitBreakerUtil;
import org.whispersystems.textsecuregcm.util.Constants;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import org.whispersystems.textsecuregcm.util.Util;
public class GCMSender {
private final Logger logger = LoggerFactory.getLogger(GCMSender.class);
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
private final Meter success = metricRegistry.meter(name(getClass(), "sent", "success"));
private final Meter failure = metricRegistry.meter(name(getClass(), "sent", "failure"));
private final Meter unregistered = metricRegistry.meter(name(getClass(), "sent", "unregistered"));
private final Meter canonical = metricRegistry.meter(name(getClass(), "sent", "canonical"));
private final String DOWNSTREAM_ERROR_COUNTER_NAME = name(GCMSender.class, "downstreamError");
private final Map<String, Meter> outboundMeters = new HashMap<>() {{
put("receipt", metricRegistry.meter(name(getClass(), "outbound", "receipt")));
put("notification", metricRegistry.meter(name(getClass(), "outbound", "notification")));
put("challenge", metricRegistry.meter(name(getClass(), "outbound", "challenge")));
put("rateLimitChallenge", metricRegistry.meter(name(getClass(), "outbound", "rateLimitChallenge")));
}};
private final AccountsManager accountsManager;
private final Sender signalSender;
private final ExecutorService executor;
private final ExperimentEnrollmentManager experimentEnrollmentManager;
private final FcmSender fcmSender;
public GCMSender(ExecutorService executor, AccountsManager accountsManager, String signalKey, ExperimentEnrollmentManager experimentEnrollmentManager, FcmSender fcmSender) {
this(executor, accountsManager, new Sender(signalKey, SystemMapper.getMapper(), 6), experimentEnrollmentManager, fcmSender);
CircuitBreakerUtil.registerMetrics(metricRegistry, signalSender.getRetry(), Sender.class);
}
@VisibleForTesting
public GCMSender(ExecutorService executor, AccountsManager accountsManager, Sender sender, ExperimentEnrollmentManager experimentEnrollmentManager, FcmSender fcmSender) {
this.accountsManager = accountsManager;
this.signalSender = sender;
this.executor = executor;
this.experimentEnrollmentManager = experimentEnrollmentManager;
this.fcmSender = fcmSender;
}
public void sendMessage(GcmMessage message) {
final boolean useFcmSender = message.getUuid()
.map(uuid -> experimentEnrollmentManager.isEnrolled(uuid, "fcmSender"))
.orElse(false);
if (useFcmSender) {
fcmSender.sendMessage(message);
} else {
Message.Builder builder = Message.newBuilder()
.withDestination(message.getGcmId())
.withPriority("high");
String key;
switch (message.getType()) {
case NOTIFICATION:
key = "notification";
break;
case CHALLENGE:
key = "challenge";
break;
case RATE_LIMIT_CHALLENGE:
key = "rateLimitChallenge";
break;
default:
throw new AssertionError();
}
Message request = builder.withDataPart(key, message.getData().orElse("")).build();
CompletableFuture<Result> future = signalSender.send(request);
markOutboundMeter(key);
future.handle((result, throwable) -> {
if (result != null && message.getType() != GcmMessage.Type.CHALLENGE) {
if (result.isUnregistered() || result.isInvalidRegistrationId()) {
executor.submit(() -> handleBadRegistration(message));
} else if (result.hasCanonicalRegistrationId()) {
executor.submit(() -> handleCanonicalRegistrationId(message, result));
} else if (!result.isSuccess()) {
executor.submit(() -> handleGenericError(message, result));
} else {
success.mark();
}
} else {
logger.warn("FCM Failed: " + throwable + ", " + throwable.getCause());
}
return null;
});
}
}
private void handleBadRegistration(GcmMessage message) {
Optional<Account> account = getAccountForEvent(message);
if (account.isPresent()) {
//noinspection OptionalGetWithoutIsPresent
Device device = account.get().getDevice(message.getDeviceId()).get();
if (device.getUninstalledFeedbackTimestamp() == 0) {
accountsManager.updateDevice(account.get(), message.getDeviceId(), d ->
d.setUninstalledFeedbackTimestamp(Util.todayInMillis()));
}
}
unregistered.mark();
}
private void handleCanonicalRegistrationId(GcmMessage message, Result result) {
logger.warn("Actually received 'CanonicalRegistrationId' ::: (canonical={}}), (original={}})",
result.getCanonicalRegistrationId(), message.getGcmId());
getAccountForEvent(message).ifPresent(account ->
accountsManager.updateDevice(
account,
message.getDeviceId(),
d -> d.setGcmId(result.getCanonicalRegistrationId())));
canonical.mark();
}
private void handleGenericError(GcmMessage message, Result result) {
logger.debug("Unrecoverable Error ::: (error={}}), (gcm_id={}}), (destination={}}), (device_id={}})",
result.getError(), message.getGcmId(), message.getUuid(), message.getDeviceId());
Metrics.counter(DOWNSTREAM_ERROR_COUNTER_NAME, "code", result.getError()).increment();
failure.mark();
}
private Optional<Account> getAccountForEvent(GcmMessage message) {
Optional<Account> account = message.getUuid().flatMap(accountsManager::getByAccountIdentifier);
if (account.isPresent()) {
Optional<Device> device = account.get().getDevice(message.getDeviceId());
if (device.isPresent()) {
if (message.getGcmId().equals(device.get().getGcmId())) {
if (device.get().getPushTimestamp() == 0 || System.currentTimeMillis() > (device.get().getPushTimestamp() + TimeUnit.SECONDS.toMillis(10))) {
return account;
}
}
}
}
return Optional.empty();
}
private void markOutboundMeter(String key) {
Meter meter = outboundMeters.get(key);
if (meter != null) meter.mark();
else logger.warn("Unknown outbound key: " + key);
}
}

View File

@@ -38,7 +38,7 @@ public class MessageSender implements Managed {
private final ApnFallbackManager apnFallbackManager;
private final ClientPresenceManager clientPresenceManager;
private final MessagesManager messagesManager;
private final GCMSender gcmSender;
private final FcmSender fcmSender;
private final APNSender apnSender;
private final PushLatencyManager pushLatencyManager;
@@ -50,14 +50,14 @@ public class MessageSender implements Managed {
public MessageSender(ApnFallbackManager apnFallbackManager,
ClientPresenceManager clientPresenceManager,
MessagesManager messagesManager,
GCMSender gcmSender,
FcmSender fcmSender,
APNSender apnSender,
PushLatencyManager pushLatencyManager)
{
this.apnFallbackManager = apnFallbackManager;
this.clientPresenceManager = clientPresenceManager;
this.messagesManager = messagesManager;
this.gcmSender = gcmSender;
this.fcmSender = fcmSender;
this.apnSender = apnSender;
this.pushLatencyManager = pushLatencyManager;
}
@@ -122,7 +122,7 @@ public class MessageSender implements Managed {
GcmMessage gcmMessage = new GcmMessage(device.getGcmId(), account.getUuid(),
(int)device.getId(), GcmMessage.Type.NOTIFICATION, Optional.empty());
gcmSender.sendMessage(gcmMessage);
fcmSender.sendMessage(gcmMessage);
RedisOperation.unchecked(() -> pushLatencyManager.recordPushSent(account.getUuid(), device.getId(), false));
}