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

@ -202,10 +202,6 @@ apn: # Apple Push Notifications configuration
AAAAAAAA AAAAAAAA
-----END PRIVATE KEY----- -----END PRIVATE KEY-----
gcm: # GCM Configuration
senderId: 123456789
apiKey: unset
fcm: # FCM configuration fcm: # FCM configuration
credentials: | credentials: |
{ "json": true } { "json": true }

View File

@ -20,7 +20,6 @@ import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguratio
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration; import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.configuration.BoostConfiguration; import org.whispersystems.textsecuregcm.configuration.BoostConfiguration;
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration; import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
import org.whispersystems.textsecuregcm.configuration.DatabaseConfiguration;
import org.whispersystems.textsecuregcm.configuration.DatadogConfiguration; import org.whispersystems.textsecuregcm.configuration.DatadogConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration; import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration;
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration; 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.DynamoDbClientConfiguration;
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables; import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
import org.whispersystems.textsecuregcm.configuration.FcmConfiguration; import org.whispersystems.textsecuregcm.configuration.FcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.GcmConfiguration;
import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration; import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration;
import org.whispersystems.textsecuregcm.configuration.GiftConfiguration; import org.whispersystems.textsecuregcm.configuration.GiftConfiguration;
import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration; import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration;
@ -164,11 +162,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty @JsonProperty
private WebSocketConfiguration webSocket = new WebSocketConfiguration(); private WebSocketConfiguration webSocket = new WebSocketConfiguration();
@Valid
@NotNull
@JsonProperty
private GcmConfiguration gcm;
@Valid @Valid
@NotNull @NotNull
@JsonProperty @JsonProperty
@ -342,10 +335,6 @@ public class WhisperServerConfiguration extends Configuration {
return limits; return limits;
} }
public GcmConfiguration getGcmConfiguration() {
return gcm;
}
public FcmConfiguration getFcmConfiguration() { public FcmConfiguration getFcmConfiguration() {
return fcm; 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.ApnFallbackManager;
import org.whispersystems.textsecuregcm.push.ClientPresenceManager; import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
import org.whispersystems.textsecuregcm.push.FcmSender; import org.whispersystems.textsecuregcm.push.FcmSender;
import org.whispersystems.textsecuregcm.push.GCMSender;
import org.whispersystems.textsecuregcm.push.MessageSender; import org.whispersystems.textsecuregcm.push.MessageSender;
import org.whispersystems.textsecuregcm.push.ProvisioningManager; import org.whispersystems.textsecuregcm.push.ProvisioningManager;
import org.whispersystems.textsecuregcm.push.ReceiptSender; import org.whispersystems.textsecuregcm.push.ReceiptSender;
@ -449,7 +448,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
PubSubManager pubSubManager = new PubSubManager(pubsubClient, dispatchManager); PubSubManager pubSubManager = new PubSubManager(pubsubClient, dispatchManager);
APNSender apnSender = new APNSender(apnSenderExecutor, accountsManager, config.getApnConfiguration()); APNSender apnSender = new APNSender(apnSenderExecutor, accountsManager, config.getApnConfiguration());
FcmSender fcmSender = new FcmSender(gcmSenderExecutor, accountsManager, config.getFcmConfiguration().credentials()); 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); RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), rateLimitersCluster);
DynamicRateLimiters dynamicRateLimiters = new DynamicRateLimiters(rateLimitersCluster, dynamicConfigurationManager); DynamicRateLimiters dynamicRateLimiters = new DynamicRateLimiters(rateLimitersCluster, dynamicConfigurationManager);
ProvisioningManager provisioningManager = new ProvisioningManager(pubSubManager); ProvisioningManager provisioningManager = new ProvisioningManager(pubSubManager);
@ -475,14 +473,14 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
ApnFallbackManager apnFallbackManager = new ApnFallbackManager(pushSchedulerCluster, apnSender, accountsManager); ApnFallbackManager apnFallbackManager = new ApnFallbackManager(pushSchedulerCluster, apnSender, accountsManager);
TwilioSmsSender twilioSmsSender = new TwilioSmsSender(config.getTwilioConfiguration(), dynamicConfigurationManager); TwilioSmsSender twilioSmsSender = new TwilioSmsSender(config.getTwilioConfiguration(), dynamicConfigurationManager);
SmsSender smsSender = new SmsSender(twilioSmsSender); 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); ReceiptSender receiptSender = new ReceiptSender(accountsManager, messageSender, receiptSenderExecutor);
TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(dynamicConfigurationManager); TurnTokenGenerator turnTokenGenerator = new TurnTokenGenerator(dynamicConfigurationManager);
RecaptchaClient recaptchaClient = new RecaptchaClient( RecaptchaClient recaptchaClient = new RecaptchaClient(
config.getRecaptchaConfiguration().getProjectPath(), config.getRecaptchaConfiguration().getProjectPath(),
config.getRecaptchaConfiguration().getCredentialConfigurationJson(), config.getRecaptchaConfiguration().getCredentialConfigurationJson(),
dynamicConfigurationManager); dynamicConfigurationManager);
PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, gcmSender, pushChallengeDynamoDb); PushChallengeManager pushChallengeManager = new PushChallengeManager(apnSender, fcmSender, pushChallengeDynamoDb);
RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager, RateLimitChallengeManager rateLimitChallengeManager = new RateLimitChallengeManager(pushChallengeManager,
recaptchaClient, dynamicRateLimiters); recaptchaClient, dynamicRateLimiters);
RateLimitChallengeOptionManager rateLimitChallengeOptionManager = RateLimitChallengeOptionManager rateLimitChallengeOptionManager =
@ -621,7 +619,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
environment.jersey().register( environment.jersey().register(
new AccountController(pendingAccountsManager, accountsManager, abusiveHostRules, rateLimiters, new AccountController(pendingAccountsManager, accountsManager, abusiveHostRules, rateLimiters,
smsSender, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(), smsSender, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(),
recaptchaClient, gcmSender, apnSender, verifyExperimentEnrollmentManager, recaptchaClient, fcmSender, apnSender, verifyExperimentEnrollmentManager,
changeNumberManager, backupCredentialsGenerator)); changeNumberManager, backupCredentialsGenerator));
environment.jersey().register(new KeysController(rateLimiters, keys, accountsManager)); 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.metrics.UserAgentTagUtil;
import org.whispersystems.textsecuregcm.push.APNSender; import org.whispersystems.textsecuregcm.push.APNSender;
import org.whispersystems.textsecuregcm.push.ApnMessage; 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.push.GcmMessage;
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient; import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
import org.whispersystems.textsecuregcm.sms.SmsSender; import org.whispersystems.textsecuregcm.sms.SmsSender;
@ -137,7 +137,7 @@ public class AccountController {
private final TurnTokenGenerator turnTokenGenerator; private final TurnTokenGenerator turnTokenGenerator;
private final Map<String, Integer> testDevices; private final Map<String, Integer> testDevices;
private final RecaptchaClient recaptchaClient; private final RecaptchaClient recaptchaClient;
private final GCMSender gcmSender; private final FcmSender fcmSender;
private final APNSender apnSender; private final APNSender apnSender;
private final ExternalServiceCredentialGenerator backupServiceCredentialGenerator; private final ExternalServiceCredentialGenerator backupServiceCredentialGenerator;
@ -153,7 +153,7 @@ public class AccountController {
TurnTokenGenerator turnTokenGenerator, TurnTokenGenerator turnTokenGenerator,
Map<String, Integer> testDevices, Map<String, Integer> testDevices,
RecaptchaClient recaptchaClient, RecaptchaClient recaptchaClient,
GCMSender gcmSender, FcmSender fcmSender,
APNSender apnSender, APNSender apnSender,
TwilioVerifyExperimentEnrollmentManager verifyExperimentEnrollmentManager, TwilioVerifyExperimentEnrollmentManager verifyExperimentEnrollmentManager,
ChangeNumberManager changeNumberManager, ChangeNumberManager changeNumberManager,
@ -168,7 +168,7 @@ public class AccountController {
this.testDevices = testDevices; this.testDevices = testDevices;
this.turnTokenGenerator = turnTokenGenerator; this.turnTokenGenerator = turnTokenGenerator;
this.recaptchaClient = recaptchaClient; this.recaptchaClient = recaptchaClient;
this.gcmSender = gcmSender; this.fcmSender = fcmSender;
this.apnSender = apnSender; this.apnSender = apnSender;
this.verifyExperimentEnrollmentManager = verifyExperimentEnrollmentManager; this.verifyExperimentEnrollmentManager = verifyExperimentEnrollmentManager;
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator; this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
@ -200,7 +200,7 @@ public class AccountController {
pendingAccounts.store(number, storedVerificationCode); pendingAccounts.store(number, storedVerificationCode);
if ("fcm".equals(pushType)) { 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)) { } else if ("apn".equals(pushType)) {
apnSender.sendMessage(new ApnMessage(pushToken, null, 0, useVoip.orElse(true), ApnMessage.Type.CHALLENGE, Optional.of(storedVerificationCode.getPushCode()))); apnSender.sendMessage(new ApnMessage(pushToken, null, 0, useVoip.orElse(true), ApnMessage.Type.CHALLENGE, Optional.of(storedVerificationCode.getPushCode())));
} else { } else {

View File

@ -17,7 +17,7 @@ import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.push.APNSender; import org.whispersystems.textsecuregcm.push.APNSender;
import org.whispersystems.textsecuregcm.push.ApnMessage; import org.whispersystems.textsecuregcm.push.ApnMessage;
import org.whispersystems.textsecuregcm.push.ApnMessage.Type; 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.GcmMessage;
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException; import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Account;
@ -28,7 +28,7 @@ import org.whispersystems.textsecuregcm.util.ua.ClientPlatform;
public class PushChallengeManager { public class PushChallengeManager {
private final APNSender apnSender; private final APNSender apnSender;
private final GCMSender gcmSender; private final FcmSender fcmSender;
private final PushChallengeDynamoDb pushChallengeDynamoDb; private final PushChallengeDynamoDb pushChallengeDynamoDb;
@ -45,11 +45,11 @@ public class PushChallengeManager {
private static final String SUCCESS_TAG_NAME = "success"; private static final String SUCCESS_TAG_NAME = "success";
private static final String SOURCE_COUNTRY_TAG_NAME = "sourceCountry"; 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) { final PushChallengeDynamoDb pushChallengeDynamoDb) {
this.apnSender = apnSender; this.apnSender = apnSender;
this.gcmSender = gcmSender; this.fcmSender = fcmSender;
this.pushChallengeDynamoDb = pushChallengeDynamoDb; this.pushChallengeDynamoDb = pushChallengeDynamoDb;
} }
@ -71,7 +71,7 @@ public class PushChallengeManager {
sent = true; sent = true;
if (StringUtils.isNotBlank(masterDevice.getGcmId())) { 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(); platform = ClientPlatform.ANDROID.name().toLowerCase();
} else if (StringUtils.isNotBlank(masterDevice.getApnId())) { } else if (StringUtils.isNotBlank(masterDevice.getApnId())) {
apnSender.sendMessage(new ApnMessage(masterDevice.getApnId(), account.getUuid(), 0, false, Type.RATE_LIMIT_CHALLENGE, Optional.of(tokenHex))); 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 ApnFallbackManager apnFallbackManager;
private final ClientPresenceManager clientPresenceManager; private final ClientPresenceManager clientPresenceManager;
private final MessagesManager messagesManager; private final MessagesManager messagesManager;
private final GCMSender gcmSender; private final FcmSender fcmSender;
private final APNSender apnSender; private final APNSender apnSender;
private final PushLatencyManager pushLatencyManager; private final PushLatencyManager pushLatencyManager;
@ -50,14 +50,14 @@ public class MessageSender implements Managed {
public MessageSender(ApnFallbackManager apnFallbackManager, public MessageSender(ApnFallbackManager apnFallbackManager,
ClientPresenceManager clientPresenceManager, ClientPresenceManager clientPresenceManager,
MessagesManager messagesManager, MessagesManager messagesManager,
GCMSender gcmSender, FcmSender fcmSender,
APNSender apnSender, APNSender apnSender,
PushLatencyManager pushLatencyManager) PushLatencyManager pushLatencyManager)
{ {
this.apnFallbackManager = apnFallbackManager; this.apnFallbackManager = apnFallbackManager;
this.clientPresenceManager = clientPresenceManager; this.clientPresenceManager = clientPresenceManager;
this.messagesManager = messagesManager; this.messagesManager = messagesManager;
this.gcmSender = gcmSender; this.fcmSender = fcmSender;
this.apnSender = apnSender; this.apnSender = apnSender;
this.pushLatencyManager = pushLatencyManager; this.pushLatencyManager = pushLatencyManager;
} }
@ -122,7 +122,7 @@ public class MessageSender implements Managed {
GcmMessage gcmMessage = new GcmMessage(device.getGcmId(), account.getUuid(), GcmMessage gcmMessage = new GcmMessage(device.getGcmId(), account.getUuid(),
(int)device.getId(), GcmMessage.Type.NOTIFICATION, Optional.empty()); (int)device.getId(), GcmMessage.Type.NOTIFICATION, Optional.empty());
gcmSender.sendMessage(gcmMessage); fcmSender.sendMessage(gcmMessage);
RedisOperation.unchecked(() -> pushLatencyManager.recordPushSent(account.getUuid(), device.getId(), false)); RedisOperation.unchecked(() -> pushLatencyManager.recordPushSent(account.getUuid(), device.getId(), false));
} }

View File

@ -37,7 +37,7 @@ class MessageSenderTest {
private ClientPresenceManager clientPresenceManager; private ClientPresenceManager clientPresenceManager;
private MessagesManager messagesManager; private MessagesManager messagesManager;
private GCMSender gcmSender; private FcmSender fcmSender;
private APNSender apnSender; private APNSender apnSender;
private MessageSender messageSender; private MessageSender messageSender;
@ -53,12 +53,12 @@ class MessageSenderTest {
clientPresenceManager = mock(ClientPresenceManager.class); clientPresenceManager = mock(ClientPresenceManager.class);
messagesManager = mock(MessagesManager.class); messagesManager = mock(MessagesManager.class);
gcmSender = mock(GCMSender.class); fcmSender = mock(FcmSender.class);
apnSender = mock(APNSender.class); apnSender = mock(APNSender.class);
messageSender = new MessageSender(mock(ApnFallbackManager.class), messageSender = new MessageSender(mock(ApnFallbackManager.class),
clientPresenceManager, clientPresenceManager,
messagesManager, messagesManager,
gcmSender, fcmSender,
apnSender, apnSender,
mock(PushLatencyManager.class)); mock(PushLatencyManager.class));
@ -80,7 +80,7 @@ class MessageSenderTest {
assertTrue(envelopeArgumentCaptor.getValue().getEphemeral()); assertTrue(envelopeArgumentCaptor.getValue().getEphemeral());
verifyNoInteractions(gcmSender); verifyNoInteractions(fcmSender);
verifyNoInteractions(apnSender); verifyNoInteractions(apnSender);
} }
@ -92,7 +92,7 @@ class MessageSenderTest {
messageSender.sendMessage(account, device, message, true); messageSender.sendMessage(account, device, message, true);
verify(messagesManager, never()).insert(any(), anyLong(), any()); verify(messagesManager, never()).insert(any(), anyLong(), any());
verifyNoInteractions(gcmSender); verifyNoInteractions(fcmSender);
verifyNoInteractions(apnSender); verifyNoInteractions(apnSender);
} }
@ -110,7 +110,7 @@ class MessageSenderTest {
assertFalse(envelopeArgumentCaptor.getValue().getEphemeral()); assertFalse(envelopeArgumentCaptor.getValue().getEphemeral());
assertEquals(message, envelopeArgumentCaptor.getValue()); assertEquals(message, envelopeArgumentCaptor.getValue());
verifyNoInteractions(gcmSender); verifyNoInteractions(fcmSender);
verifyNoInteractions(apnSender); verifyNoInteractions(apnSender);
} }
@ -122,7 +122,7 @@ class MessageSenderTest {
messageSender.sendMessage(account, device, message, false); messageSender.sendMessage(account, device, message, false);
verify(messagesManager).insert(ACCOUNT_UUID, DEVICE_ID, message); verify(messagesManager).insert(ACCOUNT_UUID, DEVICE_ID, message);
verify(gcmSender).sendMessage(any()); verify(fcmSender).sendMessage(any());
verifyNoInteractions(apnSender); verifyNoInteractions(apnSender);
} }
@ -134,7 +134,7 @@ class MessageSenderTest {
messageSender.sendMessage(account, device, message, false); messageSender.sendMessage(account, device, message, false);
verify(messagesManager).insert(ACCOUNT_UUID, DEVICE_ID, message); verify(messagesManager).insert(ACCOUNT_UUID, DEVICE_ID, message);
verifyNoInteractions(gcmSender); verifyNoInteractions(fcmSender);
verify(apnSender).sendMessage(any()); verify(apnSender).sendMessage(any());
} }
@ -146,7 +146,7 @@ class MessageSenderTest {
messageSender.sendMessage(account, device, message, false); messageSender.sendMessage(account, device, message, false);
verify(messagesManager).insert(ACCOUNT_UUID, DEVICE_ID, message); verify(messagesManager).insert(ACCOUNT_UUID, DEVICE_ID, message);
verifyNoInteractions(gcmSender); verifyNoInteractions(fcmSender);
verifyNoInteractions(apnSender); verifyNoInteractions(apnSender);
} }

View File

@ -82,7 +82,7 @@ import org.whispersystems.textsecuregcm.mappers.NonNormalizedPhoneNumberResponse
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper; import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.push.APNSender; import org.whispersystems.textsecuregcm.push.APNSender;
import org.whispersystems.textsecuregcm.push.ApnMessage; 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.push.GcmMessage;
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient; import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
import org.whispersystems.textsecuregcm.sms.SmsSender; import org.whispersystems.textsecuregcm.sms.SmsSender;
@ -147,7 +147,7 @@ class AccountControllerTest {
private static Account senderHasStorage = mock(Account.class); private static Account senderHasStorage = mock(Account.class);
private static Account senderTransfer = mock(Account.class); private static Account senderTransfer = mock(Account.class);
private static RecaptchaClient recaptchaClient = mock(RecaptchaClient.class); private static RecaptchaClient recaptchaClient = mock(RecaptchaClient.class);
private static GCMSender gcmSender = mock(GCMSender.class); private static FcmSender fcmSender = mock(FcmSender.class);
private static APNSender apnSender = mock(APNSender.class); private static APNSender apnSender = mock(APNSender.class);
private static ChangeNumberManager changeNumberManager = mock(ChangeNumberManager.class); private static ChangeNumberManager changeNumberManager = mock(ChangeNumberManager.class);
@ -179,7 +179,7 @@ class AccountControllerTest {
turnTokenGenerator, turnTokenGenerator,
Map.of(TEST_NUMBER, TEST_VERIFICATION_CODE), Map.of(TEST_NUMBER, TEST_VERIFICATION_CODE),
recaptchaClient, recaptchaClient,
gcmSender, fcmSender,
apnSender, apnSender,
verifyExperimentEnrollmentManager, verifyExperimentEnrollmentManager,
changeNumberManager, changeNumberManager,
@ -322,7 +322,7 @@ class AccountControllerTest {
senderHasStorage, senderHasStorage,
senderTransfer, senderTransfer,
recaptchaClient, recaptchaClient,
gcmSender, fcmSender,
apnSender, apnSender,
verifyExperimentEnrollmentManager, verifyExperimentEnrollmentManager,
changeNumberManager); changeNumberManager);
@ -341,7 +341,7 @@ class AccountControllerTest {
ArgumentCaptor<GcmMessage> captor = ArgumentCaptor.forClass(GcmMessage.class); ArgumentCaptor<GcmMessage> captor = ArgumentCaptor.forClass(GcmMessage.class);
verify(gcmSender, times(1)).sendMessage(captor.capture()); verify(fcmSender, times(1)).sendMessage(captor.capture());
assertThat(captor.getValue().getGcmId()).isEqualTo("mytoken"); assertThat(captor.getValue().getGcmId()).isEqualTo("mytoken");
assertThat(captor.getValue().getData().isPresent()).isTrue(); assertThat(captor.getValue().getData().isPresent()).isTrue();
assertThat(captor.getValue().getData().get().length()).isEqualTo(32); assertThat(captor.getValue().getData().get().length()).isEqualTo(32);
@ -360,7 +360,7 @@ class AccountControllerTest {
ArgumentCaptor<GcmMessage> captor = ArgumentCaptor.forClass(GcmMessage.class); ArgumentCaptor<GcmMessage> captor = ArgumentCaptor.forClass(GcmMessage.class);
verify(gcmSender, times(1)).sendMessage(captor.capture()); verify(fcmSender, times(1)).sendMessage(captor.capture());
assertThat(captor.getValue().getGcmId()).isEqualTo("mytoken"); assertThat(captor.getValue().getGcmId()).isEqualTo("mytoken");
assertThat(captor.getValue().getData().isPresent()).isTrue(); assertThat(captor.getValue().getData().isPresent()).isTrue();
assertThat(captor.getValue().getData().get().length()).isEqualTo(32); assertThat(captor.getValue().getData().get().length()).isEqualTo(32);
@ -386,7 +386,7 @@ class AccountControllerTest {
assertThat(captor.getValue().getMessage()).contains("\"challenge\" : \"" + captor.getValue().getChallengeData().get() + "\""); assertThat(captor.getValue().getMessage()).contains("\"challenge\" : \"" + captor.getValue().getChallengeData().get() + "\"");
assertThat(captor.getValue().isVoip()).isTrue(); assertThat(captor.getValue().isVoip()).isTrue();
verifyNoMoreInteractions(gcmSender); verifyNoMoreInteractions(fcmSender);
} }
@Test @Test
@ -408,7 +408,7 @@ class AccountControllerTest {
assertThat(captor.getValue().getMessage()).contains("\"challenge\" : \"" + captor.getValue().getChallengeData().get() + "\""); assertThat(captor.getValue().getMessage()).contains("\"challenge\" : \"" + captor.getValue().getChallengeData().get() + "\"");
assertThat(captor.getValue().isVoip()).isTrue(); assertThat(captor.getValue().isVoip()).isTrue();
verifyNoMoreInteractions(gcmSender); verifyNoMoreInteractions(fcmSender);
} }
@Test @Test
@ -430,7 +430,7 @@ class AccountControllerTest {
assertThat(captor.getValue().getMessage()).contains("\"challenge\" : \"" + captor.getValue().getChallengeData().get() + "\""); assertThat(captor.getValue().getMessage()).contains("\"challenge\" : \"" + captor.getValue().getChallengeData().get() + "\"");
assertThat(captor.getValue().isVoip()).isFalse(); assertThat(captor.getValue().isVoip()).isFalse();
verifyNoMoreInteractions(gcmSender); verifyNoMoreInteractions(fcmSender);
} }
@Test @Test
@ -443,7 +443,7 @@ class AccountControllerTest {
assertThat(response.getStatus()).isEqualTo(400); assertThat(response.getStatus()).isEqualTo(400);
assertThat(response.readEntity(String.class)).isBlank(); assertThat(response.readEntity(String.class)).isBlank();
verifyNoMoreInteractions(gcmSender); verifyNoMoreInteractions(fcmSender);
verifyNoMoreInteractions(apnSender); verifyNoMoreInteractions(apnSender);
} }
@ -462,7 +462,7 @@ class AccountControllerTest {
assertThat(responseEntity.getOriginalNumber()).isEqualTo(number); assertThat(responseEntity.getOriginalNumber()).isEqualTo(number);
assertThat(responseEntity.getNormalizedNumber()).isEqualTo("+447700900111"); assertThat(responseEntity.getNormalizedNumber()).isEqualTo("+447700900111");
verifyNoMoreInteractions(gcmSender); verifyNoMoreInteractions(fcmSender);
verifyNoMoreInteractions(apnSender); verifyNoMoreInteractions(apnSender);
} }

View File

@ -1,141 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.tests.push;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import org.junit.jupiter.api.Test;
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.push.FcmSender;
import org.whispersystems.textsecuregcm.push.GCMSender;
import org.whispersystems.textsecuregcm.push.GcmMessage;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
import org.whispersystems.textsecuregcm.tests.util.SynchronousExecutorService;
import org.whispersystems.textsecuregcm.util.Util;
class GCMSenderTest {
@Test
void testSendMessage() {
AccountsManager accountsManager = mock(AccountsManager.class);
Sender sender = mock(Sender.class );
Result successResult = mock(Result.class );
SynchronousExecutorService executorService = new SynchronousExecutorService();
when(successResult.isInvalidRegistrationId()).thenReturn(false);
when(successResult.isUnregistered()).thenReturn(false);
when(successResult.hasCanonicalRegistrationId()).thenReturn(false);
when(successResult.isSuccess()).thenReturn(true);
AccountsHelper.setupMockUpdate(accountsManager);
GcmMessage message = new GcmMessage("foo", UUID.randomUUID(), 1, GcmMessage.Type.NOTIFICATION, Optional.empty());
GCMSender gcmSender = new GCMSender(executorService, accountsManager, sender, mock(ExperimentEnrollmentManager.class), mock(FcmSender.class));
CompletableFuture<Result> successFuture = CompletableFuture.completedFuture(successResult);
when(sender.send(any(Message.class))).thenReturn(successFuture);
gcmSender.sendMessage(message);
verify(sender, times(1)).send(any(Message.class));
}
@Test
void testSendUninstalled() {
UUID destinationUuid = UUID.randomUUID();
String gcmId = "foo";
AccountsManager accountsManager = mock(AccountsManager.class);
Sender sender = mock(Sender.class );
Result invalidResult = mock(Result.class );
SynchronousExecutorService executorService = new SynchronousExecutorService();
Account destinationAccount = mock(Account.class);
Device destinationDevice = mock(Device.class );
AccountsHelper.setupMockUpdate(accountsManager);
when(destinationAccount.getDevice(1)).thenReturn(Optional.of(destinationDevice));
when(accountsManager.getByAccountIdentifier(destinationUuid)).thenReturn(Optional.of(destinationAccount));
when(destinationDevice.getGcmId()).thenReturn(gcmId);
when(invalidResult.isInvalidRegistrationId()).thenReturn(true);
when(invalidResult.isUnregistered()).thenReturn(false);
when(invalidResult.hasCanonicalRegistrationId()).thenReturn(false);
when(invalidResult.isSuccess()).thenReturn(true);
GcmMessage message = new GcmMessage(gcmId, destinationUuid, 1, GcmMessage.Type.NOTIFICATION, Optional.empty());
GCMSender gcmSender = new GCMSender(executorService, accountsManager, sender, mock(ExperimentEnrollmentManager.class), mock(FcmSender.class));
CompletableFuture<Result> invalidFuture = CompletableFuture.completedFuture(invalidResult);
when(sender.send(any(Message.class))).thenReturn(invalidFuture);
gcmSender.sendMessage(message);
verify(sender, times(1)).send(any(Message.class));
verify(accountsManager, times(1)).getByAccountIdentifier(eq(destinationUuid));
verify(accountsManager, times(1)).updateDevice(eq(destinationAccount), eq(1L), any());
verify(destinationDevice, times(1)).setUninstalledFeedbackTimestamp(eq(Util.todayInMillis()));
}
@Test
void testCanonicalId() {
UUID destinationUuid = UUID.randomUUID();
String gcmId = "foo";
String canonicalId = "bar";
AccountsManager accountsManager = mock(AccountsManager.class);
Sender sender = mock(Sender.class );
Result canonicalResult = mock(Result.class );
SynchronousExecutorService executorService = new SynchronousExecutorService();
Account destinationAccount = mock(Account.class );
Device destinationDevice = mock(Device.class );
when(destinationAccount.getDevice(1)).thenReturn(Optional.of(destinationDevice));
when(accountsManager.getByAccountIdentifier(destinationUuid)).thenReturn(Optional.of(destinationAccount));
when(destinationDevice.getGcmId()).thenReturn(gcmId);
AccountsHelper.setupMockUpdate(accountsManager);
when(canonicalResult.isInvalidRegistrationId()).thenReturn(false);
when(canonicalResult.isUnregistered()).thenReturn(false);
when(canonicalResult.hasCanonicalRegistrationId()).thenReturn(true);
when(canonicalResult.isSuccess()).thenReturn(false);
when(canonicalResult.getCanonicalRegistrationId()).thenReturn(canonicalId);
GcmMessage message = new GcmMessage(gcmId, destinationUuid, 1, GcmMessage.Type.NOTIFICATION, Optional.empty());
GCMSender gcmSender = new GCMSender(executorService, accountsManager, sender, mock(ExperimentEnrollmentManager.class), mock(FcmSender.class));
CompletableFuture<Result> invalidFuture = CompletableFuture.completedFuture(canonicalResult);
when(sender.send(any(Message.class))).thenReturn(invalidFuture);
gcmSender.sendMessage(message);
verify(sender, times(1)).send(any(Message.class));
verify(accountsManager, times(1)).getByAccountIdentifier(eq(destinationUuid));
verify(accountsManager, times(1)).updateDevice(eq(destinationAccount), eq(1L), any());
verify(destinationDevice, times(1)).setGcmId(eq(canonicalId));
}
}