WebSocket API for CDS
This commit is contained in:
parent
4cb1ea9e5d
commit
409bf1fc82
12 changed files with 436 additions and 109 deletions
127
ts/textsecure/WebSocket.ts
Normal file
127
ts/textsecure/WebSocket.ts
Normal file
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type ProxyAgent from 'proxy-agent';
|
||||
import { client as WebSocketClient } from 'websocket';
|
||||
import type { connection as WebSocket } from 'websocket';
|
||||
|
||||
import { AbortableProcess } from '../util/AbortableProcess';
|
||||
import { strictAssert } from '../util/assert';
|
||||
import { explodePromise } from '../util/explodePromise';
|
||||
import { getUserAgent } from '../util/getUserAgent';
|
||||
import * as durations from '../util/durations';
|
||||
import * as log from '../logging/log';
|
||||
import * as Timers from '../Timers';
|
||||
import { ConnectTimeoutError, HTTPError } from './Errors';
|
||||
import { handleStatusCode, translateError } from './Utils';
|
||||
|
||||
const TEN_SECONDS = 10 * durations.SECOND;
|
||||
|
||||
export type IResource = {
|
||||
close(code: number, reason: string): void;
|
||||
};
|
||||
|
||||
export type ConnectOptionsType<Resource extends IResource> = Readonly<{
|
||||
url: string;
|
||||
certificateAuthority: string;
|
||||
version: string;
|
||||
proxyAgent?: ReturnType<typeof ProxyAgent>;
|
||||
timeout?: number;
|
||||
|
||||
createResource(socket: WebSocket): Resource;
|
||||
}>;
|
||||
|
||||
export function connect<Resource extends IResource>({
|
||||
url,
|
||||
certificateAuthority,
|
||||
version,
|
||||
proxyAgent,
|
||||
timeout = TEN_SECONDS,
|
||||
createResource,
|
||||
}: ConnectOptionsType<Resource>): AbortableProcess<Resource> {
|
||||
const fixedScheme = url
|
||||
.replace('https://', 'wss://')
|
||||
.replace('http://', 'ws://');
|
||||
|
||||
const headers = {
|
||||
'User-Agent': getUserAgent(version),
|
||||
};
|
||||
const client = new WebSocketClient({
|
||||
tlsOptions: {
|
||||
ca: certificateAuthority,
|
||||
agent: proxyAgent,
|
||||
},
|
||||
maxReceivedFrameSize: 0x210000,
|
||||
});
|
||||
|
||||
client.connect(fixedScheme, undefined, undefined, headers);
|
||||
|
||||
const { stack } = new Error();
|
||||
|
||||
const { promise, resolve, reject } = explodePromise<Resource>();
|
||||
|
||||
const timer = Timers.setTimeout(() => {
|
||||
reject(new ConnectTimeoutError('Connection timed out'));
|
||||
|
||||
client.abort();
|
||||
}, timeout);
|
||||
|
||||
let resource: Resource | undefined;
|
||||
client.on('connect', socket => {
|
||||
Timers.clearTimeout(timer);
|
||||
|
||||
resource = createResource(socket);
|
||||
resolve(resource);
|
||||
});
|
||||
|
||||
client.on('httpResponse', async response => {
|
||||
Timers.clearTimeout(timer);
|
||||
|
||||
const statusCode = response.statusCode || -1;
|
||||
await handleStatusCode(statusCode);
|
||||
|
||||
const error = new HTTPError('connectResource: invalid websocket response', {
|
||||
code: statusCode || -1,
|
||||
headers: {},
|
||||
stack,
|
||||
});
|
||||
|
||||
const translatedError = translateError(error);
|
||||
strictAssert(
|
||||
translatedError,
|
||||
'`httpResponse` event cannot be emitted with 200 status code'
|
||||
);
|
||||
|
||||
reject(translatedError);
|
||||
});
|
||||
|
||||
client.on('connectFailed', e => {
|
||||
Timers.clearTimeout(timer);
|
||||
|
||||
reject(
|
||||
new HTTPError('connectResource: connectFailed', {
|
||||
code: -1,
|
||||
headers: {},
|
||||
response: e.toString(),
|
||||
stack,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return new AbortableProcess<Resource>(
|
||||
`WebSocket.connect(${url})`,
|
||||
{
|
||||
abort() {
|
||||
if (resource) {
|
||||
log.warn(`WebSocket: closing socket ${url}`);
|
||||
resource.close(3000, 'aborted');
|
||||
} else {
|
||||
log.warn(`WebSocket: aborting connection ${url}`);
|
||||
Timers.clearTimeout(timer);
|
||||
client.abort();
|
||||
}
|
||||
},
|
||||
},
|
||||
promise
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue