mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-26 19:34:04 +01:00
Refactor build expiration checks
This commit is contained in:
@@ -89,6 +89,8 @@ export type SocketStatuses = Record<
|
||||
SocketInfo
|
||||
>;
|
||||
|
||||
export type SocketExpirationReason = 'remote' | 'build';
|
||||
|
||||
// This class manages two websocket resources:
|
||||
//
|
||||
// - Authenticated IWebSocketResource which uses supplied WebAPICredentials and
|
||||
@@ -123,7 +125,7 @@ export class SocketManager extends EventListener {
|
||||
#incomingRequestQueue = new Array<IncomingWebSocketRequest>();
|
||||
#isNavigatorOffline = false;
|
||||
#privIsOnline: boolean | undefined;
|
||||
#isRemotelyExpired = false;
|
||||
#expirationReason: SocketExpirationReason | undefined;
|
||||
#hasStoriesDisabled: boolean;
|
||||
#reconnectController: AbortController | undefined;
|
||||
#envelopeCount = 0;
|
||||
@@ -145,17 +147,29 @@ export class SocketManager extends EventListener {
|
||||
}
|
||||
|
||||
#markOffline() {
|
||||
if (this.#privIsOnline !== false) {
|
||||
this.#privIsOnline = false;
|
||||
this.emit('offline');
|
||||
// Note: `#privIsOnline` starts as `undefined` so that we emit the first
|
||||
// `offline` event.
|
||||
if (this.#privIsOnline === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#privIsOnline = false;
|
||||
this.emit('offline');
|
||||
}
|
||||
|
||||
#markOnline() {
|
||||
if (this.#privIsOnline === true) {
|
||||
return;
|
||||
}
|
||||
this.#privIsOnline = true;
|
||||
this.emit('online');
|
||||
}
|
||||
|
||||
// Update WebAPICredentials and reconnect authenticated resource if
|
||||
// credentials changed
|
||||
public async authenticate(credentials: WebAPICredentials): Promise<void> {
|
||||
if (this.#isRemotelyExpired) {
|
||||
throw new HTTPError('SocketManager remotely expired', {
|
||||
if (this.#expirationReason != null) {
|
||||
throw new HTTPError(`SocketManager ${this.#expirationReason} expired`, {
|
||||
code: 0,
|
||||
headers: {},
|
||||
stack: new Error().stack,
|
||||
@@ -240,8 +254,11 @@ export class SocketManager extends EventListener {
|
||||
this.#authenticated = process;
|
||||
|
||||
const reconnect = async (): Promise<void> => {
|
||||
if (this.#isRemotelyExpired) {
|
||||
log.info('SocketManager: remotely expired, not reconnecting');
|
||||
if (this.#expirationReason != null) {
|
||||
log.info(
|
||||
`SocketManager: ${this.#expirationReason} expired, ` +
|
||||
'not reconnecting'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -409,8 +426,11 @@ export class SocketManager extends EventListener {
|
||||
handler: IRequestHandler,
|
||||
timeout?: number
|
||||
): Promise<IWebSocketResource> {
|
||||
if (this.#isRemotelyExpired) {
|
||||
throw new Error('Remotely expired, not connecting provisioning socket');
|
||||
if (this.#expirationReason != null) {
|
||||
throw new Error(
|
||||
`${this.#expirationReason} expired, ` +
|
||||
'not connecting provisioning socket'
|
||||
);
|
||||
}
|
||||
|
||||
return this.#connectResource({
|
||||
@@ -597,12 +617,15 @@ export class SocketManager extends EventListener {
|
||||
await this.check();
|
||||
}
|
||||
|
||||
public async onRemoteExpiration(): Promise<void> {
|
||||
log.info('SocketManager.onRemoteExpiration');
|
||||
this.#isRemotelyExpired = true;
|
||||
public async onExpiration(reason: SocketExpirationReason): Promise<void> {
|
||||
log.info('SocketManager.onRemoteExpiration', reason);
|
||||
this.#expirationReason = reason;
|
||||
|
||||
// Cancel reconnect attempt if any
|
||||
this.#reconnectController?.abort();
|
||||
|
||||
// Logout
|
||||
await this.logout();
|
||||
}
|
||||
|
||||
public async logout(): Promise<void> {
|
||||
@@ -636,10 +659,7 @@ export class SocketManager extends EventListener {
|
||||
this.#authenticatedStatus.lastConnectionTransport =
|
||||
newStatus.transportOption;
|
||||
|
||||
if (!this.#privIsOnline) {
|
||||
this.#privIsOnline = true;
|
||||
this.emit('online');
|
||||
}
|
||||
this.#markOnline();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,6 +702,14 @@ export class SocketManager extends EventListener {
|
||||
}
|
||||
|
||||
async #getUnauthenticatedResource(): Promise<IWebSocketResource> {
|
||||
if (this.#expirationReason) {
|
||||
throw new HTTPError(`SocketManager ${this.#expirationReason} expired`, {
|
||||
code: 0,
|
||||
headers: {},
|
||||
stack: new Error().stack,
|
||||
});
|
||||
}
|
||||
|
||||
// awaiting on `this.getProxyAgent()` needs to happen here
|
||||
// so that there are no calls to `await` between checking
|
||||
// the value of `this.unauthenticated` and assigning it later in this function
|
||||
@@ -691,14 +719,6 @@ export class SocketManager extends EventListener {
|
||||
return this.#unauthenticated.getResult();
|
||||
}
|
||||
|
||||
if (this.#isRemotelyExpired) {
|
||||
throw new HTTPError('SocketManager remotely expired', {
|
||||
code: 0,
|
||||
headers: {},
|
||||
stack: new Error().stack,
|
||||
});
|
||||
}
|
||||
|
||||
log.info('SocketManager: connecting unauthenticated socket');
|
||||
|
||||
const transportOption = this.#transportOption();
|
||||
|
||||
@@ -56,7 +56,11 @@ import { getRandomBytes, randomInt } from '../Crypto';
|
||||
import * as linkPreviewFetch from '../linkPreviews/linkPreviewFetch';
|
||||
import { isBadgeImageFileUrlValid } from '../badges/isBadgeImageFileUrlValid';
|
||||
|
||||
import { SocketManager, type SocketStatuses } from './SocketManager';
|
||||
import {
|
||||
SocketManager,
|
||||
type SocketStatuses,
|
||||
type SocketExpirationReason,
|
||||
} from './SocketManager';
|
||||
import type { CDSAuthType, CDSResponseType } from './cds/Types.d';
|
||||
import { CDSI } from './cds/CDSI';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
@@ -87,6 +91,7 @@ import { isProduction } from '../util/version';
|
||||
import type { ServerAlert } from '../util/handleServerAlerts';
|
||||
import { isAbortError } from '../util/isAbortError';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import { drop } from '../util/drop';
|
||||
|
||||
// Note: this will break some code that expects to be able to use err.response when a
|
||||
// web request fails, because it will force it to text. But it is very useful for
|
||||
@@ -817,6 +822,7 @@ type AjaxOptionsType<Type extends AjaxResponseType, OutputShape = unknown> = (
|
||||
|
||||
export type WebAPIConnectOptionsType = WebAPICredentials & {
|
||||
hasStoriesDisabled: boolean;
|
||||
hasBuildExpired: boolean;
|
||||
};
|
||||
|
||||
export type WebAPIConnectType = {
|
||||
@@ -1700,7 +1706,7 @@ export type WebAPIType = {
|
||||
isOnline: () => boolean | undefined;
|
||||
onNavigatorOnline: () => Promise<void>;
|
||||
onNavigatorOffline: () => Promise<void>;
|
||||
onRemoteExpiration: () => Promise<void>;
|
||||
onExpiration: (reason: SocketExpirationReason) => Promise<void>;
|
||||
reconnect: () => Promise<void>;
|
||||
};
|
||||
|
||||
@@ -1880,6 +1886,7 @@ export function initialize({
|
||||
username: initialUsername,
|
||||
password: initialPassword,
|
||||
hasStoriesDisabled,
|
||||
hasBuildExpired,
|
||||
}: WebAPIConnectOptionsType) {
|
||||
let username = initialUsername;
|
||||
let password = initialPassword;
|
||||
@@ -1933,7 +1940,11 @@ export function initialize({
|
||||
serverAlerts = alerts;
|
||||
});
|
||||
|
||||
void socketManager.authenticate({ username, password });
|
||||
if (hasBuildExpired) {
|
||||
drop(socketManager.onExpiration('build'));
|
||||
}
|
||||
|
||||
drop(socketManager.authenticate({ username, password }));
|
||||
|
||||
const cds = new CDSI(libsignalNet, {
|
||||
logger: log,
|
||||
@@ -2071,7 +2082,7 @@ export function initialize({
|
||||
isOnline,
|
||||
onNavigatorOffline,
|
||||
onNavigatorOnline,
|
||||
onRemoteExpiration,
|
||||
onExpiration,
|
||||
postBatchIdentityCheck,
|
||||
putEncryptedAttachment,
|
||||
putProfile,
|
||||
@@ -2283,8 +2294,8 @@ export function initialize({
|
||||
await socketManager.onNavigatorOffline();
|
||||
}
|
||||
|
||||
async function onRemoteExpiration(): Promise<void> {
|
||||
await socketManager.onRemoteExpiration();
|
||||
async function onExpiration(reason: SocketExpirationReason): Promise<void> {
|
||||
await socketManager.onExpiration(reason);
|
||||
}
|
||||
|
||||
async function reconnect(): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user