Page MenuHomePhabricator

D7815.id26613.diff
No OneTemporary

D7815.id26613.diff

diff --git a/keyserver/src/creators/message-creator.js b/keyserver/src/creators/message-creator.js
--- a/keyserver/src/creators/message-creator.js
+++ b/keyserver/src/creators/message-creator.js
@@ -52,6 +52,7 @@
{
+platform: string,
+deviceToken: string,
+ +cookieID: string,
+codeVersion: ?string,
},
>,
@@ -323,7 +324,7 @@
const time = earliestFocusedTimeConsideredExpired();
const visibleExtractString = `$.${threadPermissions.VISIBLE}.value`;
const query = SQL`
- SELECT m.user, m.thread, c.platform, c.device_token, c.versions,
+ SELECT m.user, m.thread, c.platform, c.device_token, c.versions, c.id,
f.user AS focused_user
`;
query.append(subthreadSelects);
@@ -349,6 +350,7 @@
const focusedUser = !!row.focused_user;
const { platform } = row;
const versions = JSON.parse(row.versions);
+ const cookieID = row.id;
let thisUserInfo = perUserInfo.get(userID);
if (!thisUserInfo) {
thisUserInfo = {
@@ -384,10 +386,11 @@
}
}
}
- if (deviceToken) {
+ if (deviceToken && cookieID) {
thisUserInfo.devices.set(deviceToken, {
platform,
deviceToken,
+ cookieID: cookieID.toString(),
codeVersion: versions ? versions.codeVersion : null,
});
}
diff --git a/keyserver/src/push/rescind.js b/keyserver/src/push/rescind.js
--- a/keyserver/src/push/rescind.js
+++ b/keyserver/src/push/rescind.js
@@ -53,13 +53,11 @@
for (const delivery of deliveries) {
if (delivery.iosID && delivery.iosDeviceTokens) {
// Old iOS
- const notification = prepareIOSNotification(
- delivery.iosID,
- row.unread_count,
- threadID,
- );
+ const notifications = [
+ prepareIOSNotification(delivery.iosID, row.unread_count, threadID),
+ ];
deliveryPromises[id] = apnPush({
- notification,
+ notifications,
deviceTokens: delivery.iosDeviceTokens,
platformDetails: { platform: 'ios' },
});
@@ -78,14 +76,16 @@
} else if (delivery.deviceType === 'ios') {
// New iOS
const { iosID, deviceTokens, codeVersion } = delivery;
- const notification = prepareIOSNotification(
- iosID,
- row.unread_count,
- threadID,
- codeVersion,
- );
+ const notifications = [
+ prepareIOSNotification(
+ iosID,
+ row.unread_count,
+ threadID,
+ codeVersion,
+ ),
+ ];
deliveryPromises[id] = apnPush({
- notification,
+ notifications,
deviceTokens,
platformDetails: { platform: 'ios', codeVersion },
});
diff --git a/keyserver/src/push/send.js b/keyserver/src/push/send.js
--- a/keyserver/src/push/send.js
+++ b/keyserver/src/push/send.js
@@ -37,6 +37,7 @@
import { updateTypes } from 'lib/types/update-types-enum.js';
import { promiseAll } from 'lib/utils/promises.js';
+import { prepareEncryptedIOSNotifications } from './crypto.js';
import { getAPNsNotificationTopic } from './providers.js';
import { rescindPushNotifs } from './rescind.js';
import {
@@ -64,6 +65,7 @@
type Device = {
+platform: Platform,
+deviceToken: string,
+ +cookieID: string,
+codeVersion: ?number,
};
type PushUserInfo = {
@@ -190,25 +192,32 @@
const iosVersionsToTokens = byPlatform.get('ios');
if (iosVersionsToTokens) {
- for (const [codeVersion, deviceTokens] of iosVersionsToTokens) {
+ for (const [
+ codeVersion,
+ { cookieIDs, deviceTokens },
+ ] of iosVersionsToTokens) {
const platformDetails = { platform: 'ios', codeVersion };
const shimmedNewRawMessageInfos = shimUnsupportedRawMessageInfos(
newRawMessageInfos,
platformDetails,
);
+
const deliveryPromise = (async () => {
- const notification = await prepareAPNsNotification({
- notifTexts,
- newRawMessageInfos: shimmedNewRawMessageInfos,
- threadID: threadInfo.id,
- collapseKey: notifInfo.collapseKey,
- badgeOnly,
- unreadCount: unreadCounts[userID],
- platformDetails,
- });
+ const notificationsArray = await prepareAPNsNotification(
+ {
+ notifTexts,
+ newRawMessageInfos: shimmedNewRawMessageInfos,
+ threadID: threadInfo.id,
+ collapseKey: notifInfo.collapseKey,
+ badgeOnly,
+ unreadCount: unreadCounts[userID],
+ platformDetails,
+ },
+ [...cookieIDs],
+ );
return await sendAPNsNotification(
'ios',
- notification,
+ notificationsArray,
[...deviceTokens],
{
...notificationInfo,
@@ -221,7 +230,7 @@
}
const androidVersionsToTokens = byPlatform.get('android');
if (androidVersionsToTokens) {
- for (const [codeVersion, deviceTokens] of androidVersionsToTokens) {
+ for (const [codeVersion, { deviceTokens }] of androidVersionsToTokens) {
const platformDetails = { platform: 'android', codeVersion };
const shimmedNewRawMessageInfos = shimUnsupportedRawMessageInfos(
newRawMessageInfos,
@@ -252,7 +261,7 @@
}
const webVersionsToTokens = byPlatform.get('web');
if (webVersionsToTokens) {
- for (const [codeVersion, deviceTokens] of webVersionsToTokens) {
+ for (const [codeVersion, { deviceTokens }] of webVersionsToTokens) {
const deliveryPromise = (async () => {
const notification = await prepareWebNotification({
notifTexts,
@@ -269,7 +278,7 @@
}
const macosVersionsToTokens = byPlatform.get('macos');
if (macosVersionsToTokens) {
- for (const [codeVersion, deviceTokens] of macosVersionsToTokens) {
+ for (const [codeVersion, { deviceTokens }] of macosVersionsToTokens) {
const platformDetails = { platform: 'macos', codeVersion };
const shimmedNewRawMessageInfos = shimUnsupportedRawMessageInfos(
newRawMessageInfos,
@@ -300,7 +309,7 @@
}
const windowsVersionsToTokens = byPlatform.get('windows');
if (windowsVersionsToTokens) {
- for (const [codeVersion, deviceTokens] of windowsVersionsToTokens) {
+ for (const [codeVersion, { deviceTokens }] of windowsVersionsToTokens) {
const deliveryPromise = (async () => {
const notification = await prepareWNSNotification({
notifTexts,
@@ -577,8 +586,11 @@
}
function getDevicesByPlatform(
- devices: Device[],
-): Map<Platform, Map<number, Set<string>>> {
+ devices: $ReadOnlyArray<Device>,
+): Map<
+ Platform,
+ Map<number, { cookieIDs: Set<string>, deviceTokens: Set<string> }>,
+> {
const byPlatform = new Map();
for (const device of devices) {
let innerMap = byPlatform.get(device.platform);
@@ -590,12 +602,14 @@
device.codeVersion !== null && device.codeVersion !== undefined
? device.codeVersion
: -1;
- let innerMostSet = innerMap.get(codeVersion);
- if (!innerMostSet) {
- innerMostSet = new Set();
- innerMap.set(codeVersion, innerMostSet);
+ let innerMostPair = innerMap.get(codeVersion);
+ if (!innerMostPair) {
+ innerMostPair = { cookieIDs: new Set(), deviceTokens: new Set() };
+ innerMap.set(codeVersion, innerMostPair);
}
- innerMostSet.add(device.deviceToken);
+ const { cookieIDs, deviceTokens } = innerMostPair;
+ cookieIDs.add(device.cookieID);
+ deviceTokens.add(device.deviceToken);
}
return byPlatform;
}
@@ -611,7 +625,8 @@
};
async function prepareAPNsNotification(
inputData: APNsNotifInputData,
-): Promise<apn.Notification> {
+ cookieIDs?: $ReadOnlyArray<string>,
+): Promise<Array<apn.Notification>> {
const {
notifTexts,
newRawMessageInfos,
@@ -622,6 +637,12 @@
platformDetails,
} = inputData;
+ const isTextNotification = newRawMessageInfos.every(
+ newRawMessageInfo => newRawMessageInfo.type === messageTypes.TEXT,
+ );
+ const shouldBeEncrypted =
+ platformDetails.platform === 'ios' && !collapseKey && isTextNotification;
+
const uniqueID = uuidv4();
const notification = new apn.Notification();
notification.topic = getAPNsNotificationTopic(platformDetails);
@@ -662,18 +683,43 @@
...copyWithMessageInfos.payload,
messageInfos,
};
- if (copyWithMessageInfos.length() <= apnMaxNotificationPayloadByteSize) {
- notification.payload.messageInfos = messageInfos;
- return notification;
+
+ let notifications, notificationsWithMessageInfos;
+ if (shouldBeEncrypted && cookieIDs) {
+ [notifications, notificationsWithMessageInfos] = await Promise.all([
+ prepareEncryptedIOSNotifications(cookieIDs, notification),
+ prepareEncryptedIOSNotifications(cookieIDs, copyWithMessageInfos),
+ ]);
+ } else {
+ notifications = [notification];
+ notificationsWithMessageInfos = [copyWithMessageInfos];
}
- const notificationCopy = _cloneDeep(notification);
- if (notificationCopy.length() > apnMaxNotificationPayloadByteSize) {
- console.warn(
- `${platformDetails.platform} notification ${uniqueID} ` +
- `exceeds size limit, even with messageInfos omitted`,
- );
+
+ const shouldAddMessageInfos = notificationsWithMessageInfos.map(notif => {
+ const copy = _cloneDeep(notif);
+ return copy.length() <= apnMaxNotificationPayloadByteSize;
+ });
+
+ const notificationsToSend = shouldAddMessageInfos.map(
+ (addMessageInfos, idx) => {
+ if (addMessageInfos) {
+ return notificationsWithMessageInfos[idx];
+ }
+ return notifications[idx];
+ },
+ );
+
+ for (const notifToSend of notificationsToSend) {
+ const copy = _cloneDeep(notifToSend);
+ if (copy.length() > apnMaxNotificationPayloadByteSize) {
+ console.warn(
+ `${platformDetails.platform} notification ${uniqueID} ` +
+ `exceeds size limit, even with messageInfos omitted`,
+ );
+ }
}
- return notification;
+
+ return notificationsToSend;
}
type AndroidNotifInputData = {
@@ -819,16 +865,18 @@
};
async function sendAPNsNotification(
platform: 'ios' | 'macos',
- notification: apn.Notification,
+ notifications: $ReadOnlyArray<apn.Notification>,
deviceTokens: $ReadOnlyArray<string>,
notificationInfo: NotificationInfo,
): Promise<APNsResult> {
const { source, codeVersion } = notificationInfo;
+
const response = await apnPush({
- notification,
+ notifications,
deviceTokens,
platformDetails: { platform, codeVersion },
});
+ const [notification] = notifications;
const delivery: APNsDelivery = {
source,
deviceType: platform,
@@ -1078,7 +1126,10 @@
const iosVersionsToTokens = byPlatform.get('ios');
if (iosVersionsToTokens) {
- for (const [codeVersion, deviceTokens] of iosVersionsToTokens) {
+ for (const [
+ codeVersion,
+ { cookieIDs, deviceTokens },
+ ] of iosVersionsToTokens) {
const notification = new apn.Notification();
notification.topic = getAPNsNotificationTopic({
platform: 'ios',
@@ -1086,8 +1137,12 @@
});
notification.badge = unreadCount;
notification.pushType = 'alert';
+ const notificationsArray = await prepareEncryptedIOSNotifications(
+ [...cookieIDs],
+ notification,
+ );
deliveryPromises.push(
- sendAPNsNotification('ios', notification, [...deviceTokens], {
+ sendAPNsNotification('ios', notificationsArray, [...deviceTokens], {
source,
dbID,
userID,
@@ -1099,7 +1154,7 @@
const androidVersionsToTokens = byPlatform.get('android');
if (androidVersionsToTokens) {
- for (const [codeVersion, deviceTokens] of androidVersionsToTokens) {
+ for (const [codeVersion, { deviceTokens }] of androidVersionsToTokens) {
const notificationData =
codeVersion < 69
? { badge: unreadCount.toString() }
@@ -1118,7 +1173,7 @@
const macosVersionsToTokens = byPlatform.get('macos');
if (macosVersionsToTokens) {
- for (const [codeVersion, deviceTokens] of macosVersionsToTokens) {
+ for (const [codeVersion, { deviceTokens }] of macosVersionsToTokens) {
const notification = new apn.Notification();
notification.topic = getAPNsNotificationTopic({
platform: 'macos',
@@ -1127,7 +1182,7 @@
notification.badge = unreadCount;
notification.pushType = 'alert';
deliveryPromises.push(
- sendAPNsNotification('macos', notification, [...deviceTokens], {
+ sendAPNsNotification('macos', [notification], [...deviceTokens], {
source,
dbID,
userID,
diff --git a/keyserver/src/push/utils.js b/keyserver/src/push/utils.js
--- a/keyserver/src/push/utils.js
+++ b/keyserver/src/push/utils.js
@@ -46,11 +46,11 @@
+invalidTokens?: $ReadOnlyArray<string>,
};
async function apnPush({
- notification,
+ notifications,
deviceTokens,
platformDetails,
}: {
- +notification: apn.Notification,
+ +notifications: $ReadOnlyArray<apn.Notification>,
+deviceTokens: $ReadOnlyArray<string>,
+platformDetails: PlatformDetails,
}): Promise<APNPushResult> {
@@ -61,10 +61,22 @@
return { success: true };
}
invariant(apnProvider, `keyserver/secrets/${pushProfile}.json should exist`);
- const result = await apnProvider.send(notification, deviceTokens);
+
+ const results = await Promise.all(
+ notifications.map((notification, idx) => {
+ return apnProvider.send(notification, deviceTokens[idx]);
+ }),
+ );
+
+ const mergedResults = { sent: [], failed: [] };
+ for (const result of results) {
+ mergedResults.sent.push(...result.sent);
+ mergedResults.failed.push(...result.failed);
+ }
+
const errors = [];
const invalidTokens = [];
- for (const error of result.failed) {
+ for (const error of mergedResults.failed) {
errors.push(error);
/* eslint-disable eqeqeq */
if (

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 26, 6:55 PM (11 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2708175
Default Alt Text
D7815.id26613.diff (14 KB)

Event Timeline