mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 19:08:01 +01:00
Clarify roles/responsibilities of components in the message-handling pathway
This commit is contained in:
@@ -9,9 +9,14 @@ import static org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.signal.libsignal.protocol.SealedSenderMultiRecipientMessage;
|
||||
import org.whispersystems.textsecuregcm.identity.IdentityType;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
/**
|
||||
* A MessageSender sends Signal messages to destination devices. Messages may be "normal" user-to-user messages,
|
||||
@@ -42,26 +47,82 @@ public class MessageSender {
|
||||
this.pushNotificationManager = pushNotificationManager;
|
||||
}
|
||||
|
||||
public void sendMessage(final Account account, final Device device, final Envelope message, final boolean online) {
|
||||
final boolean destinationPresent = messagesManager.insert(account.getUuid(),
|
||||
device.getId(),
|
||||
online ? message.toBuilder().setEphemeral(true).build() : message);
|
||||
/**
|
||||
* Sends messages to devices associated with the given destination account. If a destination device has a valid push
|
||||
* notification token and does not have an active connection to a Signal server, then this method will also send a
|
||||
* push notification to that device to announce the availability of new messages.
|
||||
*
|
||||
* @param account the account to which to send messages
|
||||
* @param messagesByDeviceId a map of device IDs to message payloads
|
||||
*/
|
||||
public void sendMessages(final Account account, final Map<Byte, Envelope> messagesByDeviceId) {
|
||||
messagesManager.insert(account.getIdentifier(IdentityType.ACI), messagesByDeviceId)
|
||||
.forEach((deviceId, destinationPresent) -> {
|
||||
final Envelope message = messagesByDeviceId.get(deviceId);
|
||||
|
||||
if (!destinationPresent && !online) {
|
||||
try {
|
||||
pushNotificationManager.sendNewMessageNotification(account, device.getId(), message.getUrgent());
|
||||
} catch (final NotPushRegisteredException ignored) {
|
||||
}
|
||||
}
|
||||
if (!destinationPresent && !message.getEphemeral()) {
|
||||
try {
|
||||
pushNotificationManager.sendNewMessageNotification(account, deviceId, message.getUrgent());
|
||||
} catch (final NotPushRegisteredException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
Metrics.counter(SEND_COUNTER_NAME,
|
||||
CHANNEL_TAG_NAME, getDeliveryChannelName(device),
|
||||
EPHEMERAL_TAG_NAME, String.valueOf(online),
|
||||
CLIENT_ONLINE_TAG_NAME, String.valueOf(destinationPresent),
|
||||
URGENT_TAG_NAME, String.valueOf(message.getUrgent()),
|
||||
STORY_TAG_NAME, String.valueOf(message.getStory()),
|
||||
SEALED_SENDER_TAG_NAME, String.valueOf(!message.hasSourceServiceId()))
|
||||
.increment();
|
||||
Metrics.counter(SEND_COUNTER_NAME,
|
||||
CHANNEL_TAG_NAME, account.getDevice(deviceId).map(MessageSender::getDeliveryChannelName).orElse("unknown"),
|
||||
EPHEMERAL_TAG_NAME, String.valueOf(message.getEphemeral()),
|
||||
CLIENT_ONLINE_TAG_NAME, String.valueOf(destinationPresent),
|
||||
URGENT_TAG_NAME, String.valueOf(message.getUrgent()),
|
||||
STORY_TAG_NAME, String.valueOf(message.getStory()),
|
||||
SEALED_SENDER_TAG_NAME, String.valueOf(!message.hasSourceServiceId()))
|
||||
.increment();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends messages to a group of recipients. If a destination device has a valid push notification token and does not
|
||||
* have an active connection to a Signal server, then this method will also send a push notification to that device to
|
||||
* announce the availability of new messages.
|
||||
*
|
||||
* @param multiRecipientMessage the multi-recipient message to send to the given recipients
|
||||
* @param resolvedRecipients a map of recipients to resolved Signal accounts
|
||||
* @param clientTimestamp the time at which the sender reports the message was sent
|
||||
* @param isStory {@code true} if the message is a story or {@code false otherwise}
|
||||
* @param isEphemeral {@code true} if the message should only be delivered to devices with active connections or
|
||||
* {@code false otherwise}
|
||||
* @param isUrgent {@code true} if the message is urgent or {@code false otherwise}
|
||||
*
|
||||
* @return a future that completes when all messages have been inserted into delivery queues
|
||||
*/
|
||||
public CompletableFuture<Void> sendMultiRecipientMessage(final SealedSenderMultiRecipientMessage multiRecipientMessage,
|
||||
final Map<SealedSenderMultiRecipientMessage.Recipient, Account> resolvedRecipients,
|
||||
final long clientTimestamp,
|
||||
final boolean isStory,
|
||||
final boolean isEphemeral,
|
||||
final boolean isUrgent) {
|
||||
|
||||
return messagesManager.insertMultiRecipientMessage(multiRecipientMessage, resolvedRecipients, clientTimestamp,
|
||||
isStory, isEphemeral, isUrgent)
|
||||
.thenAccept(clientPresenceByAccountAndDevice ->
|
||||
clientPresenceByAccountAndDevice.forEach((account, clientPresenceByDeviceId) ->
|
||||
clientPresenceByDeviceId.forEach((deviceId, clientPresent) -> {
|
||||
if (!clientPresent && !isEphemeral) {
|
||||
try {
|
||||
pushNotificationManager.sendNewMessageNotification(account, deviceId, isUrgent);
|
||||
} catch (final NotPushRegisteredException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
Metrics.counter(SEND_COUNTER_NAME,
|
||||
CHANNEL_TAG_NAME,
|
||||
account.getDevice(deviceId).map(MessageSender::getDeliveryChannelName).orElse("unknown"),
|
||||
EPHEMERAL_TAG_NAME, String.valueOf(isEphemeral),
|
||||
CLIENT_ONLINE_TAG_NAME, String.valueOf(clientPresent),
|
||||
URGENT_TAG_NAME, String.valueOf(isUrgent),
|
||||
STORY_TAG_NAME, String.valueOf(isStory),
|
||||
SEALED_SENDER_TAG_NAME, String.valueOf(true))
|
||||
.increment();
|
||||
})))
|
||||
.thenRun(Util.NOOP);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.push;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.stream.Collectors;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos.Envelope;
|
||||
@@ -43,21 +44,21 @@ public class ReceiptSender {
|
||||
try {
|
||||
accountManager.getByAccountIdentifier(destinationIdentifier.uuid()).ifPresentOrElse(
|
||||
destinationAccount -> {
|
||||
final Envelope.Builder message = Envelope.newBuilder()
|
||||
final Envelope message = Envelope.newBuilder()
|
||||
.setServerTimestamp(System.currentTimeMillis())
|
||||
.setSourceServiceId(sourceIdentifier.toServiceIdentifierString())
|
||||
.setSourceDevice(sourceDeviceId)
|
||||
.setDestinationServiceId(destinationIdentifier.toServiceIdentifierString())
|
||||
.setClientTimestamp(messageId)
|
||||
.setType(Envelope.Type.SERVER_DELIVERY_RECEIPT)
|
||||
.setUrgent(false);
|
||||
.setUrgent(false)
|
||||
.build();
|
||||
|
||||
for (final Device destinationDevice : destinationAccount.getDevices()) {
|
||||
try {
|
||||
messageSender.sendMessage(destinationAccount, destinationDevice, message.build(), false);
|
||||
} catch (final Exception e) {
|
||||
logger.warn("Could not send delivery receipt", e);
|
||||
}
|
||||
try {
|
||||
messageSender.sendMessages(destinationAccount, destinationAccount.getDevices().stream()
|
||||
.collect(Collectors.toMap(Device::getId, ignored -> message)));
|
||||
} catch (final Exception e) {
|
||||
logger.warn("Could not send delivery receipt", e);
|
||||
}
|
||||
},
|
||||
() -> logger.info("No longer registered: {}", destinationIdentifier)
|
||||
|
||||
Reference in New Issue
Block a user