Add mock test for libsignal websockets
This commit is contained in:
@@ -219,7 +219,7 @@
|
||||
"@indutny/parallel-prettier": "3.0.0",
|
||||
"@indutny/rezip-electron": "2.0.1",
|
||||
"@napi-rs/canvas": "0.1.61",
|
||||
"@signalapp/mock-server": "10.5.0",
|
||||
"@signalapp/mock-server": "11.0.0",
|
||||
"@storybook/addon-a11y": "8.4.4",
|
||||
"@storybook/addon-actions": "8.4.4",
|
||||
"@storybook/addon-controls": "8.4.4",
|
||||
|
17
pnpm-lock.yaml
generated
17
pnpm-lock.yaml
generated
@@ -432,8 +432,8 @@ importers:
|
||||
specifier: 0.1.61
|
||||
version: 0.1.61
|
||||
'@signalapp/mock-server':
|
||||
specifier: 10.5.0
|
||||
version: 10.5.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
specifier: 11.0.0
|
||||
version: 11.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
'@storybook/addon-a11y':
|
||||
specifier: 8.4.4
|
||||
version: 8.4.4(storybook@8.4.4(bufferutil@4.0.9)(prettier@3.3.3)(utf-8-validate@5.0.10))
|
||||
@@ -2530,8 +2530,8 @@ packages:
|
||||
'@signalapp/libsignal-client@0.66.2':
|
||||
resolution: {integrity: sha512-zwgU0LXIJjJNVBxJWnjbJN1+gIUJ+eY+BnqIP1d0751Sq1zy5VMHs+/5nmEyVGNn6ptD0qseYXkxtr/XLs07Lw==}
|
||||
|
||||
'@signalapp/mock-server@10.5.0':
|
||||
resolution: {integrity: sha512-w7KXcWYRPXhAxYWzeBNesu+A9DO6FYJUhg52md3M9yQkPR2Yr/YBvc9zBhFo5fafZmkaRoxDoz9y29Ivd5ZykQ==}
|
||||
'@signalapp/mock-server@11.0.0':
|
||||
resolution: {integrity: sha512-JHEqdjXvWcXyLJ90OtaTIQVtizH/w+rmnPplNmHuUiDZIQIEvE3lRyZFyIvgVTNg29lQWs/Gw/o+hICerdOChQ==}
|
||||
|
||||
'@signalapp/parchment-cjs@3.0.1':
|
||||
resolution: {integrity: sha512-hSBMQ1M7wE4GcC8ZeNtvpJF+DAJg3eIRRf1SiHS3I3Algav/sgJJNm6HIYm6muHuK7IJmuEjkL3ILSXgmu0RfQ==}
|
||||
@@ -6802,9 +6802,6 @@ packages:
|
||||
resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
long@4.0.0:
|
||||
resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==}
|
||||
|
||||
long@5.2.3:
|
||||
resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==}
|
||||
|
||||
@@ -12262,7 +12259,7 @@ snapshots:
|
||||
type-fest: 4.26.1
|
||||
uuid: 8.3.2
|
||||
|
||||
'@signalapp/mock-server@10.5.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
|
||||
'@signalapp/mock-server@11.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@indutny/parallel-prettier': 3.0.0(prettier@3.3.3)
|
||||
'@signalapp/libsignal-client': 0.60.2
|
||||
@@ -12270,7 +12267,7 @@ snapshots:
|
||||
'@tus/server': 1.10.2
|
||||
debug: 4.3.7(supports-color@8.1.1)
|
||||
is-plain-obj: 3.0.0
|
||||
long: 4.0.0
|
||||
long: 5.2.3
|
||||
micro: 9.4.1
|
||||
microrouter: 3.1.3
|
||||
prettier: 3.3.3
|
||||
@@ -17566,8 +17563,6 @@ snapshots:
|
||||
|
||||
loglevel@1.9.2: {}
|
||||
|
||||
long@4.0.0: {}
|
||||
|
||||
long@5.2.3: {}
|
||||
|
||||
longest-streak@2.0.4: {}
|
||||
|
7
ts/CI.ts
7
ts/CI.ts
@@ -17,6 +17,7 @@ import { SECOND } from './util/durations';
|
||||
import { isSignalRoute } from './util/signalRoutes';
|
||||
import { strictAssert } from './util/assert';
|
||||
import { MessageModel } from './models/messages';
|
||||
import type { SocketStatuses } from './textsecure/SocketManager';
|
||||
|
||||
type ResolveType = (data: unknown) => void;
|
||||
|
||||
@@ -28,6 +29,7 @@ export type CIType = {
|
||||
sentAt: number
|
||||
): Promise<ReadonlyArray<MessageAttributesType>>;
|
||||
getPendingEventCount: (event: string) => number;
|
||||
getSocketStatus: () => SocketStatuses;
|
||||
handleEvent: (event: string, data: unknown) => unknown;
|
||||
setProvisioningURL: (url: string) => unknown;
|
||||
solveChallenge: (response: ChallengeResponseType) => unknown;
|
||||
@@ -202,6 +204,10 @@ export function getCI({
|
||||
handleEvent('print', format(...args));
|
||||
}
|
||||
|
||||
function getSocketStatus() {
|
||||
return window.getSocketStatus();
|
||||
}
|
||||
|
||||
async function resetReleaseNotesFetcher() {
|
||||
await Promise.all([
|
||||
window.textsecure.storage.put(
|
||||
@@ -218,6 +224,7 @@ export function getCI({
|
||||
getConversationId,
|
||||
createNotificationToken,
|
||||
getMessagesBySentAt,
|
||||
getSocketStatus,
|
||||
handleEvent,
|
||||
setProvisioningURL,
|
||||
solveChallenge,
|
||||
|
@@ -398,7 +398,10 @@ export async function startApp(): Promise<void> {
|
||||
|
||||
window.getSocketStatus = () => {
|
||||
if (server === undefined) {
|
||||
return SocketStatus.CLOSED;
|
||||
return {
|
||||
authenticated: { status: SocketStatus.CLOSED },
|
||||
unauthenticated: { status: SocketStatus.CLOSED },
|
||||
};
|
||||
}
|
||||
return server.getSocketStatus();
|
||||
};
|
||||
@@ -1141,7 +1144,8 @@ export async function startApp(): Promise<void> {
|
||||
setupAppState();
|
||||
drop(start());
|
||||
window.Signal.Services.initializeNetworkObserver(
|
||||
window.reduxActions.network
|
||||
window.reduxActions.network,
|
||||
() => window.getSocketStatus().authenticated.status
|
||||
);
|
||||
window.Signal.Services.initializeUpdateListener(
|
||||
window.reduxActions.updates
|
||||
@@ -1947,7 +1951,7 @@ export async function startApp(): Promise<void> {
|
||||
window.addEventListener('offline', onNavigatorOffline);
|
||||
|
||||
window.Whisper.events.on('socketStatusChange', () => {
|
||||
if (window.getSocketStatus() === SocketStatus.OPEN) {
|
||||
if (window.getSocketStatus().authenticated.status === SocketStatus.OPEN) {
|
||||
pauseQueuesAndNotificationsOnSocketConnect();
|
||||
}
|
||||
});
|
||||
@@ -1976,7 +1980,7 @@ export async function startApp(): Promise<void> {
|
||||
}
|
||||
|
||||
function isSocketOnline() {
|
||||
const socketStatus = window.getSocketStatus();
|
||||
const socketStatus = window.getSocketStatus().authenticated.status;
|
||||
return (
|
||||
socketStatus === SocketStatus.CONNECTING ||
|
||||
socketStatus === SocketStatus.OPEN
|
||||
|
@@ -61,7 +61,7 @@ export function Inbox({
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const status = window.getSocketStatus();
|
||||
const { status } = window.getSocketStatus().authenticated;
|
||||
switch (status) {
|
||||
case 'CONNECTING':
|
||||
break;
|
||||
|
@@ -5,7 +5,6 @@ import type {
|
||||
SetNetworkStatusPayloadType,
|
||||
NetworkActionType,
|
||||
} from '../state/ducks/network';
|
||||
import { getSocketStatus } from '../shims/socketStatus';
|
||||
import * as log from '../logging/log';
|
||||
import { SECOND } from '../util/durations';
|
||||
import { electronLookup } from '../util/dns';
|
||||
@@ -31,14 +30,15 @@ type NetworkActions = {
|
||||
};
|
||||
|
||||
export function initializeNetworkObserver(
|
||||
networkActions: NetworkActions
|
||||
networkActions: NetworkActions,
|
||||
getAuthSocketStatus: () => SocketStatus
|
||||
): void {
|
||||
log.info('Initializing network observer');
|
||||
|
||||
let onlineStatus = OnlineStatus.Online;
|
||||
|
||||
const refresh = () => {
|
||||
const socketStatus = getSocketStatus();
|
||||
const socketStatus = getAuthSocketStatus();
|
||||
|
||||
networkActions.setNetworkStatus({
|
||||
isOnline: onlineStatus !== OnlineStatus.Offline,
|
||||
|
@@ -1,10 +0,0 @@
|
||||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { SocketStatus } from '../types/SocketStatus';
|
||||
|
||||
export function getSocketStatus(): SocketStatus {
|
||||
const { getSocketStatus: getMessageReceiverStatus } = window;
|
||||
|
||||
return getMessageReceiverStatus();
|
||||
}
|
@@ -142,6 +142,15 @@ function sanitizePathComponent(component: string): string {
|
||||
return normalizePath(component.replace(/[^a-z]+/gi, '-'));
|
||||
}
|
||||
|
||||
const DEFAULT_REMOTE_CONFIG = [
|
||||
['desktop.backup.credentialFetch', { enabled: true }],
|
||||
['desktop.internalUser', { enabled: true }],
|
||||
['desktop.releaseNotes', { enabled: true }],
|
||||
['desktop.senderKey.retry', { enabled: true }],
|
||||
['global.groupsv2.groupSizeHardLimit', { enabled: true, value: '64' }],
|
||||
['global.groupsv2.maxGroupSize', { enabled: true, value: '32' }],
|
||||
] as const;
|
||||
|
||||
//
|
||||
// Bootstrap is a class that prepares mock server and desktop for running
|
||||
// tests/benchmarks.
|
||||
@@ -266,6 +275,10 @@ export class Bootstrap {
|
||||
path.join(os.tmpdir(), 'mock-signal-')
|
||||
);
|
||||
|
||||
DEFAULT_REMOTE_CONFIG.forEach(([key, value]) =>
|
||||
this.server.setRemoteConfig(key, value)
|
||||
);
|
||||
|
||||
debug('setting storage path=%j', this.#storagePath);
|
||||
}
|
||||
|
||||
@@ -725,7 +738,7 @@ export class Bootstrap {
|
||||
storagePath: this.#storagePath,
|
||||
storageProfile: 'mock',
|
||||
serverUrl: url,
|
||||
storageUrl: url,
|
||||
storageUrl: `${url}/storageService`,
|
||||
resourcesUrl: `${url}/updates2`,
|
||||
sfuUrl: url,
|
||||
cdn: {
|
||||
|
95
ts/test-mock/network/libsignal_test.ts
Normal file
95
ts/test-mock/network/libsignal_test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import createDebug from 'debug';
|
||||
import { assert } from 'chai';
|
||||
import { type PrimaryDevice, StorageState } from '@signalapp/mock-server';
|
||||
|
||||
import type { App } from '../playwright';
|
||||
import { Bootstrap } from '../bootstrap';
|
||||
import { typeIntoInput, waitForEnabledComposer } from '../helpers';
|
||||
import { MINUTE } from '../../util/durations';
|
||||
|
||||
export const debug = createDebug('mock:test:libsignal');
|
||||
|
||||
describe('Libsignal-net', function (this: Mocha.Suite) {
|
||||
this.timeout(MINUTE);
|
||||
let bootstrap: Bootstrap;
|
||||
let app: App;
|
||||
let contact: PrimaryDevice;
|
||||
|
||||
beforeEach(async () => {
|
||||
bootstrap = new Bootstrap();
|
||||
await bootstrap.init();
|
||||
[contact] = bootstrap.contacts;
|
||||
|
||||
let state = StorageState.getEmpty();
|
||||
|
||||
state = state.addContact(contact, {
|
||||
identityKey: contact.publicKey.serialize(),
|
||||
profileKey: contact.profileKey.serialize(),
|
||||
whitelisted: true,
|
||||
});
|
||||
|
||||
await bootstrap.phone.setStorageState(state);
|
||||
|
||||
bootstrap.server.setRemoteConfig(
|
||||
'desktop.experimentalTransportEnabled.alpha',
|
||||
{ enabled: true }
|
||||
);
|
||||
|
||||
bootstrap.server.setRemoteConfig(
|
||||
'desktop.experimentalTransport.enableAuth',
|
||||
{ enabled: true }
|
||||
);
|
||||
|
||||
// Link & close so that app can get remote config first over non-libsignal websocket,
|
||||
// and then on next app start it will connect via libsignal
|
||||
await bootstrap.linkAndClose();
|
||||
app = await bootstrap.startApp();
|
||||
});
|
||||
|
||||
afterEach(async function (this: Mocha.Context) {
|
||||
if (!bootstrap) {
|
||||
return;
|
||||
}
|
||||
|
||||
await bootstrap.maybeSaveLogs(this.currentTest, app);
|
||||
await app.close();
|
||||
await bootstrap.teardown();
|
||||
});
|
||||
|
||||
it('can send and receive messages', async () => {
|
||||
const window = await app.getWindow();
|
||||
const { desktop } = bootstrap;
|
||||
|
||||
debug('receiving incoming message');
|
||||
await contact.sendText(bootstrap.desktop, 'incoming message');
|
||||
|
||||
debug('ensuring app received message, opening conversation');
|
||||
{
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
const item = leftPane
|
||||
.getByTestId(contact.toContact().aci)
|
||||
.getByText('incoming message');
|
||||
await item.click();
|
||||
}
|
||||
|
||||
debug('sending outgoing message');
|
||||
const input = await waitForEnabledComposer(window);
|
||||
await typeIntoInput(input, 'outgoing message');
|
||||
await input.press('Enter');
|
||||
|
||||
debug('waiting for message on server side');
|
||||
const { body, source } = await contact.waitForMessage();
|
||||
assert.strictEqual(body, 'outgoing message');
|
||||
assert.strictEqual(source, desktop);
|
||||
|
||||
debug('confirming app successfully sent message');
|
||||
await app.waitForMessageSend();
|
||||
|
||||
debug('confirming that app was actually using libsignal');
|
||||
const { authenticated, unauthenticated } = await app.getSocketStatus();
|
||||
assert.strictEqual(authenticated.lastConnectionTransport, 'libsignal');
|
||||
assert.strictEqual(unauthenticated.lastConnectionTransport, 'libsignal');
|
||||
});
|
||||
});
|
@@ -14,6 +14,7 @@ import type { ReceiptType } from '../types/Receipt';
|
||||
import { SECOND } from '../util/durations';
|
||||
import { drop } from '../util/drop';
|
||||
import type { MessageAttributesType } from '../model-types';
|
||||
import type { SocketStatuses } from '../textsecure/SocketManager';
|
||||
|
||||
export type AppLoadedInfoType = Readonly<{
|
||||
loadTime: number;
|
||||
@@ -187,6 +188,11 @@ export class App extends EventEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
public async getSocketStatus(): Promise<SocketStatuses> {
|
||||
const window = await this.getWindow();
|
||||
return window.evaluate('window.SignalCI.getSocketStatus()');
|
||||
}
|
||||
|
||||
public async getMessagesBySentAt(
|
||||
timestamp: number
|
||||
): Promise<Array<MessageAttributesType>> {
|
||||
|
@@ -27,6 +27,10 @@ describe('release notes', function (this: Mocha.Suite) {
|
||||
bootstrap = new Bootstrap();
|
||||
await bootstrap.init();
|
||||
|
||||
bootstrap.server.setRemoteConfig('desktop.releaseNotes', {
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
app = await bootstrap.link();
|
||||
});
|
||||
|
||||
|
@@ -26,7 +26,7 @@ import { sleep } from '../util/sleep';
|
||||
import { drop } from '../util/drop';
|
||||
import type { ProxyAgent } from '../util/createProxyAgent';
|
||||
import { createProxyAgent } from '../util/createProxyAgent';
|
||||
import { SocketStatus } from '../types/SocketStatus';
|
||||
import { type SocketInfo, SocketStatus } from '../types/SocketStatus';
|
||||
import * as Errors from '../types/errors';
|
||||
import * as Bytes from '../Bytes';
|
||||
import * as log from '../logging/log';
|
||||
@@ -39,6 +39,7 @@ import type {
|
||||
import WebSocketResource, {
|
||||
connectAuthenticatedLibsignal,
|
||||
connectUnauthenticatedLibsignal,
|
||||
LibsignalWebSocketResource,
|
||||
ServerRequestType,
|
||||
TransportOption,
|
||||
WebSocketResourceWithShadowing,
|
||||
@@ -48,6 +49,7 @@ import type { IRequestHandler, WebAPICredentials } from './Types.d';
|
||||
import { connect as connectWebSocket } from './WebSocket';
|
||||
import { isNightly, isBeta, isStaging } from '../util/version';
|
||||
import { getBasicAuth } from '../util/getBasicAuth';
|
||||
import { isTestOrMockEnvironment } from '../environment';
|
||||
|
||||
const FIVE_MINUTES = 5 * durations.MINUTE;
|
||||
|
||||
@@ -68,6 +70,20 @@ export type SocketManagerOptions = Readonly<{
|
||||
hasStoriesDisabled: boolean;
|
||||
}>;
|
||||
|
||||
type SocketStatusUpdate =
|
||||
| {
|
||||
status: SocketStatus.OPEN;
|
||||
transportOption: TransportOption.Libsignal | TransportOption.Original;
|
||||
}
|
||||
| {
|
||||
status: Exclude<SocketStatus, SocketStatus.OPEN>;
|
||||
};
|
||||
|
||||
export type SocketStatuses = Record<
|
||||
'authenticated' | 'unauthenticated',
|
||||
SocketInfo
|
||||
>;
|
||||
|
||||
// This class manages two websocket resources:
|
||||
//
|
||||
// - Authenticated IWebSocketResource which uses supplied WebAPICredentials and
|
||||
@@ -92,7 +108,12 @@ export class SocketManager extends EventListener {
|
||||
#unauthenticatedExpirationTimer?: NodeJS.Timeout;
|
||||
#credentials?: WebAPICredentials;
|
||||
#lazyProxyAgent?: Promise<ProxyAgent>;
|
||||
#status = SocketStatus.CLOSED;
|
||||
#authenticatedStatus: SocketInfo = {
|
||||
status: SocketStatus.CLOSED,
|
||||
};
|
||||
#unathenticatedStatus: SocketInfo = {
|
||||
status: SocketStatus.CLOSED,
|
||||
};
|
||||
#requestHandlers = new Set<IRequestHandler>();
|
||||
#incomingRequestQueue = new Array<IncomingWebSocketRequest>();
|
||||
#isNavigatorOffline = false;
|
||||
@@ -111,8 +132,11 @@ export class SocketManager extends EventListener {
|
||||
this.#hasStoriesDisabled = options.hasStoriesDisabled;
|
||||
}
|
||||
|
||||
public getStatus(): SocketStatus {
|
||||
return this.#status;
|
||||
public getStatus(): SocketStatuses {
|
||||
return {
|
||||
authenticated: this.#authenticatedStatus,
|
||||
unauthenticated: this.#unathenticatedStatus,
|
||||
};
|
||||
}
|
||||
|
||||
#markOffline() {
|
||||
@@ -163,7 +187,7 @@ export class SocketManager extends EventListener {
|
||||
`(hasStoriesDisabled=${this.#hasStoriesDisabled})`
|
||||
);
|
||||
|
||||
this.#setStatus(SocketStatus.CONNECTING);
|
||||
this.#setAuthenticatedStatus({ status: SocketStatus.CONNECTING });
|
||||
|
||||
const proxyAgent = await this.#getProxyAgent();
|
||||
const useLibsignalTransport =
|
||||
@@ -252,7 +276,14 @@ export class SocketManager extends EventListener {
|
||||
let authenticated: IWebSocketResource;
|
||||
try {
|
||||
authenticated = await process.getResult();
|
||||
this.#setStatus(SocketStatus.OPEN);
|
||||
|
||||
this.#setAuthenticatedStatus({
|
||||
status: SocketStatus.OPEN,
|
||||
transportOption:
|
||||
authenticated instanceof LibsignalWebSocketResource
|
||||
? TransportOption.Libsignal
|
||||
: TransportOption.Original,
|
||||
});
|
||||
} catch (error) {
|
||||
log.warn(
|
||||
'SocketManager: authenticated socket connection failed with ' +
|
||||
@@ -290,6 +321,11 @@ export class SocketManager extends EventListener {
|
||||
) {
|
||||
this.emit('authError');
|
||||
return;
|
||||
} else if (
|
||||
error instanceof LibSignalErrorBase &&
|
||||
error.code === ErrorCode.IoError
|
||||
) {
|
||||
this.#markOffline();
|
||||
} else if (
|
||||
error instanceof LibSignalErrorBase &&
|
||||
error.code === ErrorCode.AppExpired
|
||||
@@ -566,21 +602,44 @@ export class SocketManager extends EventListener {
|
||||
// Private
|
||||
//
|
||||
|
||||
#setStatus(status: SocketStatus): void {
|
||||
if (this.#status === status) {
|
||||
#setAuthenticatedStatus(newStatus: SocketStatusUpdate): void {
|
||||
if (this.#authenticatedStatus.status === newStatus.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#status = status;
|
||||
this.#authenticatedStatus.status = newStatus.status;
|
||||
this.emit('statusChange');
|
||||
|
||||
if (this.#status === SocketStatus.OPEN && !this.#privIsOnline) {
|
||||
this.#privIsOnline = true;
|
||||
this.emit('online');
|
||||
if (newStatus.status === SocketStatus.OPEN) {
|
||||
this.#authenticatedStatus.lastConnectionTimestamp = Date.now();
|
||||
this.#authenticatedStatus.lastConnectionTransport =
|
||||
newStatus.transportOption;
|
||||
|
||||
if (!this.#privIsOnline) {
|
||||
this.#privIsOnline = true;
|
||||
this.emit('online');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#setUnauthenticatedStatus(newStatus: SocketStatusUpdate): void {
|
||||
this.#unathenticatedStatus.status = newStatus.status;
|
||||
|
||||
if (newStatus.status === SocketStatus.OPEN) {
|
||||
this.#unathenticatedStatus.lastConnectionTimestamp = Date.now();
|
||||
this.#unathenticatedStatus.lastConnectionTransport =
|
||||
newStatus.transportOption;
|
||||
}
|
||||
}
|
||||
|
||||
#transportOption(): TransportOption {
|
||||
if (isTestOrMockEnvironment()) {
|
||||
const configValue = window.Signal.RemoteConfig.isEnabled(
|
||||
'desktop.experimentalTransportEnabled.alpha'
|
||||
);
|
||||
return configValue ? TransportOption.Libsignal : TransportOption.Original;
|
||||
}
|
||||
|
||||
// in staging, switch to using libsignal transport
|
||||
if (isStaging(this.options.version)) {
|
||||
return TransportOption.Libsignal;
|
||||
@@ -641,6 +700,10 @@ export class SocketManager extends EventListener {
|
||||
`SocketManager: connecting unauthenticated socket, transport option [${transportOption}]`
|
||||
);
|
||||
|
||||
this.#setUnauthenticatedStatus({
|
||||
status: SocketStatus.CONNECTING,
|
||||
});
|
||||
|
||||
let process: AbortableProcess<IWebSocketResource>;
|
||||
|
||||
if (transportOption === TransportOption.Libsignal) {
|
||||
@@ -667,6 +730,13 @@ export class SocketManager extends EventListener {
|
||||
let unauthenticated: IWebSocketResource;
|
||||
try {
|
||||
unauthenticated = await this.#unauthenticated.getResult();
|
||||
this.#setUnauthenticatedStatus({
|
||||
status: SocketStatus.OPEN,
|
||||
transportOption:
|
||||
unauthenticated instanceof LibsignalWebSocketResource
|
||||
? TransportOption.Libsignal
|
||||
: TransportOption.Original,
|
||||
});
|
||||
} catch (error) {
|
||||
log.info(
|
||||
'SocketManager: failed to connect unauthenticated socket ' +
|
||||
@@ -824,7 +894,7 @@ export class SocketManager extends EventListener {
|
||||
|
||||
this.#incomingRequestQueue = [];
|
||||
this.#authenticated = undefined;
|
||||
this.#setStatus(SocketStatus.CLOSED);
|
||||
this.#setAuthenticatedStatus({ status: SocketStatus.CLOSED });
|
||||
}
|
||||
|
||||
#dropUnauthenticated(process: AbortableProcess<IWebSocketResource>): void {
|
||||
@@ -833,6 +903,7 @@ export class SocketManager extends EventListener {
|
||||
}
|
||||
|
||||
this.#unauthenticated = undefined;
|
||||
this.#setUnauthenticatedStatus({ status: SocketStatus.CLOSED });
|
||||
if (!this.#unauthenticatedExpirationTimer) {
|
||||
return;
|
||||
}
|
||||
|
@@ -30,7 +30,6 @@ import { createHTTPSAgent } from '../util/createHTTPSAgent';
|
||||
import { createProxyAgent } from '../util/createProxyAgent';
|
||||
import type { ProxyAgent } from '../util/createProxyAgent';
|
||||
import type { FetchFunctionType } from '../util/uploads/tusProtocol';
|
||||
import type { SocketStatus } from '../types/SocketStatus';
|
||||
import { VerificationTransport } from '../types/VerificationTransport';
|
||||
import { toLogFormat } from '../types/errors';
|
||||
import { isPackIdValid, redactPackId } from '../types/Stickers';
|
||||
@@ -52,7 +51,7 @@ import { randomInt } from '../Crypto';
|
||||
import * as linkPreviewFetch from '../linkPreviews/linkPreviewFetch';
|
||||
import { isBadgeImageFileUrlValid } from '../badges/isBadgeImageFileUrlValid';
|
||||
|
||||
import { SocketManager } from './SocketManager';
|
||||
import { SocketManager, type SocketStatuses } from './SocketManager';
|
||||
import type { CDSAuthType, CDSResponseType } from './cds/Types.d';
|
||||
import { CDSI } from './cds/CDSI';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
@@ -1578,7 +1577,7 @@ export type WebAPIType = {
|
||||
getConfig: () => Promise<RemoteConfigResponseType>;
|
||||
authenticate: (credentials: WebAPICredentials) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
getSocketStatus: () => SocketStatus;
|
||||
getSocketStatus: () => SocketStatuses;
|
||||
registerRequestHandler: (handler: IRequestHandler) => void;
|
||||
unregisterRequestHandler: (handler: IRequestHandler) => void;
|
||||
onHasStoriesDisabledChange: (newValue: boolean) => void;
|
||||
@@ -2106,7 +2105,7 @@ export function initialize({
|
||||
}
|
||||
}
|
||||
|
||||
function getSocketStatus(): SocketStatus {
|
||||
function getSocketStatus(): SocketStatuses {
|
||||
return socketManager.getStatus();
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { TransportOption } from '../textsecure/WebsocketResources';
|
||||
|
||||
// Maps to values found here: https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
|
||||
// which are returned by libtextsecure's MessageReceiver
|
||||
export enum SocketStatus {
|
||||
@@ -9,3 +11,11 @@ export enum SocketStatus {
|
||||
CLOSING = 'CLOSING',
|
||||
CLOSED = 'CLOSED',
|
||||
}
|
||||
|
||||
export type SocketInfo = {
|
||||
status: SocketStatus;
|
||||
lastConnectionTimestamp?: number;
|
||||
lastConnectionTransport?:
|
||||
| TransportOption.Libsignal
|
||||
| TransportOption.Original;
|
||||
};
|
||||
|
8
ts/window.d.ts
vendored
8
ts/window.d.ts
vendored
@@ -51,6 +51,7 @@ import type { RetryPlaceholders } from './util/retryPlaceholders';
|
||||
import type { PropsPreloadType as PreferencesPropsType } from './components/Preferences';
|
||||
import type { WindowsNotificationData } from './services/notifications';
|
||||
import type { QueryStatsOptions } from './sql/main';
|
||||
import type { SocketStatuses } from './textsecure/SocketManager';
|
||||
|
||||
export { Long } from 'long';
|
||||
|
||||
@@ -146,7 +147,10 @@ export type SignalCoreType = {
|
||||
calling: CallingClass;
|
||||
backups: BackupsService;
|
||||
initializeGroupCredentialFetcher: () => Promise<void>;
|
||||
initializeNetworkObserver: (network: ReduxActions['network']) => void;
|
||||
initializeNetworkObserver: (
|
||||
network: ReduxActions['network'],
|
||||
getAuthSocketStatus: () => SocketStatus
|
||||
) => void;
|
||||
initializeUpdateListener: (updates: ReduxActions['updates']) => void;
|
||||
retryPlaceholders?: RetryPlaceholders;
|
||||
lightSessionResetQueue?: PQueue;
|
||||
@@ -201,7 +205,7 @@ declare global {
|
||||
getBackupServerPublicParams: () => string;
|
||||
getSfuUrl: () => string;
|
||||
getIceServerOverride: () => string;
|
||||
getSocketStatus: () => SocketStatus;
|
||||
getSocketStatus: () => SocketStatuses;
|
||||
getTitle: () => string;
|
||||
waitForEmptyEventQueue: () => Promise<void>;
|
||||
getVersion: () => string;
|
||||
|
@@ -78,6 +78,7 @@ if (
|
||||
getSfuUrl: () => window.Signal.Services.calling._sfuUrl,
|
||||
getIceServerOverride: () =>
|
||||
window.Signal.Services.calling._iceServerOverride,
|
||||
getSocketStatus: () => window.textsecure.server?.getSocketStatus(),
|
||||
getStorageItem: (name: keyof StorageAccessType) => window.storage.get(name),
|
||||
putStorageItem: <K extends keyof StorageAccessType>(
|
||||
name: K,
|
||||
|
Reference in New Issue
Block a user