From 50a934a428fcbf61ad1df275b10cb09d0e1a024d Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Thu, 24 Aug 2023 09:26:28 -0300 Subject: [PATCH 01/13] Properly type realtime instance in ConnectionManager --- src/common/lib/transport/connectionmanager.ts | 9 +++++---- src/common/lib/types/protocolmessage.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index 992fe239b6..0c9c3f15b0 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -17,8 +17,8 @@ import Transport, { TransportCtor } from './transport'; import * as API from '../../../../ably'; import { ErrCallback } from 'common/types/utils'; import HttpStatusCodes from 'common/constants/HttpStatusCodes'; +import BaseRealtime from '../client/baserealtime'; -type Realtime = any; type ClientOptions = any; let globalObject = typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : self; @@ -190,7 +190,7 @@ type ConnectionState = { }; class ConnectionManager extends EventEmitter { - realtime: Realtime; + realtime: BaseRealtime; options: ClientOptions; states: Record; state: ConnectionState; @@ -226,7 +226,7 @@ class ConnectionManager extends EventEmitter { queue: { message: ProtocolMessage; transport: Transport }[]; } = { isProcessing: false, queue: [] }; - constructor(realtime: Realtime, options: ClientOptions) { + constructor(realtime: BaseRealtime, options: ClientOptions) { super(); ConnectionManager.initTransports(); this.realtime = realtime; @@ -1191,7 +1191,8 @@ class ConnectionManager extends EventEmitter { const newState = (this.state = this.states[stateChange.current as string]); if (stateChange.reason) { this.errorReason = stateChange.reason; - this.realtime.connection.errorReason = stateChange.reason; + // TODO remove this type assertion after fixing https://github.com/ably/ably-js/issues/1405 + this.realtime.connection.errorReason = stateChange.reason as ErrorInfo; } if (newState.terminal || newState.state === 'suspended') { /* suspended is nonterminal, but once in the suspended state, realtime diff --git a/src/common/lib/types/protocolmessage.ts b/src/common/lib/types/protocolmessage.ts index fde1978f9a..b721fe9e10 100644 --- a/src/common/lib/types/protocolmessage.ts +++ b/src/common/lib/types/protocolmessage.ts @@ -110,7 +110,7 @@ class ProtocolMessage { static serialize = Utils.encodeBody; - static deserialize = function (serialized: unknown, MsgPack: MsgPack, format?: Utils.Format): ProtocolMessage { + static deserialize = function (serialized: unknown, MsgPack: MsgPack | null, format?: Utils.Format): ProtocolMessage { const deserialized = Utils.decodeBody>(serialized, MsgPack, format); return ProtocolMessage.fromDeserialized(deserialized); }; From b818c279588c2d0ddb7da5336b2fd47a0be89c1e Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Wed, 23 Aug 2023 14:07:16 -0300 Subject: [PATCH 02/13] Fix type of IPlatform.Transports --- src/common/platform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/platform.ts b/src/common/platform.ts index b55e625a26..b276c1cbd9 100644 --- a/src/common/platform.ts +++ b/src/common/platform.ts @@ -30,7 +30,7 @@ export default class Platform { */ static Crypto: IUntypedCryptoStatic | null; static Http: typeof IHttp; - static Transports: Array<(connectionManager: typeof ConnectionManager) => Transport>; + static Transports: Array<(connectionManager: typeof ConnectionManager) => typeof Transport>; static Defaults: IDefaults; static WebStorage: IWebStorage | null; } From ebbb35425a621e425732141a6663e13b35ed88b8 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Wed, 23 Aug 2023 14:38:02 -0300 Subject: [PATCH 03/13] Extract type of IPlatform.Transports Preparation for #1394 (making transports tree-shakable). --- src/common/lib/transport/connectionmanager.ts | 2 ++ src/common/platform.ts | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index 0c9c3f15b0..0a7fc38c72 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -2167,3 +2167,5 @@ class ConnectionManager extends EventEmitter { } export default ConnectionManager; + +export type TransportInitialiser = (connectionManager: typeof ConnectionManager) => typeof Transport; diff --git a/src/common/platform.ts b/src/common/platform.ts index b276c1cbd9..aa8e133019 100644 --- a/src/common/platform.ts +++ b/src/common/platform.ts @@ -1,10 +1,9 @@ import { IPlatformConfig } from './types/IPlatformConfig'; import { IHttp } from './types/http'; -import ConnectionManager from './lib/transport/connectionmanager'; +import { TransportInitialiser } from './lib/transport/connectionmanager'; import IDefaults from './types/IDefaults'; import IWebStorage from './types/IWebStorage'; import IBufferUtils from './types/IBufferUtils'; -import Transport from './lib/transport/transport'; import * as WebBufferUtils from '../platform/web/lib/util/bufferutils'; import * as NodeBufferUtils from '../platform/nodejs/lib/util/bufferutils'; import { IUntypedCryptoStatic } from '../common/types/ICryptoStatic'; @@ -30,7 +29,7 @@ export default class Platform { */ static Crypto: IUntypedCryptoStatic | null; static Http: typeof IHttp; - static Transports: Array<(connectionManager: typeof ConnectionManager) => typeof Transport>; + static Transports: TransportInitialiser[]; static Defaults: IDefaults; static WebStorage: IWebStorage | null; } From 5d0a71ccbc4d64ee36f6564a03b06f888823f9e7 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Wed, 23 Aug 2023 14:01:05 -0300 Subject: [PATCH 04/13] Convert platform/**/lib/transport/index.ts to TypeScript --- src/platform/nodejs/lib/transport/index.js | 3 --- src/platform/nodejs/lib/transport/index.ts | 3 +++ src/platform/nodejs/lib/transport/nodecomettransport.d.ts | 5 +++++ src/platform/web/lib/transport/index.js | 4 ---- src/platform/web/lib/transport/index.ts | 4 ++++ 5 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 src/platform/nodejs/lib/transport/index.js create mode 100644 src/platform/nodejs/lib/transport/index.ts create mode 100644 src/platform/nodejs/lib/transport/nodecomettransport.d.ts delete mode 100644 src/platform/web/lib/transport/index.js create mode 100644 src/platform/web/lib/transport/index.ts diff --git a/src/platform/nodejs/lib/transport/index.js b/src/platform/nodejs/lib/transport/index.js deleted file mode 100644 index 2d9be41828..0000000000 --- a/src/platform/nodejs/lib/transport/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import NodeCometTransport from './nodecomettransport'; - -export default [NodeCometTransport]; diff --git a/src/platform/nodejs/lib/transport/index.ts b/src/platform/nodejs/lib/transport/index.ts new file mode 100644 index 0000000000..1c0163c500 --- /dev/null +++ b/src/platform/nodejs/lib/transport/index.ts @@ -0,0 +1,3 @@ +import initialiseNodeCometTransport from './nodecomettransport'; + +export default [initialiseNodeCometTransport]; diff --git a/src/platform/nodejs/lib/transport/nodecomettransport.d.ts b/src/platform/nodejs/lib/transport/nodecomettransport.d.ts new file mode 100644 index 0000000000..9067155ab9 --- /dev/null +++ b/src/platform/nodejs/lib/transport/nodecomettransport.d.ts @@ -0,0 +1,5 @@ +import ConnectionManager from '../../../../common/lib/transport/connectionmanager'; +import Transport from '../../../../common/lib/transport/transport'; + +declare function initialiseNodeCometTransport(connectionManager: typeof ConnectionManager): typeof Transport; +export default initialiseNodeCometTransport; diff --git a/src/platform/web/lib/transport/index.js b/src/platform/web/lib/transport/index.js deleted file mode 100644 index 8fd7afb26a..0000000000 --- a/src/platform/web/lib/transport/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import XHRPollingTransport from './xhrpollingtransport'; -import XHRStreamingTransport from './xhrstreamingtransport'; - -export default [XHRPollingTransport, XHRStreamingTransport]; diff --git a/src/platform/web/lib/transport/index.ts b/src/platform/web/lib/transport/index.ts new file mode 100644 index 0000000000..8d97bccaa7 --- /dev/null +++ b/src/platform/web/lib/transport/index.ts @@ -0,0 +1,4 @@ +import initialiseXHRPollingTransport from './xhrpollingtransport'; +import initialiseXHRStreamingTransport from './xhrstreamingtransport'; + +export default [initialiseXHRPollingTransport, initialiseXHRStreamingTransport]; From 17fce5575ba52f8ef658a28d95e62ba0451a89de Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Thu, 24 Aug 2023 15:20:40 -0300 Subject: [PATCH 05/13] Fix inflection of TransportNames enum name --- .../constants/{TransportNames.ts => TransportName.ts} | 4 ++-- src/common/types/IDefaults.d.ts | 10 +++++----- src/platform/nodejs/lib/util/defaults.ts | 10 +++++----- src/platform/web/lib/util/defaults.ts | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) rename src/common/constants/{TransportNames.ts => TransportName.ts} (68%) diff --git a/src/common/constants/TransportNames.ts b/src/common/constants/TransportName.ts similarity index 68% rename from src/common/constants/TransportNames.ts rename to src/common/constants/TransportName.ts index 9cda74c614..8c49a76cee 100644 --- a/src/common/constants/TransportNames.ts +++ b/src/common/constants/TransportName.ts @@ -1,8 +1,8 @@ -enum TransportNames { +enum TransportName { WebSocket = 'web_socket', Comet = 'comet', XhrStreaming = 'xhr_streaming', XhrPolling = 'xhr_polling', } -export default TransportNames; +export default TransportName; diff --git a/src/common/types/IDefaults.d.ts b/src/common/types/IDefaults.d.ts index 3fb342cbc8..1fd02bf5da 100644 --- a/src/common/types/IDefaults.d.ts +++ b/src/common/types/IDefaults.d.ts @@ -1,11 +1,11 @@ -import TransportNames from '../constants/TransportNames'; +import TransportName from '../constants/TransportName'; import { RestAgentOptions } from './ClientOptions'; export default interface IDefaults { connectivityCheckUrl: string; - defaultTransports: TransportNames[]; - baseTransportOrder: TransportNames[]; - transportPreferenceOrder: TransportNames[]; - upgradeTransports: TransportNames[]; + defaultTransports: TransportName[]; + baseTransportOrder: TransportName[]; + transportPreferenceOrder: TransportName[]; + upgradeTransports: TransportName[]; restAgentOptions?: RestAgentOptions; } diff --git a/src/platform/nodejs/lib/util/defaults.ts b/src/platform/nodejs/lib/util/defaults.ts index adaf8a5083..e6b55f18f8 100644 --- a/src/platform/nodejs/lib/util/defaults.ts +++ b/src/platform/nodejs/lib/util/defaults.ts @@ -1,14 +1,14 @@ import IDefaults from '../../../../common/types/IDefaults'; -import TransportNames from '../../../../common/constants/TransportNames'; +import TransportName from '../../../../common/constants/TransportName'; const Defaults: IDefaults = { connectivityCheckUrl: 'https://internet-up.ably-realtime.com/is-the-internet-up.txt', /* Note: order matters here: the base transport is the leftmost one in the * intersection of baseTransportOrder and the transports clientOption that's supported. */ - defaultTransports: [TransportNames.WebSocket], - baseTransportOrder: [TransportNames.Comet, TransportNames.WebSocket], - transportPreferenceOrder: [TransportNames.Comet, TransportNames.WebSocket], - upgradeTransports: [TransportNames.WebSocket], + defaultTransports: [TransportName.WebSocket], + baseTransportOrder: [TransportName.Comet, TransportName.WebSocket], + transportPreferenceOrder: [TransportName.Comet, TransportName.WebSocket], + upgradeTransports: [TransportName.WebSocket], restAgentOptions: { maxSockets: 40, keepAlive: true }, }; diff --git a/src/platform/web/lib/util/defaults.ts b/src/platform/web/lib/util/defaults.ts index 7366ec75a4..b182008c18 100644 --- a/src/platform/web/lib/util/defaults.ts +++ b/src/platform/web/lib/util/defaults.ts @@ -1,15 +1,15 @@ import IDefaults from 'common/types/IDefaults'; -import TransportNames from 'common/constants/TransportNames'; +import TransportName from 'common/constants/TransportName'; const Defaults: IDefaults = { connectivityCheckUrl: 'https://internet-up.ably-realtime.com/is-the-internet-up.txt', /* Order matters here: the base transport is the leftmost one in the * intersection of baseTransportOrder and the transports clientOption that's * supported. */ - defaultTransports: [TransportNames.XhrPolling, TransportNames.XhrStreaming, TransportNames.WebSocket], - baseTransportOrder: [TransportNames.XhrPolling, TransportNames.XhrStreaming, TransportNames.WebSocket], - transportPreferenceOrder: [TransportNames.XhrPolling, TransportNames.XhrStreaming, TransportNames.WebSocket], - upgradeTransports: [TransportNames.XhrStreaming, TransportNames.WebSocket], + defaultTransports: [TransportName.XhrPolling, TransportName.XhrStreaming, TransportName.WebSocket], + baseTransportOrder: [TransportName.XhrPolling, TransportName.XhrStreaming, TransportName.WebSocket], + transportPreferenceOrder: [TransportName.XhrPolling, TransportName.XhrStreaming, TransportName.WebSocket], + upgradeTransports: [TransportName.XhrStreaming, TransportName.WebSocket], }; export default Defaults; From a0df5f58bb030ab10453104f7fe98833b57dba3c Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Thu, 24 Aug 2023 15:30:13 -0300 Subject: [PATCH 06/13] =?UTF-8?q?Use=20TransportName=20values=20when=20set?= =?UTF-8?q?ting=20transports=E2=80=99=20shortName?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preparation for #1394 (making transports tree-shakable). --- src/common/lib/transport/websockettransport.ts | 3 ++- src/platform/nodejs/lib/transport/nodecomettransport.js | 3 ++- src/platform/web/lib/transport/xhrpollingtransport.ts | 3 ++- src/platform/web/lib/transport/xhrstreamingtransport.ts | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/common/lib/transport/websockettransport.ts b/src/common/lib/transport/websockettransport.ts index 624c23dff5..3daafd2f8f 100644 --- a/src/common/lib/transport/websockettransport.ts +++ b/src/common/lib/transport/websockettransport.ts @@ -8,8 +8,9 @@ import ErrorInfo from '../types/errorinfo'; import NodeWebSocket from 'ws'; import ConnectionManager, { TransportParams } from './connectionmanager'; import Auth from '../client/auth'; +import TransportName from 'common/constants/TransportName'; -const shortName = 'web_socket'; +const shortName = TransportName.WebSocket; function isNodeWebSocket(ws: WebSocket | NodeWebSocket): ws is NodeWebSocket { return !!(ws as NodeWebSocket).on; diff --git a/src/platform/nodejs/lib/transport/nodecomettransport.js b/src/platform/nodejs/lib/transport/nodecomettransport.js index 7ba730eb10..f215c97a74 100644 --- a/src/platform/nodejs/lib/transport/nodecomettransport.js +++ b/src/platform/nodejs/lib/transport/nodecomettransport.js @@ -10,10 +10,11 @@ import http from 'http'; import https from 'https'; import url from 'url'; import util from 'util'; +import TransportName from '../../../../common/constants/TransportName'; var NodeCometTransport = function (connectionManager) { var noop = function () {}; - var shortName = 'comet'; + var shortName = TransportName.Comet; /* * A transport to use with nodejs diff --git a/src/platform/web/lib/transport/xhrpollingtransport.ts b/src/platform/web/lib/transport/xhrpollingtransport.ts index 212af4682f..a0dd833a02 100644 --- a/src/platform/web/lib/transport/xhrpollingtransport.ts +++ b/src/platform/web/lib/transport/xhrpollingtransport.ts @@ -4,8 +4,9 @@ import XHRRequest from './xhrrequest'; import ConnectionManager, { TransportParams } from 'common/lib/transport/connectionmanager'; import Auth from 'common/lib/client/auth'; import { RequestParams } from 'common/types/http'; +import TransportName from 'common/constants/TransportName'; -var shortName = 'xhr_polling'; +var shortName = TransportName.XhrPolling; class XHRPollingTransport extends CometTransport { shortName = shortName; constructor(connectionManager: ConnectionManager, auth: Auth, params: TransportParams) { diff --git a/src/platform/web/lib/transport/xhrstreamingtransport.ts b/src/platform/web/lib/transport/xhrstreamingtransport.ts index d2b816a650..c7c5c4fa2b 100644 --- a/src/platform/web/lib/transport/xhrstreamingtransport.ts +++ b/src/platform/web/lib/transport/xhrstreamingtransport.ts @@ -4,8 +4,9 @@ import XHRRequest from './xhrrequest'; import ConnectionManager, { TransportParams } from 'common/lib/transport/connectionmanager'; import Auth from 'common/lib/client/auth'; import { RequestParams } from 'common/types/http'; +import TransportName from 'common/constants/TransportName'; -const shortName = 'xhr_streaming'; +const shortName = TransportName.XhrStreaming; class XHRStreamingTransport extends CometTransport { shortName = shortName; constructor(connectionManager: ConnectionManager, auth: Auth, params: TransportParams) { From 8d4e4779999f1afac93fb701308d1839f071a77e Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Thu, 24 Aug 2023 15:54:39 -0300 Subject: [PATCH 07/13] =?UTF-8?q?Decouple=20Platform.Transport=E2=80=99s?= =?UTF-8?q?=20transport=20order=20from=20implementations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preparation for #1394 (making transports tree-shakable). --- src/common/lib/transport/connectionmanager.ts | 3 ++- src/common/platform.ts | 8 +++++++- src/platform/nodejs/lib/transport/index.ts | 6 +++++- src/platform/web/lib/transport/index.ts | 9 ++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index 0a7fc38c72..48a99b9010 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -410,7 +410,8 @@ class ConnectionManager extends EventEmitter { static initTransports() { WebSocketTransport(ConnectionManager); - Utils.arrForEach(Platform.Transports, function (initFn) { + Utils.arrForEach(Platform.Transports.order, function (transportName) { + const initFn = Platform.Transports.implementations[transportName]!; initFn(ConnectionManager); }); } diff --git a/src/common/platform.ts b/src/common/platform.ts index aa8e133019..3e103063dc 100644 --- a/src/common/platform.ts +++ b/src/common/platform.ts @@ -7,11 +7,14 @@ import IBufferUtils from './types/IBufferUtils'; import * as WebBufferUtils from '../platform/web/lib/util/bufferutils'; import * as NodeBufferUtils from '../platform/nodejs/lib/util/bufferutils'; import { IUntypedCryptoStatic } from '../common/types/ICryptoStatic'; +import TransportName from './constants/TransportName'; type Bufferlike = WebBufferUtils.Bufferlike | NodeBufferUtils.Bufferlike; type BufferUtilsOutput = WebBufferUtils.Output | NodeBufferUtils.Output; type ToBufferOutput = WebBufferUtils.ToBufferOutput | NodeBufferUtils.ToBufferOutput; +export type TransportImplementations = Partial>; + export default class Platform { static Config: IPlatformConfig; /* @@ -29,7 +32,10 @@ export default class Platform { */ static Crypto: IUntypedCryptoStatic | null; static Http: typeof IHttp; - static Transports: TransportInitialiser[]; + static Transports: { + order: TransportName[]; + implementations: TransportImplementations; + }; static Defaults: IDefaults; static WebStorage: IWebStorage | null; } diff --git a/src/platform/nodejs/lib/transport/index.ts b/src/platform/nodejs/lib/transport/index.ts index 1c0163c500..c5db7c0429 100644 --- a/src/platform/nodejs/lib/transport/index.ts +++ b/src/platform/nodejs/lib/transport/index.ts @@ -1,3 +1,7 @@ +import TransportName from 'common/constants/TransportName'; import initialiseNodeCometTransport from './nodecomettransport'; -export default [initialiseNodeCometTransport]; +export default { + order: [TransportName.Comet], + implementations: { [TransportName.Comet]: initialiseNodeCometTransport }, +}; diff --git a/src/platform/web/lib/transport/index.ts b/src/platform/web/lib/transport/index.ts index 8d97bccaa7..98674dd0e9 100644 --- a/src/platform/web/lib/transport/index.ts +++ b/src/platform/web/lib/transport/index.ts @@ -1,4 +1,11 @@ +import TransportName from 'common/constants/TransportName'; import initialiseXHRPollingTransport from './xhrpollingtransport'; import initialiseXHRStreamingTransport from './xhrstreamingtransport'; -export default [initialiseXHRPollingTransport, initialiseXHRStreamingTransport]; +export default { + order: [TransportName.XhrPolling, TransportName.XhrStreaming], + implementations: { + [TransportName.XhrPolling]: initialiseXHRPollingTransport, + [TransportName.XhrStreaming]: initialiseXHRStreamingTransport, + }, +}; From 4fe06fafe2ba61c0e94c4f37bccc491652efe6f7 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Thu, 24 Aug 2023 10:35:47 -0300 Subject: [PATCH 08/13] Fix connectionManager type in transport initialisers --- src/common/lib/transport/websockettransport.ts | 2 +- src/platform/web/lib/transport/xhrpollingtransport.ts | 2 +- src/platform/web/lib/transport/xhrstreamingtransport.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/lib/transport/websockettransport.ts b/src/common/lib/transport/websockettransport.ts index 3daafd2f8f..540d9cf799 100644 --- a/src/common/lib/transport/websockettransport.ts +++ b/src/common/lib/transport/websockettransport.ts @@ -196,7 +196,7 @@ class WebSocketTransport extends Transport { } } -function initialiseTransport(connectionManager: any): typeof WebSocketTransport { +function initialiseTransport(connectionManager: typeof ConnectionManager): typeof WebSocketTransport { if (WebSocketTransport.isAvailable()) connectionManager.supportedTransports[shortName] = WebSocketTransport; return WebSocketTransport; diff --git a/src/platform/web/lib/transport/xhrpollingtransport.ts b/src/platform/web/lib/transport/xhrpollingtransport.ts index a0dd833a02..5a7dfafd1c 100644 --- a/src/platform/web/lib/transport/xhrpollingtransport.ts +++ b/src/platform/web/lib/transport/xhrpollingtransport.ts @@ -34,7 +34,7 @@ class XHRPollingTransport extends CometTransport { } } -function initialiseTransport(connectionManager: any): typeof XHRPollingTransport { +function initialiseTransport(connectionManager: typeof ConnectionManager): typeof XHRPollingTransport { if (XHRPollingTransport.isAvailable()) connectionManager.supportedTransports[shortName] = XHRPollingTransport; return XHRPollingTransport; diff --git a/src/platform/web/lib/transport/xhrstreamingtransport.ts b/src/platform/web/lib/transport/xhrstreamingtransport.ts index c7c5c4fa2b..8640eec37e 100644 --- a/src/platform/web/lib/transport/xhrstreamingtransport.ts +++ b/src/platform/web/lib/transport/xhrstreamingtransport.ts @@ -32,7 +32,7 @@ class XHRStreamingTransport extends CometTransport { } } -function initialiseTransport(connectionManager: any): typeof XHRStreamingTransport { +function initialiseTransport(connectionManager: typeof ConnectionManager): typeof XHRStreamingTransport { if (XHRStreamingTransport.isAvailable()) connectionManager.supportedTransports[shortName] = XHRStreamingTransport; return XHRStreamingTransport; From af25aa4bd19f75903753722166bf4e65959ebc71 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 29 Aug 2023 16:53:56 -0300 Subject: [PATCH 09/13] Fix supportedTransports type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the bare Record type implies that fetching from it will return a value for any string key, which isn’t the case. --- src/common/lib/transport/connectionmanager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index 48a99b9010..940cf6bcef 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -99,7 +99,7 @@ function decodeRecoveryKey(recoveryKey: string): RecoveryContext | null { } } -const supportedTransports: Record = {}; +const supportedTransports: Partial> = {}; export class TransportParams { options: ClientOptions; @@ -486,7 +486,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.tryATransport()', 'trying ' + candidate); Transport.tryConnect( - ConnectionManager.supportedTransports[candidate], + ConnectionManager.supportedTransports[candidate]!, this, this.realtime.auth, transportParams, From ead86621dffd02e1ee7bdbbfa2dab6ef92d4c9cf Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 29 Aug 2023 17:18:46 -0300 Subject: [PATCH 10/13] Properly type client options in connectionmanager.ts --- src/common/lib/transport/connectionmanager.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index 940cf6bcef..4f97386c8d 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -18,8 +18,7 @@ import * as API from '../../../../ably'; import { ErrCallback } from 'common/types/utils'; import HttpStatusCodes from 'common/constants/HttpStatusCodes'; import BaseRealtime from '../client/baserealtime'; - -type ClientOptions = any; +import { NormalisedClientOptions } from 'common/types/ClientOptions'; let globalObject = typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : self; @@ -91,9 +90,9 @@ type RecoveryContext = { channelSerials: { [name: string]: string }; }; -function decodeRecoveryKey(recoveryKey: string): RecoveryContext | null { +function decodeRecoveryKey(recoveryKey: NormalisedClientOptions['recover']): RecoveryContext | null { try { - return JSON.parse(recoveryKey); + return JSON.parse(recoveryKey as string); } catch (e) { return null; } @@ -102,7 +101,7 @@ function decodeRecoveryKey(recoveryKey: string): RecoveryContext | null { const supportedTransports: Partial> = {}; export class TransportParams { - options: ClientOptions; + options: NormalisedClientOptions; host: string | null; mode: string; format?: Utils.Format; @@ -110,7 +109,7 @@ export class TransportParams { stream?: any; heartbeats?: boolean; - constructor(options: ClientOptions, host: string | null, mode: string, connectionKey?: string) { + constructor(options: NormalisedClientOptions, host: string | null, mode: string, connectionKey?: string) { this.options = options; this.host = host; this.mode = mode; @@ -191,7 +190,7 @@ type ConnectionState = { class ConnectionManager extends EventEmitter { realtime: BaseRealtime; - options: ClientOptions; + options: NormalisedClientOptions; states: Record; state: ConnectionState; errorReason: IPartialErrorInfo | string | null; @@ -226,7 +225,7 @@ class ConnectionManager extends EventEmitter { queue: { message: ProtocolMessage; transport: Transport }[]; } = { isProcessing: false, queue: [] }; - constructor(realtime: BaseRealtime, options: ClientOptions) { + constructor(realtime: BaseRealtime, options: NormalisedClientOptions) { super(); ConnectionManager.initTransports(); this.realtime = realtime; @@ -601,7 +600,7 @@ class ConnectionManager extends EventEmitter { if (mode === 'recover' && this.options.recover) { /* After a successful recovery, we unpersist, as a recovery key cannot * be used more than once */ - this.options.recover = null; + delete this.options.recover; this.unpersistConnection(); } }); From f05de53e0c8a97c8ba7a122a761f3430405f2c12 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 29 Aug 2023 17:22:11 -0300 Subject: [PATCH 11/13] Tighten up type in places where transport name is used --- src/common/constants/TransportName.ts | 16 +++++++++++----- src/common/lib/transport/connectionmanager.ts | 19 ++++++++++--------- .../lib/transport/websockettransport.ts | 4 ++-- src/common/lib/util/utils.ts | 4 ++-- src/platform/nodejs/lib/transport/index.ts | 6 +++--- .../lib/transport/nodecomettransport.js | 4 ++-- src/platform/nodejs/lib/util/defaults.ts | 10 +++++----- src/platform/web/lib/transport/index.ts | 8 ++++---- .../web/lib/transport/xhrpollingtransport.ts | 4 ++-- .../lib/transport/xhrstreamingtransport.ts | 4 ++-- src/platform/web/lib/util/defaults.ts | 10 +++++----- 11 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/common/constants/TransportName.ts b/src/common/constants/TransportName.ts index 8c49a76cee..29137c7f4c 100644 --- a/src/common/constants/TransportName.ts +++ b/src/common/constants/TransportName.ts @@ -1,8 +1,14 @@ -enum TransportName { - WebSocket = 'web_socket', - Comet = 'comet', - XhrStreaming = 'xhr_streaming', - XhrPolling = 'xhr_polling', +export namespace TransportNames { + export const WebSocket = 'web_socket' as const; + export const Comet = 'comet' as const; + export const XhrStreaming = 'xhr_streaming' as const; + export const XhrPolling = 'xhr_polling' as const; } +type TransportName = + | typeof TransportNames.WebSocket + | typeof TransportNames.Comet + | typeof TransportNames.XhrStreaming + | typeof TransportNames.XhrPolling; + export default TransportName; diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index 4f97386c8d..3915879f62 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -19,6 +19,7 @@ import { ErrCallback } from 'common/types/utils'; import HttpStatusCodes from 'common/constants/HttpStatusCodes'; import BaseRealtime from '../client/baserealtime'; import { NormalisedClientOptions } from 'common/types/ClientOptions'; +import TransportName from 'common/constants/TransportName'; let globalObject = typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : self; @@ -98,7 +99,7 @@ function decodeRecoveryKey(recoveryKey: NormalisedClientOptions['recover']): Rec } } -const supportedTransports: Partial> = {}; +const supportedTransports: Partial> = {}; export class TransportParams { options: NormalisedClientOptions; @@ -201,9 +202,9 @@ class ConnectionManager extends EventEmitter { connectionKey?: string; connectionStateTtl: number; maxIdleInterval: number | null; - transports: string[]; - baseTransport: string; - upgradeTransports: string[]; + transports: TransportName[]; + baseTransport: TransportName; + upgradeTransports: TransportName[]; transportPreference: string | null; httpHosts: string[]; activeProtocol: null | Protocol; @@ -481,7 +482,7 @@ class ConnectionManager extends EventEmitter { * @param candidate, the transport to try * @param callback */ - tryATransport(transportParams: TransportParams, candidate: string, callback: Function): void { + tryATransport(transportParams: TransportParams, candidate: TransportName, callback: Function): void { Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.tryATransport()', 'trying ' + candidate); Transport.tryConnect( @@ -1674,13 +1675,13 @@ class ConnectionManager extends EventEmitter { this.tryATransport(transportParams, this.baseTransport, hostAttemptCb); } - getUpgradePossibilities(): string[] { + getUpgradePossibilities(): TransportName[] { /* returns the subset of upgradeTransports to the right of the current * transport in upgradeTransports (if it's in there - if not, currentSerial * will be -1, so return upgradeTransports.slice(0) == upgradeTransports */ const current = (this.activeProtocol as Protocol).getTransport().shortName; const currentSerial = Utils.arrIndexOf(this.upgradeTransports, current); - return this.upgradeTransports.slice(currentSerial + 1) as string[]; + return this.upgradeTransports.slice(currentSerial + 1); } upgradeIfNeeded(transportParams: Record): void { @@ -1695,7 +1696,7 @@ class ConnectionManager extends EventEmitter { return; } - Utils.arrForEach(upgradePossibilities, (upgradeTransport: string) => { + Utils.arrForEach(upgradePossibilities, (upgradeTransport: TransportName) => { /* Note: the transport may mutate the params, so give each transport a fresh one */ const upgradeTransportParams = this.createTransportParams(transportParams.host, 'upgrade'); this.tryATransport(upgradeTransportParams, upgradeTransport, noop); @@ -2098,7 +2099,7 @@ class ConnectionManager extends EventEmitter { this.proposedTransports.push(transport); } - getTransportPreference(): string { + getTransportPreference(): TransportName { return this.transportPreference || (haveWebStorage() && Platform.WebStorage?.get?.(transportPreferenceName)); } diff --git a/src/common/lib/transport/websockettransport.ts b/src/common/lib/transport/websockettransport.ts index 540d9cf799..079ac13036 100644 --- a/src/common/lib/transport/websockettransport.ts +++ b/src/common/lib/transport/websockettransport.ts @@ -8,9 +8,9 @@ import ErrorInfo from '../types/errorinfo'; import NodeWebSocket from 'ws'; import ConnectionManager, { TransportParams } from './connectionmanager'; import Auth from '../client/auth'; -import TransportName from 'common/constants/TransportName'; +import { TransportNames } from 'common/constants/TransportName'; -const shortName = TransportName.WebSocket; +const shortName = TransportNames.WebSocket; function isNodeWebSocket(ws: WebSocket | NodeWebSocket): ws is NodeWebSocket { return !!(ws as NodeWebSocket).on; diff --git a/src/common/lib/util/utils.ts b/src/common/lib/util/utils.ts index ba8fa6f370..494a4b6a3e 100644 --- a/src/common/lib/util/utils.ts +++ b/src/common/lib/util/utils.ts @@ -156,7 +156,7 @@ export function containsValue(ob: Record, val: unknown): boolea return false; } -export function intersect(arr: Array, ob: string[] | Record): string[] { +export function intersect(arr: Array, ob: K[] | Partial>): K[] { return isArray(ob) ? arrIntersect(arr, ob) : arrIntersectOb(arr, ob); } @@ -169,7 +169,7 @@ export function arrIntersect(arr1: Array, arr2: Array): Array { return result; } -export function arrIntersectOb(arr: Array, ob: Record): string[] { +export function arrIntersectOb(arr: Array, ob: Partial>): K[] { const result = []; for (let i = 0; i < arr.length; i++) { const member = arr[i]; diff --git a/src/platform/nodejs/lib/transport/index.ts b/src/platform/nodejs/lib/transport/index.ts index c5db7c0429..084b0cc955 100644 --- a/src/platform/nodejs/lib/transport/index.ts +++ b/src/platform/nodejs/lib/transport/index.ts @@ -1,7 +1,7 @@ -import TransportName from 'common/constants/TransportName'; +import { TransportNames } from 'common/constants/TransportName'; import initialiseNodeCometTransport from './nodecomettransport'; export default { - order: [TransportName.Comet], - implementations: { [TransportName.Comet]: initialiseNodeCometTransport }, + order: [TransportNames.Comet], + implementations: { [TransportNames.Comet]: initialiseNodeCometTransport }, }; diff --git a/src/platform/nodejs/lib/transport/nodecomettransport.js b/src/platform/nodejs/lib/transport/nodecomettransport.js index f215c97a74..6e6655bb0d 100644 --- a/src/platform/nodejs/lib/transport/nodecomettransport.js +++ b/src/platform/nodejs/lib/transport/nodecomettransport.js @@ -10,11 +10,11 @@ import http from 'http'; import https from 'https'; import url from 'url'; import util from 'util'; -import TransportName from '../../../../common/constants/TransportName'; +import { TransportNames } from '../../../../common/constants/TransportName'; var NodeCometTransport = function (connectionManager) { var noop = function () {}; - var shortName = TransportName.Comet; + var shortName = TransportNames.Comet; /* * A transport to use with nodejs diff --git a/src/platform/nodejs/lib/util/defaults.ts b/src/platform/nodejs/lib/util/defaults.ts index e6b55f18f8..8b29953cbd 100644 --- a/src/platform/nodejs/lib/util/defaults.ts +++ b/src/platform/nodejs/lib/util/defaults.ts @@ -1,14 +1,14 @@ import IDefaults from '../../../../common/types/IDefaults'; -import TransportName from '../../../../common/constants/TransportName'; +import { TransportNames } from '../../../../common/constants/TransportName'; const Defaults: IDefaults = { connectivityCheckUrl: 'https://internet-up.ably-realtime.com/is-the-internet-up.txt', /* Note: order matters here: the base transport is the leftmost one in the * intersection of baseTransportOrder and the transports clientOption that's supported. */ - defaultTransports: [TransportName.WebSocket], - baseTransportOrder: [TransportName.Comet, TransportName.WebSocket], - transportPreferenceOrder: [TransportName.Comet, TransportName.WebSocket], - upgradeTransports: [TransportName.WebSocket], + defaultTransports: [TransportNames.WebSocket], + baseTransportOrder: [TransportNames.Comet, TransportNames.WebSocket], + transportPreferenceOrder: [TransportNames.Comet, TransportNames.WebSocket], + upgradeTransports: [TransportNames.WebSocket], restAgentOptions: { maxSockets: 40, keepAlive: true }, }; diff --git a/src/platform/web/lib/transport/index.ts b/src/platform/web/lib/transport/index.ts index 98674dd0e9..8fb1447186 100644 --- a/src/platform/web/lib/transport/index.ts +++ b/src/platform/web/lib/transport/index.ts @@ -1,11 +1,11 @@ -import TransportName from 'common/constants/TransportName'; +import { TransportNames } from 'common/constants/TransportName'; import initialiseXHRPollingTransport from './xhrpollingtransport'; import initialiseXHRStreamingTransport from './xhrstreamingtransport'; export default { - order: [TransportName.XhrPolling, TransportName.XhrStreaming], + order: [TransportNames.XhrPolling, TransportNames.XhrStreaming], implementations: { - [TransportName.XhrPolling]: initialiseXHRPollingTransport, - [TransportName.XhrStreaming]: initialiseXHRStreamingTransport, + [TransportNames.XhrPolling]: initialiseXHRPollingTransport, + [TransportNames.XhrStreaming]: initialiseXHRStreamingTransport, }, }; diff --git a/src/platform/web/lib/transport/xhrpollingtransport.ts b/src/platform/web/lib/transport/xhrpollingtransport.ts index 5a7dfafd1c..c063397198 100644 --- a/src/platform/web/lib/transport/xhrpollingtransport.ts +++ b/src/platform/web/lib/transport/xhrpollingtransport.ts @@ -4,9 +4,9 @@ import XHRRequest from './xhrrequest'; import ConnectionManager, { TransportParams } from 'common/lib/transport/connectionmanager'; import Auth from 'common/lib/client/auth'; import { RequestParams } from 'common/types/http'; -import TransportName from 'common/constants/TransportName'; +import { TransportNames } from 'common/constants/TransportName'; -var shortName = TransportName.XhrPolling; +var shortName = TransportNames.XhrPolling; class XHRPollingTransport extends CometTransport { shortName = shortName; constructor(connectionManager: ConnectionManager, auth: Auth, params: TransportParams) { diff --git a/src/platform/web/lib/transport/xhrstreamingtransport.ts b/src/platform/web/lib/transport/xhrstreamingtransport.ts index 8640eec37e..57cf30e7b7 100644 --- a/src/platform/web/lib/transport/xhrstreamingtransport.ts +++ b/src/platform/web/lib/transport/xhrstreamingtransport.ts @@ -4,9 +4,9 @@ import XHRRequest from './xhrrequest'; import ConnectionManager, { TransportParams } from 'common/lib/transport/connectionmanager'; import Auth from 'common/lib/client/auth'; import { RequestParams } from 'common/types/http'; -import TransportName from 'common/constants/TransportName'; +import { TransportNames } from 'common/constants/TransportName'; -const shortName = TransportName.XhrStreaming; +const shortName = TransportNames.XhrStreaming; class XHRStreamingTransport extends CometTransport { shortName = shortName; constructor(connectionManager: ConnectionManager, auth: Auth, params: TransportParams) { diff --git a/src/platform/web/lib/util/defaults.ts b/src/platform/web/lib/util/defaults.ts index b182008c18..3dbd549733 100644 --- a/src/platform/web/lib/util/defaults.ts +++ b/src/platform/web/lib/util/defaults.ts @@ -1,15 +1,15 @@ import IDefaults from 'common/types/IDefaults'; -import TransportName from 'common/constants/TransportName'; +import { TransportNames } from 'common/constants/TransportName'; const Defaults: IDefaults = { connectivityCheckUrl: 'https://internet-up.ably-realtime.com/is-the-internet-up.txt', /* Order matters here: the base transport is the leftmost one in the * intersection of baseTransportOrder and the transports clientOption that's * supported. */ - defaultTransports: [TransportName.XhrPolling, TransportName.XhrStreaming, TransportName.WebSocket], - baseTransportOrder: [TransportName.XhrPolling, TransportName.XhrStreaming, TransportName.WebSocket], - transportPreferenceOrder: [TransportName.XhrPolling, TransportName.XhrStreaming, TransportName.WebSocket], - upgradeTransports: [TransportName.XhrStreaming, TransportName.WebSocket], + defaultTransports: [TransportNames.XhrPolling, TransportNames.XhrStreaming, TransportNames.WebSocket], + baseTransportOrder: [TransportNames.XhrPolling, TransportNames.XhrStreaming, TransportNames.WebSocket], + transportPreferenceOrder: [TransportNames.XhrPolling, TransportNames.XhrStreaming, TransportNames.WebSocket], + upgradeTransports: [TransportNames.XhrStreaming, TransportNames.WebSocket], }; export default Defaults; From 57c09f6f3c65f8c0300af3908059ef3ba8f63daf Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Thu, 24 Aug 2023 10:33:53 -0300 Subject: [PATCH 12/13] Move supportedTransports storage into ConnectionManager instance Preparation for #1394 (making transports tree-shakable). --- src/common/lib/transport/connectionmanager.ts | 31 ++++++++++++------- .../lib/transport/websockettransport.ts | 6 ++-- .../lib/transport/nodecomettransport.d.ts | 4 +-- .../lib/transport/nodecomettransport.js | 4 +-- .../web/lib/transport/xhrpollingtransport.ts | 6 ++-- .../lib/transport/xhrstreamingtransport.ts | 6 ++-- test/common/modules/shared_helper.js | 1 - 7 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index 3915879f62..fb8311824b 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -99,8 +99,6 @@ function decodeRecoveryKey(recoveryKey: NormalisedClientOptions['recover']): Rec } } -const supportedTransports: Partial> = {}; - export class TransportParams { options: NormalisedClientOptions; host: string | null; @@ -190,6 +188,7 @@ type ConnectionState = { }; class ConnectionManager extends EventEmitter { + supportedTransports: Partial> = {}; realtime: BaseRealtime; options: NormalisedClientOptions; states: Record; @@ -228,7 +227,7 @@ class ConnectionManager extends EventEmitter { constructor(realtime: BaseRealtime, options: NormalisedClientOptions) { super(); - ConnectionManager.initTransports(); + this.initTransports(); this.realtime = realtime; this.options = options; const timeouts = options.timeouts; @@ -305,10 +304,7 @@ class ConnectionManager extends EventEmitter { this.connectionStateTtl = timeouts.connectionStateTtl; this.maxIdleInterval = null; - this.transports = Utils.intersect( - options.transports || Defaults.defaultTransports, - ConnectionManager.supportedTransports - ); + this.transports = Utils.intersect(options.transports || Defaults.defaultTransports, this.supportedTransports); /* baseTransports selects the leftmost transport in the Defaults.baseTransportOrder list * that's both requested and supported. */ this.baseTransport = Utils.intersect(Defaults.baseTransportOrder, this.transports)[0]; @@ -404,18 +400,25 @@ class ConnectionManager extends EventEmitter { * transport management *********************/ + // Used by tests static get supportedTransports() { - return supportedTransports; + const storage: TransportStorage = { supportedTransports: {} }; + this.initTransports(storage); + return storage.supportedTransports; } - static initTransports() { - WebSocketTransport(ConnectionManager); + private static initTransports(storage: TransportStorage) { + WebSocketTransport(storage); Utils.arrForEach(Platform.Transports.order, function (transportName) { const initFn = Platform.Transports.implementations[transportName]!; initFn(ConnectionManager); }); } + initTransports() { + ConnectionManager.initTransports(this); + } + createTransportParams(host: string | null, mode: string): TransportParams { return new TransportParams(this.options, host, mode, this.connectionKey); } @@ -486,7 +489,7 @@ class ConnectionManager extends EventEmitter { Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.tryATransport()', 'trying ' + candidate); Transport.tryConnect( - ConnectionManager.supportedTransports[candidate]!, + this.supportedTransports[candidate]!, this, this.realtime.auth, transportParams, @@ -2169,4 +2172,8 @@ class ConnectionManager extends EventEmitter { export default ConnectionManager; -export type TransportInitialiser = (connectionManager: typeof ConnectionManager) => typeof Transport; +export interface TransportStorage { + supportedTransports: Partial>; +} + +export type TransportInitialiser = (transportStorage: TransportStorage) => typeof Transport; diff --git a/src/common/lib/transport/websockettransport.ts b/src/common/lib/transport/websockettransport.ts index 079ac13036..60f2cc061a 100644 --- a/src/common/lib/transport/websockettransport.ts +++ b/src/common/lib/transport/websockettransport.ts @@ -6,7 +6,7 @@ import Logger from '../util/logger'; import ProtocolMessage from '../types/protocolmessage'; import ErrorInfo from '../types/errorinfo'; import NodeWebSocket from 'ws'; -import ConnectionManager, { TransportParams } from './connectionmanager'; +import ConnectionManager, { TransportParams, TransportStorage } from './connectionmanager'; import Auth from '../client/auth'; import { TransportNames } from 'common/constants/TransportName'; @@ -196,8 +196,8 @@ class WebSocketTransport extends Transport { } } -function initialiseTransport(connectionManager: typeof ConnectionManager): typeof WebSocketTransport { - if (WebSocketTransport.isAvailable()) connectionManager.supportedTransports[shortName] = WebSocketTransport; +function initialiseTransport(transportStorage: TransportStorage): typeof WebSocketTransport { + if (WebSocketTransport.isAvailable()) transportStorage.supportedTransports[shortName] = WebSocketTransport; return WebSocketTransport; } diff --git a/src/platform/nodejs/lib/transport/nodecomettransport.d.ts b/src/platform/nodejs/lib/transport/nodecomettransport.d.ts index 9067155ab9..f90fa468b0 100644 --- a/src/platform/nodejs/lib/transport/nodecomettransport.d.ts +++ b/src/platform/nodejs/lib/transport/nodecomettransport.d.ts @@ -1,5 +1,5 @@ -import ConnectionManager from '../../../../common/lib/transport/connectionmanager'; +import { TransportStorage } from '../../../../common/lib/transport/connectionmanager'; import Transport from '../../../../common/lib/transport/transport'; -declare function initialiseNodeCometTransport(connectionManager: typeof ConnectionManager): typeof Transport; +declare function initialiseNodeCometTransport(transportStorage: TransportStorage): typeof Transport; export default initialiseNodeCometTransport; diff --git a/src/platform/nodejs/lib/transport/nodecomettransport.js b/src/platform/nodejs/lib/transport/nodecomettransport.js index 6e6655bb0d..9a1c29411d 100644 --- a/src/platform/nodejs/lib/transport/nodecomettransport.js +++ b/src/platform/nodejs/lib/transport/nodecomettransport.js @@ -12,7 +12,7 @@ import url from 'url'; import util from 'util'; import { TransportNames } from '../../../../common/constants/TransportName'; -var NodeCometTransport = function (connectionManager) { +var NodeCometTransport = function (transportStorage) { var noop = function () {}; var shortName = TransportNames.Comet; @@ -32,7 +32,7 @@ var NodeCometTransport = function (connectionManager) { NodeCometTransport.isAvailable = function () { return true; }; - connectionManager.supportedTransports[shortName] = NodeCometTransport; + transportStorage.supportedTransports[shortName] = NodeCometTransport; NodeCometTransport.prototype.toString = function () { return ( diff --git a/src/platform/web/lib/transport/xhrpollingtransport.ts b/src/platform/web/lib/transport/xhrpollingtransport.ts index c063397198..4d3b2110d3 100644 --- a/src/platform/web/lib/transport/xhrpollingtransport.ts +++ b/src/platform/web/lib/transport/xhrpollingtransport.ts @@ -1,7 +1,7 @@ import Platform from '../../../../common/platform'; import CometTransport from '../../../../common/lib/transport/comettransport'; import XHRRequest from './xhrrequest'; -import ConnectionManager, { TransportParams } from 'common/lib/transport/connectionmanager'; +import ConnectionManager, { TransportParams, TransportStorage } from 'common/lib/transport/connectionmanager'; import Auth from 'common/lib/client/auth'; import { RequestParams } from 'common/types/http'; import { TransportNames } from 'common/constants/TransportName'; @@ -34,8 +34,8 @@ class XHRPollingTransport extends CometTransport { } } -function initialiseTransport(connectionManager: typeof ConnectionManager): typeof XHRPollingTransport { - if (XHRPollingTransport.isAvailable()) connectionManager.supportedTransports[shortName] = XHRPollingTransport; +function initialiseTransport(transportStorage: TransportStorage): typeof XHRPollingTransport { + if (XHRPollingTransport.isAvailable()) transportStorage.supportedTransports[shortName] = XHRPollingTransport; return XHRPollingTransport; } diff --git a/src/platform/web/lib/transport/xhrstreamingtransport.ts b/src/platform/web/lib/transport/xhrstreamingtransport.ts index 57cf30e7b7..9ef8631fec 100644 --- a/src/platform/web/lib/transport/xhrstreamingtransport.ts +++ b/src/platform/web/lib/transport/xhrstreamingtransport.ts @@ -1,7 +1,7 @@ import CometTransport from '../../../../common/lib/transport/comettransport'; import Platform from '../../../../common/platform'; import XHRRequest from './xhrrequest'; -import ConnectionManager, { TransportParams } from 'common/lib/transport/connectionmanager'; +import ConnectionManager, { TransportParams, TransportStorage } from 'common/lib/transport/connectionmanager'; import Auth from 'common/lib/client/auth'; import { RequestParams } from 'common/types/http'; import { TransportNames } from 'common/constants/TransportName'; @@ -32,8 +32,8 @@ class XHRStreamingTransport extends CometTransport { } } -function initialiseTransport(connectionManager: typeof ConnectionManager): typeof XHRStreamingTransport { - if (XHRStreamingTransport.isAvailable()) connectionManager.supportedTransports[shortName] = XHRStreamingTransport; +function initialiseTransport(transportStorage: TransportStorage): typeof XHRStreamingTransport { + if (XHRStreamingTransport.isAvailable()) transportStorage.supportedTransports[shortName] = XHRStreamingTransport; return XHRStreamingTransport; } diff --git a/test/common/modules/shared_helper.js b/test/common/modules/shared_helper.js index 4d8581ab22..ecf011b760 100644 --- a/test/common/modules/shared_helper.js +++ b/test/common/modules/shared_helper.js @@ -14,7 +14,6 @@ define([ var platform = clientModule.Ably.Realtime.Platform; var BufferUtils = platform.BufferUtils; var expect = chai.expect; - clientModule.Ably.Realtime.ConnectionManager.initTransports(); var availableTransports = utils.keysArray(clientModule.Ably.Realtime.ConnectionManager.supportedTransports), bestTransport = availableTransports[0], /* IANA reserved; requests to it will hang forever */ From d7f9bd5b41651e4692f244eb41385c015b98a332 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Wed, 23 Aug 2023 14:21:56 -0300 Subject: [PATCH 13/13] Make transports tree-shakable We expose WebSocketTransport, XHRPolling, and XHRStreaming modules. Resolves #1394. --- scripts/moduleReport.js | 10 +- src/common/lib/client/baserealtime.ts | 21 +++ src/common/lib/client/defaultrealtime.ts | 9 +- src/common/lib/client/modulesmap.ts | 4 + src/common/lib/transport/connectionmanager.ts | 28 ++-- src/common/platform.ts | 3 +- src/platform/nodejs/lib/transport/index.ts | 6 +- src/platform/web/lib/transport/index.ts | 26 +++- src/platform/web/modules.ts | 5 +- src/platform/web/modules/transports.ts | 3 + test/browser/modules.test.js | 147 +++++++++++++----- test/browser/simple.test.js | 2 +- test/common/modules/shared_helper.js | 4 +- 13 files changed, 205 insertions(+), 63 deletions(-) create mode 100644 src/platform/web/modules/transports.ts diff --git a/scripts/moduleReport.js b/scripts/moduleReport.js index 84d6bac99b..ee5cadf0a8 100644 --- a/scripts/moduleReport.js +++ b/scripts/moduleReport.js @@ -1,7 +1,15 @@ const esbuild = require('esbuild'); // List of all modules accepted in ModulesMap -const moduleNames = ['Rest', 'Crypto', 'MsgPack', 'RealtimePresence']; +const moduleNames = [ + 'Rest', + 'Crypto', + 'MsgPack', + 'RealtimePresence', + 'XHRPolling', + 'XHRStreaming', + 'WebSocketTransport', +]; // List of all free-standing functions exported by the library along with the // ModulesMap entries that we expect them to transitively import diff --git a/src/common/lib/client/baserealtime.ts b/src/common/lib/client/baserealtime.ts index ff51a2a773..0fe9558313 100644 --- a/src/common/lib/client/baserealtime.ts +++ b/src/common/lib/client/baserealtime.ts @@ -11,24 +11,45 @@ import ClientOptions from '../../types/ClientOptions'; import * as API from '../../../../ably'; import { ModulesMap } from './modulesmap'; import RealtimePresence from './realtimepresence'; +import { TransportNames } from 'common/constants/TransportName'; +import { TransportImplementations } from 'common/platform'; /** `BaseRealtime` is an export of the tree-shakable version of the SDK, and acts as the base class for the `DefaultRealtime` class exported by the non tree-shakable version. */ class BaseRealtime extends BaseClient { readonly _RealtimePresence: typeof RealtimePresence | null; + // Extra transport implementations available to this client, in addition to those in Platform.Transports.bundledImplementations + readonly _additionalTransportImplementations: TransportImplementations; _channels: any; connection: Connection; constructor(options: ClientOptions, modules: ModulesMap) { super(options, modules); Logger.logAction(Logger.LOG_MINOR, 'Realtime()', ''); + this._additionalTransportImplementations = BaseRealtime.transportImplementationsFromModules(modules); this._RealtimePresence = modules.RealtimePresence ?? null; this.connection = new Connection(this, this.options); this._channels = new Channels(this); if (options.autoConnect !== false) this.connect(); } + private static transportImplementationsFromModules(modules: ModulesMap) { + const transports: TransportImplementations = {}; + + if (modules.WebSocketTransport) { + transports[TransportNames.WebSocket] = modules.WebSocketTransport; + } + if (modules.XHRStreaming) { + transports[TransportNames.XhrStreaming] = modules.XHRStreaming; + } + if (modules.XHRPolling) { + transports[TransportNames.XhrPolling] = modules.XHRPolling; + } + + return transports; + } + get channels() { return this._channels; } diff --git a/src/common/lib/client/defaultrealtime.ts b/src/common/lib/client/defaultrealtime.ts index 242e612288..2204ab71ac 100644 --- a/src/common/lib/client/defaultrealtime.ts +++ b/src/common/lib/client/defaultrealtime.ts @@ -9,6 +9,7 @@ import { DefaultMessage } from '../types/defaultmessage'; import { MsgPack } from 'common/types/msgpack'; import RealtimePresence from './realtimepresence'; import { DefaultPresenceMessage } from '../types/defaultpresencemessage'; +import initialiseWebSocketTransport from '../transport/websockettransport'; /** `DefaultRealtime` is the class that the non tree-shakable version of the SDK exports as `Realtime`. It ensures that this version of the SDK includes all of the functionality which is optionally available in the tree-shakable version. @@ -20,7 +21,13 @@ export class DefaultRealtime extends BaseRealtime { throw new Error('Expected DefaultRealtime._MsgPack to have been set'); } - super(options, { ...allCommonModules, Crypto: DefaultRealtime.Crypto ?? undefined, MsgPack, RealtimePresence }); + super(options, { + ...allCommonModules, + Crypto: DefaultRealtime.Crypto ?? undefined, + MsgPack, + RealtimePresence, + WebSocketTransport: initialiseWebSocketTransport, + }); } static Utils = Utils; diff --git a/src/common/lib/client/modulesmap.ts b/src/common/lib/client/modulesmap.ts index a4de0a0d51..ada7de44ee 100644 --- a/src/common/lib/client/modulesmap.ts +++ b/src/common/lib/client/modulesmap.ts @@ -2,12 +2,16 @@ import { Rest } from './rest'; import { IUntypedCryptoStatic } from '../../types/ICryptoStatic'; import { MsgPack } from 'common/types/msgpack'; import RealtimePresence from './realtimepresence'; +import { TransportInitialiser } from '../transport/connectionmanager'; export interface ModulesMap { Rest?: typeof Rest; Crypto?: IUntypedCryptoStatic; MsgPack?: MsgPack; RealtimePresence?: typeof RealtimePresence; + WebSocketTransport?: TransportInitialiser; + XHRPolling?: TransportInitialiser; + XHRStreaming?: TransportInitialiser; } export const allCommonModules: ModulesMap = { Rest }; diff --git a/src/common/lib/transport/connectionmanager.ts b/src/common/lib/transport/connectionmanager.ts index fb8311824b..335e2a01d4 100644 --- a/src/common/lib/transport/connectionmanager.ts +++ b/src/common/lib/transport/connectionmanager.ts @@ -2,7 +2,7 @@ import ProtocolMessage from 'common/lib/types/protocolmessage'; import * as Utils from 'common/lib/util/utils'; import Protocol, { PendingMessage } from './protocol'; import Defaults, { getAgentString } from 'common/lib/util/defaults'; -import Platform from 'common/platform'; +import Platform, { TransportImplementations } from 'common/platform'; import EventEmitter from '../util/eventemitter'; import MessageQueue from './messagequeue'; import Logger from '../util/logger'; @@ -12,14 +12,13 @@ import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from 'common/lib/types import Auth from 'common/lib/client/auth'; import Message from 'common/lib/types/message'; import Multicaster, { MulticasterInstance } from 'common/lib/util/multicaster'; -import WebSocketTransport from './websockettransport'; import Transport, { TransportCtor } from './transport'; import * as API from '../../../../ably'; import { ErrCallback } from 'common/types/utils'; import HttpStatusCodes from 'common/constants/HttpStatusCodes'; import BaseRealtime from '../client/baserealtime'; import { NormalisedClientOptions } from 'common/types/ClientOptions'; -import TransportName from 'common/constants/TransportName'; +import TransportName, { TransportNames } from 'common/constants/TransportName'; let globalObject = typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : self; @@ -227,8 +226,8 @@ class ConnectionManager extends EventEmitter { constructor(realtime: BaseRealtime, options: NormalisedClientOptions) { super(); - this.initTransports(); this.realtime = realtime; + this.initTransports(); this.options = options; const timeouts = options.timeouts; /* connectingTimeout: leave preferenceConnectTimeout (~6s) to try the @@ -401,22 +400,29 @@ class ConnectionManager extends EventEmitter { *********************/ // Used by tests - static get supportedTransports() { + static supportedTransports(additionalImplementations: TransportImplementations) { const storage: TransportStorage = { supportedTransports: {} }; - this.initTransports(storage); + this.initTransports(additionalImplementations, storage); return storage.supportedTransports; } - private static initTransports(storage: TransportStorage) { - WebSocketTransport(storage); + private static initTransports(additionalImplementations: TransportImplementations, storage: TransportStorage) { + const implementations = { ...Platform.Transports.bundledImplementations, ...additionalImplementations }; + + const initialiseWebSocketTransport = implementations[TransportNames.WebSocket]; + if (initialiseWebSocketTransport) { + initialiseWebSocketTransport(storage); + } Utils.arrForEach(Platform.Transports.order, function (transportName) { - const initFn = Platform.Transports.implementations[transportName]!; - initFn(ConnectionManager); + const initFn = implementations[transportName]; + if (initFn) { + initFn(storage); + } }); } initTransports() { - ConnectionManager.initTransports(this); + ConnectionManager.initTransports(this.realtime._additionalTransportImplementations, this); } createTransportParams(host: string | null, mode: string): TransportParams { diff --git a/src/common/platform.ts b/src/common/platform.ts index 3e103063dc..609b232687 100644 --- a/src/common/platform.ts +++ b/src/common/platform.ts @@ -34,7 +34,8 @@ export default class Platform { static Http: typeof IHttp; static Transports: { order: TransportName[]; - implementations: TransportImplementations; + // Transport implementations that always come with this platform + bundledImplementations: TransportImplementations; }; static Defaults: IDefaults; static WebStorage: IWebStorage | null; diff --git a/src/platform/nodejs/lib/transport/index.ts b/src/platform/nodejs/lib/transport/index.ts index 084b0cc955..80ab1d0115 100644 --- a/src/platform/nodejs/lib/transport/index.ts +++ b/src/platform/nodejs/lib/transport/index.ts @@ -1,7 +1,11 @@ import { TransportNames } from 'common/constants/TransportName'; import initialiseNodeCometTransport from './nodecomettransport'; +import { default as initialiseWebSocketTransport } from '../../../../common/lib/transport/websockettransport'; export default { order: [TransportNames.Comet], - implementations: { [TransportNames.Comet]: initialiseNodeCometTransport }, + bundledImplementations: { + [TransportNames.WebSocket]: initialiseWebSocketTransport, + [TransportNames.Comet]: initialiseNodeCometTransport, + }, }; diff --git a/src/platform/web/lib/transport/index.ts b/src/platform/web/lib/transport/index.ts index 8fb1447186..ced6dc4eef 100644 --- a/src/platform/web/lib/transport/index.ts +++ b/src/platform/web/lib/transport/index.ts @@ -1,11 +1,25 @@ -import { TransportNames } from 'common/constants/TransportName'; +import TransportName from 'common/constants/TransportName'; +import Platform from 'common/platform'; import initialiseXHRPollingTransport from './xhrpollingtransport'; import initialiseXHRStreamingTransport from './xhrstreamingtransport'; +import { default as initialiseWebSocketTransport } from '../../../../common/lib/transport/websockettransport'; -export default { - order: [TransportNames.XhrPolling, TransportNames.XhrStreaming], - implementations: { - [TransportNames.XhrPolling]: initialiseXHRPollingTransport, - [TransportNames.XhrStreaming]: initialiseXHRStreamingTransport, +// For reasons that I don’t understand, if we use [TransportNames.XhrStreaming] and [TransportNames.XhrPolling] for the keys in defaultTransports’s, then defaultTransports does not get tree-shaken. Hence using literals instead. They’re still correctly type-checked. + +const order: TransportName[] = ['xhr_polling', 'xhr_streaming']; + +const defaultTransports: (typeof Platform)['Transports'] = { + order, + bundledImplementations: { + web_socket: initialiseWebSocketTransport, + xhr_polling: initialiseXHRPollingTransport, + xhr_streaming: initialiseXHRStreamingTransport, }, }; + +export default defaultTransports; + +export const ModulesTransports: (typeof Platform)['Transports'] = { + order, + bundledImplementations: {}, +}; diff --git a/src/platform/web/modules.ts b/src/platform/web/modules.ts index 0488f6bdf3..a3bf018afe 100644 --- a/src/platform/web/modules.ts +++ b/src/platform/web/modules.ts @@ -10,7 +10,7 @@ import BufferUtils from './lib/util/bufferutils'; import Http from './lib/util/http'; import Config from './config'; // @ts-ignore -import Transports from './lib/transport'; +import { ModulesTransports } from './lib/transport'; import Logger from '../../common/lib/util/logger'; import { getDefaults } from '../../common/lib/util/defaults'; import WebStorage from './lib/util/webstorage'; @@ -19,7 +19,7 @@ import PlatformDefaults from './lib/util/defaults'; Platform.BufferUtils = BufferUtils; Platform.Http = Http; Platform.Config = Config; -Platform.Transports = Transports; +Platform.Transports = ModulesTransports; Platform.WebStorage = WebStorage; Logger.initLogHandlers(); @@ -44,5 +44,6 @@ export * from './modules/message'; export * from './modules/presencemessage'; export * from './modules/msgpack'; export * from './modules/realtimepresence'; +export * from './modules/transports'; export { Rest } from '../../common/lib/client/rest'; export { BaseRest, BaseRealtime, ErrorInfo }; diff --git a/src/platform/web/modules/transports.ts b/src/platform/web/modules/transports.ts new file mode 100644 index 0000000000..37c0a09cf8 --- /dev/null +++ b/src/platform/web/modules/transports.ts @@ -0,0 +1,3 @@ +export { default as XHRPolling } from '../lib/transport/xhrpollingtransport'; +export { default as XHRStreaming } from '../lib/transport/xhrstreamingtransport'; +export { default as WebSocketTransport } from '../../../common/lib/transport/websockettransport'; diff --git a/test/browser/modules.test.js b/test/browser/modules.test.js index 0e0acd38a7..501bd09f9d 100644 --- a/test/browser/modules.test.js +++ b/test/browser/modules.test.js @@ -14,6 +14,9 @@ import { decodePresenceMessage, decodePresenceMessages, constructPresenceMessage, + XHRPolling, + XHRStreaming, + WebSocketTransport, } from '../../build/modules/index.js'; describe('browser/modules', function () { @@ -42,13 +45,17 @@ describe('browser/modules', function () { }); describe('without any modules', () => { - for (const clientClass of [BaseRest, BaseRealtime]) { - describe(clientClass.name, () => { - it('can be constructed', async () => { - expect(() => new clientClass(ablyClientOptions(), {})).not.to.throw(); - }); + describe('BaseRest', () => { + it('can be constructed', () => { + expect(() => new BaseRest(ablyClientOptions(), {})).not.to.throw(); }); - } + }); + + describe('BaseRealtime', () => { + it('throws an error due to absence of a transport module', () => { + expect(() => new BaseRealtime(ablyClientOptions(), {})).to.throw('no requested transports available'); + }); + }); }); describe('Rest', () => { @@ -62,7 +69,7 @@ describe('browser/modules', function () { describe('BaseRealtime with Rest', () => { it('offers REST functionality', async () => { - const client = new BaseRealtime(ablyClientOptions(), { Rest }); + const client = new BaseRealtime(ablyClientOptions(), { WebSocketTransport, Rest }); const time = await client.time(); expect(time).to.be.a('number'); }); @@ -70,7 +77,7 @@ describe('browser/modules', function () { describe('BaseRealtime without Rest', () => { it('throws an error when attempting to use REST functionality', async () => { - const client = new BaseRealtime(ablyClientOptions(), {}); + const client = new BaseRealtime(ablyClientOptions(), { WebSocketTransport }); expect(() => client.time()).to.throw('Rest module not provided'); }); }); @@ -206,48 +213,68 @@ describe('browser/modules', function () { describe('Crypto', () => { describe('without Crypto', () => { - for (const clientClass of [BaseRest, BaseRealtime]) { - describe(clientClass.name, () => { + async function testThrowsAnErrorWhenGivenChannelOptionsWithACipher(clientClassConfig) { + const client = new clientClassConfig.clientClass( + ablyClientOptions(), + clientClassConfig.additionalModules ?? {} + ); + const key = await generateRandomKey(); + expect(() => client.channels.get('channel', { cipher: { key } })).to.throw('Crypto module not provided'); + } + + for (const clientClassConfig of [ + { clientClass: BaseRest }, + { clientClass: BaseRealtime, additionalModules: { WebSocketTransport } }, + ]) { + describe(clientClassConfig.clientClass.name, () => { it('throws an error when given channel options with a cipher', async () => { - const client = new clientClass(ablyClientOptions(), {}); - const key = await generateRandomKey(); - expect(() => client.channels.get('channel', { cipher: { key } })).to.throw('Crypto module not provided'); + await testThrowsAnErrorWhenGivenChannelOptionsWithACipher(clientClassConfig); }); }); } }); describe('with Crypto', () => { - for (const clientClass of [BaseRest, BaseRealtime]) { - describe(clientClass.name, () => { - it('is able to publish encrypted messages', async () => { - const clientOptions = ablyClientOptions(); + async function testIsAbleToPublishEncryptedMessages(clientClassConfig) { + const clientOptions = ablyClientOptions(); - const key = await generateRandomKey(); + const key = await generateRandomKey(); - // Publish the message on a channel configured to use encryption, and receive it on one not configured to use encryption + // Publish the message on a channel configured to use encryption, and receive it on one not configured to use encryption - const rxClient = new BaseRealtime(clientOptions, {}); - const rxChannel = rxClient.channels.get('channel'); - await rxChannel.attach(); + const rxClient = new BaseRealtime(clientOptions, { WebSocketTransport }); + const rxChannel = rxClient.channels.get('channel'); + await rxChannel.attach(); - const rxMessagePromise = new Promise((resolve, _) => rxChannel.subscribe((message) => resolve(message))); + const rxMessagePromise = new Promise((resolve, _) => rxChannel.subscribe((message) => resolve(message))); - const encryptionChannelOptions = { cipher: { key } }; + const encryptionChannelOptions = { cipher: { key } }; - const txMessage = { name: 'message', data: 'data' }; - const txClient = new clientClass(clientOptions, { Crypto }); - const txChannel = txClient.channels.get('channel', encryptionChannelOptions); - await txChannel.publish(txMessage); + const txMessage = { name: 'message', data: 'data' }; + const txClient = new clientClassConfig.clientClass(clientOptions, { + ...(clientClassConfig.additionalModules ?? {}), + Crypto, + }); + const txChannel = txClient.channels.get('channel', encryptionChannelOptions); + await txChannel.publish(txMessage); + + const rxMessage = await rxMessagePromise; - const rxMessage = await rxMessagePromise; + // Verify that the message was published with encryption + expect(rxMessage.encoding).to.equal('utf-8/cipher+aes-256-cbc'); - // Verify that the message was published with encryption - expect(rxMessage.encoding).to.equal('utf-8/cipher+aes-256-cbc'); + // Verify that the message was correctly encrypted + const rxMessageDecrypted = await decodeEncryptedMessage(rxMessage, encryptionChannelOptions); + testMessageEquality(rxMessageDecrypted, txMessage); + } - // Verify that the message was correctly encrypted - const rxMessageDecrypted = await decodeEncryptedMessage(rxMessage, encryptionChannelOptions); - testMessageEquality(rxMessageDecrypted, txMessage); + for (const clientClassConfig of [ + { clientClass: BaseRest }, + { clientClass: BaseRealtime, additionalModules: { WebSocketTransport } }, + ]) { + describe(clientClassConfig.clientClass.name, () => { + it('is able to publish encrypted messages', async () => { + await testIsAbleToPublishEncryptedMessages(clientClassConfig); }); }); } @@ -298,7 +325,9 @@ describe('browser/modules', function () { describe('BaseRealtime', () => { it('uses JSON', async () => { - const client = new BaseRealtime(ablyClientOptions({ useBinaryProtocol: true, autoConnect: false }), {}); + const client = new BaseRealtime(ablyClientOptions({ useBinaryProtocol: true, autoConnect: false }), { + WebSocketTransport, + }); await testRealtimeUsesFormat(client, 'json'); }); }); @@ -307,7 +336,9 @@ describe('browser/modules', function () { describe('with MsgPack', () => { describe('BaseRest', () => { it('uses MessagePack', async () => { - const client = new BaseRest(ablyClientOptions({ useBinaryProtocol: true }), { MsgPack }); + const client = new BaseRest(ablyClientOptions({ useBinaryProtocol: true }), { + MsgPack, + }); await testRestUsesContentType(client, 'application/x-msgpack'); }); }); @@ -315,6 +346,7 @@ describe('browser/modules', function () { describe('BaseRealtime', () => { it('uses MessagePack', async () => { const client = new BaseRealtime(ablyClientOptions({ useBinaryProtocol: true, autoConnect: false }), { + WebSocketTransport, MsgPack, }); await testRealtimeUsesFormat(client, 'msgpack'); @@ -327,7 +359,7 @@ describe('browser/modules', function () { describe('RealtimePresence', () => { describe('BaseRealtime without RealtimePresence', () => { it('throws an error when attempting to access the `presence` property', () => { - const client = new BaseRealtime(ablyClientOptions(), {}); + const client = new BaseRealtime(ablyClientOptions(), { WebSocketTransport }); const channel = client.channels.get('channel'); expect(() => channel.presence).to.throw('RealtimePresence module not provided'); @@ -336,9 +368,12 @@ describe('browser/modules', function () { describe('BaseRealtime with RealtimePresence', () => { it('offers realtime presence functionality', async () => { - const rxChannel = new BaseRealtime(ablyClientOptions(), { RealtimePresence }).channels.get('channel'); + const rxChannel = new BaseRealtime(ablyClientOptions(), { WebSocketTransport, RealtimePresence }).channels.get( + 'channel' + ); const txClientId = randomString(); const txChannel = new BaseRealtime(ablyClientOptions({ clientId: txClientId }), { + WebSocketTransport, RealtimePresence, }).channels.get('channel'); @@ -397,4 +432,40 @@ describe('browser/modules', function () { }); }); }); + + describe('Transports', () => { + describe('BaseRealtime', () => { + for (const scenario of [ + { moduleMapKey: 'WebSocketTransport', transportModule: WebSocketTransport, transportName: 'web_socket' }, + { moduleMapKey: 'XHRPolling', transportModule: XHRPolling, transportName: 'xhr_polling' }, + { moduleMapKey: 'XHRStreaming', transportModule: XHRStreaming, transportName: 'xhr_streaming' }, + ]) { + describe(`with the ${scenario.moduleMapKey} module`, () => { + it(`is able to use the ${scenario.transportName} transport`, async () => { + const realtime = new BaseRealtime( + ablyClientOptions({ autoConnect: false, transports: [scenario.transportName] }), + { + [scenario.moduleMapKey]: scenario.transportModule, + } + ); + + let firstTransportCandidate; + const connectionManager = realtime.connection.connectionManager; + const originalTryATransport = connectionManager.tryATransport; + realtime.connection.connectionManager.tryATransport = (transportParams, candidate, callback) => { + if (!firstTransportCandidate) { + firstTransportCandidate = candidate; + } + originalTryATransport.bind(connectionManager)(transportParams, candidate, callback); + }; + + realtime.connect(); + + await realtime.connection.once('connected'); + expect(firstTransportCandidate).to.equal(scenario.transportName); + }); + }); + } + }); + }); }); diff --git a/test/browser/simple.test.js b/test/browser/simple.test.js index bfc22014d7..23e2667e17 100644 --- a/test/browser/simple.test.js +++ b/test/browser/simple.test.js @@ -17,7 +17,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) { }); function isTransportAvailable(transport) { - return transport in Ably.Realtime.ConnectionManager.supportedTransports; + return transport in Ably.Realtime.ConnectionManager.supportedTransports(Ably.Realtime._transports); } function realtimeConnection(transports) { diff --git a/test/common/modules/shared_helper.js b/test/common/modules/shared_helper.js index ecf011b760..465ef5a74f 100644 --- a/test/common/modules/shared_helper.js +++ b/test/common/modules/shared_helper.js @@ -14,7 +14,9 @@ define([ var platform = clientModule.Ably.Realtime.Platform; var BufferUtils = platform.BufferUtils; var expect = chai.expect; - var availableTransports = utils.keysArray(clientModule.Ably.Realtime.ConnectionManager.supportedTransports), + var availableTransports = utils.keysArray( + clientModule.Ably.Realtime.ConnectionManager.supportedTransports(clientModule.Ably.Realtime._transports) + ), bestTransport = availableTransports[0], /* IANA reserved; requests to it will hang forever */ unroutableHost = '10.255.255.1',