Upgrade libsignal-client to 0.44.0 and adopt API changes

This commit is contained in:
Sergey Skrobotov 2024-04-04 14:39:52 -07:00 committed by GitHub
parent 37725647c8
commit e388f13910
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 266 additions and 138 deletions

View file

@ -4343,7 +4343,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see
```
## attest 0.1.0, device-transfer 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-core 0.1.0, libsignal-ffi 0.42.0, libsignal-jni 0.42.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, libsignal-node 0.42.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, poksho 0.7.0, signal-crypto 0.1.0, signal-media 0.1.0, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, signal-pin 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0
## attest 0.1.0, device-transfer 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-core 0.1.0, libsignal-ffi 0.44.0, libsignal-jni 0.44.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, libsignal-node 0.44.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, poksho 0.7.0, signal-crypto 0.1.0, signal-media 0.1.0, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, signal-pin 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0
```
GNU AFFERO GENERAL PUBLIC LICENSE

View file

@ -2426,6 +2426,9 @@ ipc.on('get-config', async event => {
preferredSystemLocales: getPreferredSystemLocales(),
localeOverride: getLocaleOverride(),
version: app.getVersion(),
libsignalNetEnvironment: config.has('libsignalNetEnvironment')
? config.get<string>('libsignalNetEnvironment')
: undefined,
buildCreation: config.get<number>('buildCreation'),
buildExpiration: config.get<number>('buildExpiration'),
challengeUrl: config.get<string>('challengeUrl'),

View file

@ -104,7 +104,7 @@
"@react-aria/utils": "3.16.0",
"@react-spring/web": "9.5.5",
"@signalapp/better-sqlite3": "8.7.1",
"@signalapp/libsignal-client": "0.42.0",
"@signalapp/libsignal-client": "0.44.0",
"@signalapp/ringrtc": "2.39.3",
"@signalapp/windows-dummy-keystroke": "1.0.0",
"@types/fabric": "4.5.3",

View file

@ -1,6 +1,7 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Net } from '@signalapp/libsignal-client';
import URL from 'url';
import type { RequestInit, Response } from 'node-fetch';
import { Headers } from 'node-fetch';
@ -103,7 +104,10 @@ export class SocketManager extends EventListener {
private reconnectController: AbortController | undefined;
constructor(private readonly options: SocketManagerOptions) {
constructor(
private readonly libsignalNet: Net.Net,
private readonly options: SocketManagerOptions
) {
super();
this.hasStoriesDisabled = options.hasStoriesDisabled;
@ -565,14 +569,9 @@ export class SocketManager extends EventListener {
}
private connectLibsignalUnauthenticated(): AbortableProcess<IWebSocketResource> {
return new AbortableProcess<IWebSocketResource>(
`WebSocket.connect(libsignal.${UNAUTHENTICATED_CHANNEL_NAME})`,
{
abort() {
// noop
},
},
Promise.resolve(new LibsignalWebSocketResource(this.options.version))
return LibsignalWebSocketResource.connect(
this.libsignalNet,
UNAUTHENTICATED_CHANNEL_NAME
);
}
@ -666,7 +665,8 @@ export class SocketManager extends EventListener {
const url = `${this.options.url}${path}?${qs.encode(queryWithDefaults)}`;
const { version } = this.options;
return connectWebSocket({
const start = performance.now();
const webSocketResourceConnection = connectWebSocket({
name,
url,
version,
@ -675,17 +675,69 @@ export class SocketManager extends EventListener {
extraHeaders,
createResource(socket: WebSocket): IWebSocketResource {
return !resourceOptions.transportOption ||
resourceOptions.transportOption === TransportOption.Original
? new WebSocketResource(socket, resourceOptions)
: new WebSocketResourceWithShadowing(
socket,
resourceOptions,
version
createResource(socket: WebSocket): WebSocketResource {
const duration = (performance.now() - start).toFixed(1);
log.info(
`WebSocketResource(${resourceOptions.name}) connected in ${duration}ms`
);
return new WebSocketResource(socket, resourceOptions);
},
});
const shadowingModeEnabled =
!resourceOptions.transportOption ||
resourceOptions.transportOption === TransportOption.Original;
return shadowingModeEnabled
? webSocketResourceConnection
: this.connectWithShadowing(webSocketResourceConnection, resourceOptions);
}
/**
* A method that takes in an `AbortableProcess<>` that establishes
* a `WebSocketResource` connection and wraps it in a process
* that also tries to establish a `LibsignalWebSocketResource` connection.
*
* The shadowing connection will not block the main one (e.g. if it takes
* longer to connect) and an error in the shadowing connection will not
* affect the overall behavior.
*
* @param mainConnection an `AbortableProcess<WebSocketResource>` responsible
* for establishing a Desktop system WebSocket connection.
* @param options `WebSocketResourceOptions` options
* @private
*/
private connectWithShadowing(
mainConnection: AbortableProcess<WebSocketResource>,
options: WebSocketResourceOptions
): AbortableProcess<IWebSocketResource> {
// creating an `AbortableProcess` of libsignal websocket connection
const shadowingConnection = LibsignalWebSocketResource.connect(
this.libsignalNet,
options.name
);
const shadowWrapper = async () => {
// if main connection results in an error,
// it's propagated as the error of the resulting process
const mainSocket = await mainConnection.resultPromise;
// here, we're not awaiting on `shadowingConnection.resultPromise`
// and just letting `WebSocketResourceWithShadowing`
// initiate and handle the result of the shadowing connection attempt
return new WebSocketResourceWithShadowing(
mainSocket,
shadowingConnection,
options
);
};
return new AbortableProcess<IWebSocketResource>(
`WebSocketResourceWithShadowing.connect(${options.name})`,
{
abort() {
mainConnection.abort();
shadowingConnection.abort();
},
},
shadowWrapper()
);
}
private async checkResource(

View file

@ -16,6 +16,7 @@ import { z } from 'zod';
import type { Readable } from 'stream';
import type { connection as WebSocket } from 'websocket';
import { Net } from '@signalapp/libsignal-client';
import { assertDev, strictAssert } from '../util/assert';
import { isRecord } from '../util/isRecord';
import * as durations from '../util/durations';
@ -71,6 +72,7 @@ import * as log from '../logging/log';
import { maybeParseUrl, urlPathFromComponents } from '../util/url';
import { SECOND } from '../util/durations';
import type { IWebSocketResource } from './WebsocketResources';
import { Environment, getEnvironment } from '../environment';
// 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
@ -78,6 +80,32 @@ import type { IWebSocketResource } from './WebsocketResources';
const DEBUG = false;
const DEFAULT_TIMEOUT = 30 * SECOND;
// Libsignal has internally configured values for domain names
// (and other connectivity params) of the services.
function resolveLibsignalNetEnvironment(
appEnv: Environment,
libsignalNetEnv: string | undefined
): Net.Environment {
switch (appEnv) {
case Environment.Production:
return Net.Environment.Production;
case Environment.Development:
// In the case of the `Development` Desktop env,
// we should be checking the provided string value
// of `libsignalNetEnv`
switch (libsignalNetEnv) {
case 'production':
return Net.Environment.Production;
default:
return Net.Environment.Staging;
}
case Environment.Test:
case Environment.Staging:
default:
return Net.Environment.Staging;
}
}
function _createRedactor(
...toReplace: ReadonlyArray<string | undefined>
): RedactUrl {
@ -588,6 +616,7 @@ type InitializeOptionsType = {
proxyUrl: string | undefined;
version: string;
directoryConfig: DirectoryConfigType;
libsignalNetEnvironment: string | undefined;
};
export type MessageType = Readonly<{
@ -1249,6 +1278,7 @@ export function initialize({
contentProxyUrl,
proxyUrl,
version,
libsignalNetEnvironment,
}: InitializeOptionsType): WebAPIConnectType {
if (!isString(url)) {
throw new Error('WebAPI.initialize: Invalid server url');
@ -1290,6 +1320,17 @@ export function initialize({
throw new Error('WebAPI.initialize: Invalid version');
}
// `libsignalNet` is an instance of a class from libsignal that is responsible
// for providing network layer API and related functionality.
// It's important to have a single instance of this class as it holds
// resources that are shared across all other use cases.
const env = resolveLibsignalNetEnvironment(
getEnvironment(),
libsignalNetEnvironment
);
log.info(`libsignal net environment resolved to [${Net.Environment[env]}]`);
const libsignalNet = new Net.Net(env);
// Thanks to function-hoisting, we can put this return statement before all of the
// below function definitions.
return {
@ -1313,7 +1354,7 @@ export function initialize({
let activeRegistration: ExplodePromiseResultType<void> | undefined;
const socketManager = new SocketManager({
const socketManager = new SocketManager(libsignalNet, {
url,
artCreatorUrl,
certificateAuthority,
@ -1344,7 +1385,7 @@ export function initialize({
const { directoryUrl, directoryMRENCLAVE } = directoryConfig;
const cds = new CDSI({
const cds = new CDSI(libsignalNet, {
logger: log,
proxyUrl,

View file

@ -17,6 +17,7 @@ import { ConnectTimeoutError, HTTPError } from './Errors';
import { handleStatusCode, translateError } from './Utils';
const TEN_SECONDS = 10 * durations.SECOND;
const WEBSOCKET_CONNECT_TIMEOUT = TEN_SECONDS;
const KEEPALIVE_INTERVAL_MS = TEN_SECONDS;
export type IResource = {
@ -42,7 +43,7 @@ export function connect<Resource extends IResource>({
version,
proxyAgent,
extraHeaders = {},
timeout = TEN_SECONDS,
timeout = WEBSOCKET_CONNECT_TIMEOUT,
createResource,
}: ConnectOptionsType<Resource>): AbortableProcess<Resource> {
const fixedScheme = url

View file

@ -26,7 +26,6 @@
/* eslint-disable @typescript-eslint/no-namespace */
/* eslint-disable @typescript-eslint/brace-style */
import { Net } from '@signalapp/libsignal-client';
import type { connection as WebSocket, IMessage } from 'websocket';
import Long from 'long';
import pTimeout from 'p-timeout';
@ -35,8 +34,9 @@ import net from 'net';
import { z } from 'zod';
import { clearInterval } from 'timers';
import { random } from 'lodash';
import type { DebugInfo } from '@signalapp/libsignal-client/Native';
import type { ChatServiceDebugInfo } from '@signalapp/libsignal-client/Native';
import type { Net } from '@signalapp/libsignal-client';
import type { EventHandler } from './EventTarget';
import EventTarget from './EventTarget';
@ -50,14 +50,13 @@ import { SignalService as Proto } from '../protobuf';
import * as log from '../logging/log';
import * as Timers from '../Timers';
import type { IResource } from './WebSocket';
import { isProduction, isStaging } from '../util/version';
import { isProduction } from '../util/version';
import { ToastType } from '../types/Toast';
import { AbortableProcess } from '../util/AbortableProcess';
const THIRTY_SECONDS = 30 * durations.SECOND;
const HEALTHCHECK_TIMEOUT = durations.SECOND;
const STATS_UPDATE_INTERVAL = durations.MINUTE;
const MAX_MESSAGE_SIZE = 512 * 1024;
@ -83,6 +82,7 @@ export namespace IpVersion {
}
const AggregatedStatsSchema = z.object({
connectionFailures: z.number(),
requestsCompared: z.number(),
ipVersionMismatches: z.number(),
unexpectedReconnects: z.number(),
@ -127,6 +127,7 @@ export namespace AggregatedStats {
export function add(a: AggregatedStats, b: AggregatedStats): AggregatedStats {
return {
requestsCompared: a.requestsCompared + b.requestsCompared,
connectionFailures: a.connectionFailures + b.connectionFailures,
healthcheckFailures: a.healthcheckFailures + b.healthcheckFailures,
ipVersionMismatches: a.ipVersionMismatches + b.ipVersionMismatches,
unexpectedReconnects: a.unexpectedReconnects + b.unexpectedReconnects,
@ -138,6 +139,7 @@ export namespace AggregatedStats {
export function createEmpty(): AggregatedStats {
return {
requestsCompared: 0,
connectionFailures: 0,
ipVersionMismatches: 0,
unexpectedReconnects: 0,
healthcheckFailures: 0,
@ -152,9 +154,10 @@ export namespace AggregatedStats {
return false;
}
return (
stats.healthcheckBadStatus + stats.healthcheckFailures > 20 ||
stats.ipVersionMismatches > 50 ||
stats.unexpectedReconnects > 50
stats.healthcheckBadStatus +
stats.healthcheckFailures +
stats.connectionFailures >
20 || stats.unexpectedReconnects > 50
);
}
@ -261,11 +264,42 @@ export interface IWebSocketResource extends IResource {
}
export class LibsignalWebSocketResource implements IWebSocketResource {
private readonly net: Net.Net;
constructor(
private readonly chatService: Net.ChatService,
private readonly socketIpVersion: IpVersion | undefined
) {}
constructor(version: string) {
this.net = new Net.Net(
isStaging(version) ? Net.Environment.Staging : Net.Environment.Production
public static connect(
libsignalNet: Net.Net,
name: string
): AbortableProcess<LibsignalWebSocketResource> {
const chatService = libsignalNet.newChatService();
const connectAsync = async () => {
try {
const debugInfo = await chatService.connectUnauthenticated();
log.info(`LibsignalWebSocketResource(${name}) connected`, debugInfo);
return new LibsignalWebSocketResource(
chatService,
IpVersion.fromDebugInfoCode(debugInfo.ipType)
);
} catch (error) {
// Handle any errors that occur during connection
log.error(
`LibsignalWebSocketResource(${name}) connection failed`,
Errors.toLogFormat(error)
);
throw error;
}
};
return new AbortableProcess<LibsignalWebSocketResource>(
`LibsignalWebSocketResource.connect(${name})`,
{
abort() {
// if interrupted, trying to disconnect
drop(chatService.disconnect());
},
},
connectAsync()
);
}
@ -273,6 +307,10 @@ export class LibsignalWebSocketResource implements IWebSocketResource {
return undefined;
}
public ipVersion(): IpVersion | undefined {
return this.socketIpVersion;
}
public addEventListener(
_name: 'close',
_handler: (ev: CloseEvent) => void
@ -281,13 +319,11 @@ export class LibsignalWebSocketResource implements IWebSocketResource {
}
public close(_code?: number, _reason?: string): void {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.net.disconnectChatService();
drop(this.chatService.disconnect());
}
public shutdown(): void {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.net.disconnectChatService();
drop(this.chatService.disconnect());
}
public forceKeepAlive(): void {
@ -301,16 +337,15 @@ export class LibsignalWebSocketResource implements IWebSocketResource {
public async sendRequestGetDebugInfo(
options: SendRequestOptions
): Promise<[Response, DebugInfo]> {
const { response, debugInfo } = await this.net.unauthenticatedFetchAndDebug(
{
): Promise<[Response, ChatServiceDebugInfo]> {
const { response, debugInfo } =
await this.chatService.unauthenticatedFetchAndDebug({
verb: options.verb,
path: options.path,
headers: options.headers ? options.headers : [],
body: options.body,
timeoutMillis: options.timeout,
}
);
});
return [
new Response(response.body, {
status: response.status,
@ -323,9 +358,7 @@ export class LibsignalWebSocketResource implements IWebSocketResource {
}
export class WebSocketResourceWithShadowing implements IWebSocketResource {
private main: WebSocketResource;
private shadowing: LibsignalWebSocketResource;
private shadowing: LibsignalWebSocketResource | undefined;
private stats: AggregatedStats;
@ -336,12 +369,10 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
private logId: string;
constructor(
socket: WebSocket,
options: WebSocketResourceOptions,
version: string
private readonly main: WebSocketResource,
private readonly shadowingConnection: AbortableProcess<LibsignalWebSocketResource>,
options: WebSocketResourceOptions
) {
this.main = new WebSocketResource(socket, options);
this.shadowing = new LibsignalWebSocketResource(version);
this.stats = AggregatedStats.createEmpty();
this.logId = `WebSocketResourceWithShadowing(${options.name})`;
this.statsTimer = setInterval(
@ -351,6 +382,28 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
this.shadowingWithReporting =
options.transportOption === TransportOption.ShadowingHigh;
// the idea is that we want to keep the shadowing connection process
// "in the background", so that the main connection wouldn't need to wait on it.
// then when we're connected, `this.shadowing` socket resource is initialized
// or an error reported in case of connection failure
const initializeAfterConnected = async () => {
try {
this.shadowing = await shadowingConnection.resultPromise;
// checking IP one time per connection
if (this.main.ipVersion() !== this.shadowing.ipVersion()) {
this.stats.ipVersionMismatches += 1;
const mainIpType = this.main.ipVersion();
const shadowIpType = this.shadowing.ipVersion();
log.warn(
`${this.logId}: libsignal websocket IP [${shadowIpType}], Desktop websocket IP [${mainIpType}]`
);
}
} catch (error) {
this.stats.connectionFailures += 1;
}
};
drop(initializeAfterConnected());
this.addEventListener('close', (_ev): void => {
clearInterval(this.statsTimer);
this.updateStats(options.name);
@ -392,12 +445,20 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
public close(): void {
this.main.close();
if (this.shadowing) {
this.shadowing.close();
} else {
this.shadowingConnection.abort();
}
}
public shutdown(): void {
this.main.shutdown();
if (this.shadowing) {
this.shadowing.shutdown();
} else {
this.shadowingConnection.abort();
}
}
public forceKeepAlive(timeout?: number): void {
@ -421,12 +482,20 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
}
private async sendShadowRequest(): Promise<void> {
// it could be that we're still connecting libsignal websocket
// in which case we're skipping the check
if (!this.shadowing) {
log.info(
`${this.logId}: skipping healthcheck - websocket not connected yet`
);
return;
}
try {
const [healthCheckResult, debugInfo] =
await this.shadowing.sendRequestGetDebugInfo({
verb: 'GET',
path: '/v1/keepalive',
timeout: HEALTHCHECK_TIMEOUT,
timeout: KEEPALIVE_TIMEOUT_MS,
});
this.stats.requestsCompared += 1;
if (!isSuccessfulStatusCode(healthCheckResult.status)) {
@ -435,18 +504,7 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
`${this.logId}: keepalive via libsignal responded with status [${healthCheckResult.status}]`
);
}
const ipVersion = IpVersion.fromDebugInfoCode(debugInfo.ipType);
if (this.main.ipVersion() !== ipVersion) {
this.stats.ipVersionMismatches += 1;
log.warn(
`${
this.logId
}: keepalive via libsignal using IP [${ipVersion}] while main is using IP [${this.main.ipVersion()}]`
);
}
if (debugInfo.reconnectCount > 1) {
this.stats.unexpectedReconnects = debugInfo.reconnectCount - 1;
}
this.stats.unexpectedReconnects = debugInfo.reconnectCount;
} catch (error) {
this.stats.healthcheckFailures += 1;
log.warn(
@ -823,7 +881,7 @@ const KEEPALIVE_INTERVAL_MS = 30 * durations.SECOND;
// immediate disconnect.
const STALE_THRESHOLD_MS = 5 * durations.MINUTE;
// If we don't receive a response to keepalive request within 10 seconds -
// If we don't receive a response to keepalive request within 30 seconds -
// close the socket.
const KEEPALIVE_TIMEOUT_MS = 30 * durations.SECOND;

View file

@ -1,6 +1,7 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Net } from '@signalapp/libsignal-client';
import type { connection as WebSocket } from 'websocket';
import * as Bytes from '../../Bytes';
@ -16,8 +17,8 @@ export type CDSIOptionsType = Readonly<{
export class CDSI extends CDSSocketManagerBase<CDSISocket, CDSIOptionsType> {
private readonly mrenclave: Buffer;
constructor(options: CDSIOptionsType) {
super(options);
constructor(libsignalNet: Net.Net, options: CDSIOptionsType) {
super(libsignalNet, options);
this.mrenclave = Buffer.from(Bytes.fromHex(options.mrenclave));
}

View file

@ -1,9 +1,11 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { RateLimitedError as NetRateLimitedError } from '@signalapp/libsignal-client';
import {
import type {
RateLimitedError as NetRateLimitedError,
Net,
} from '@signalapp/libsignal-client';
import {
ErrorCode as LibSignalErrorCode,
LibSignalErrorBase,
} from '@signalapp/libsignal-client';
@ -25,7 +27,6 @@ import type {
} from './Types.d';
import { RateLimitedError } from './RateLimitedError';
import { connect as connectWebSocket } from '../WebSocket';
import { Environment, getEnvironment } from '../../environment';
const REQUEST_TIMEOUT = 10 * SECOND;
@ -42,6 +43,10 @@ export abstract class CDSSocketManagerBase<
> extends CDSBase<Options> {
private retryAfter?: number;
constructor(private readonly libsignalNet: Net.Net, options: Options) {
super(options);
}
public async request(
options: CDSRequestOptionsType
): Promise<CDSResponseType> {
@ -106,24 +111,22 @@ export abstract class CDSSocketManagerBase<
options: CDSRequestOptionsType
): Promise<CDSResponseType> {
const log = this.logger;
const {
acisAndAccessKeys,
e164s,
timeout = REQUEST_TIMEOUT,
returnAcisWithoutUaks = false,
} = options;
const { acisAndAccessKeys, e164s, returnAcisWithoutUaks = false } = options;
const auth = await this.getAuth();
log.info('CDSSocketManager: making request via libsignal');
const net = new Net.Net(this.libsignalNetEnvironment());
try {
log.info('CDSSocketManager: starting lookup request');
const response = await net.cdsiLookup(auth, {
const { timeout = REQUEST_TIMEOUT } = options;
const response = await pTimeout(
this.libsignalNet.cdsiLookup(auth, {
acisAndAccessKeys,
e164s,
timeout,
returnAcisWithoutUaks,
});
}),
timeout
);
log.info('CDSSocketManager: lookup request finished');
return response as CDSResponseType;
@ -142,19 +145,6 @@ export abstract class CDSSocketManagerBase<
}
}
private libsignalNetEnvironment(): Net.Environment {
const env = getEnvironment();
switch (env) {
case Environment.Production:
return Net.Environment.Production;
case Environment.Development:
case Environment.Test:
case Environment.Staging:
default:
return Net.Environment.Staging;
}
}
private connect(auth: CDSAuthType): AbortableProcess<Socket> {
return connectWebSocket<Socket>({
name: 'CDSSocket',

View file

@ -71,6 +71,7 @@ export const rendererConfigSchema = z.object({
resourcesUrl: configRequiredStringSchema,
userDataPath: configRequiredStringSchema,
version: configRequiredStringSchema,
libsignalNetEnvironment: configOptionalStringSchema,
directoryConfig: directoryConfigSchema,
// Only used by main window

View file

@ -175,6 +175,7 @@ if (config.ciMode !== 'full' && config.environment !== 'test') {
type NetworkStatistics = {
signalConnectionCount?: string;
unauthorizedConnectionFailures?: string;
unauthorizedRequestsCompared?: string;
unauthorizedHealthcheckFailures?: string;
unauthorizedHealthcheckBadStatus?: string;
@ -206,6 +207,9 @@ ipc.on('additional-log-data-request', async event => {
if (unauthorizedStats.requestsCompared > 0) {
networkStatistics = {
...networkStatistics,
unauthorizedConnectionFailures: formatCountForLogging(
unauthorizedStats.connectionFailures
),
unauthorizedRequestsCompared: formatCountForLogging(
unauthorizedStats.requestsCompared
),

View file

@ -38,6 +38,7 @@ window.WebAPI = window.textsecure.WebAPI.initialize({
contentProxyUrl: config.contentProxyUrl,
proxyUrl: config.proxyUrl,
version: config.version,
libsignalNetEnvironment: config.libsignalNetEnvironment,
});
window.libphonenumberInstance = PhoneNumberUtil.getInstance();

View file

@ -3983,7 +3983,16 @@
bindings "^1.5.0"
tar "^6.1.0"
"@signalapp/libsignal-client@0.42.0", "@signalapp/libsignal-client@^0.42.0":
"@signalapp/libsignal-client@0.44.0":
version "0.44.0"
resolved "https://registry.yarnpkg.com/@signalapp/libsignal-client/-/libsignal-client-0.44.0.tgz#c08bf33bb16276baff35a932b1f43d19d17028cf"
integrity sha512-tbxkRoNd1l2cg6aN5tyiTCy724qU/OlKpX9IECbMu/W1V77SaQS6ch0kPdR+KClBMyEtWxnd/q3/V4NMiENF4w==
dependencies:
node-gyp-build "^4.2.3"
type-fest "^3.5.0"
uuid "^8.3.0"
"@signalapp/libsignal-client@^0.42.0":
version "0.42.0"
resolved "https://registry.yarnpkg.com/@signalapp/libsignal-client/-/libsignal-client-0.42.0.tgz#259d87233f1e065ae93cf8fe758bcc2461e3e814"
integrity sha512-03lr1LmMTSy3lto8lbdaQMvuvwqs7+fatNP3Kp6dHAnR/OoXh6Y1l493U5X86Z87XGdM0gfGntxZwZ+Qju9Dpg==
@ -7568,7 +7577,7 @@ caniuse-lite@^1.0.30001349, caniuse-lite@^1.0.30001541:
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz"
integrity sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw==
canvas@^2.6.1, "canvas@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz":
canvas@^2.6.1, "canvas@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz", dmg-license@^1.0.11, "dmg-license@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz", jsdom@^15.2.1, "jsdom@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz":
version "1.0.0"
resolved "https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz#cb46cf7e01574aa6390858149f66897afe53c9ca"
@ -8946,10 +8955,6 @@ dmg-builder@24.6.3:
optionalDependencies:
dmg-license "^1.0.11"
dmg-license@^1.0.11, "dmg-license@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz":
version "1.0.0"
resolved "https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz#cb46cf7e01574aa6390858149f66897afe53c9ca"
dns-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
@ -13378,10 +13383,6 @@ jsdoc@^4.0.0:
strip-json-comments "^3.1.0"
underscore "~1.13.2"
jsdom@^15.2.1, "jsdom@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz":
version "1.0.0"
resolved "https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz#cb46cf7e01574aa6390858149f66897afe53c9ca"
jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@ -18452,7 +18453,7 @@ string-length@^5.0.1:
char-regex "^2.0.0"
strip-ansi "^7.0.1"
"string-width-cjs@npm:string-width@^4.2.0":
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -18495,15 +18496,6 @@ string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@ -18584,7 +18576,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@ -18618,13 +18610,6 @@ strip-ansi@^6.0.0:
dependencies:
ansi-regex "^5.0.0"
strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@ -20278,7 +20263,7 @@ workerpool@6.2.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@ -20312,15 +20297,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"