Skip to content

Commit

Permalink
Make HTTP request implementations tree-shakable
Browse files Browse the repository at this point in the history
We expose XHRRequest and FetchRequest modules.

The user is required to provide an HTTP module, even for Realtime, since
it’s used for the internet connectivity check and for making a token
request to the authUrl.

Resolves #1395.
  • Loading branch information
lawrence-forooghian committed Nov 14, 2023
1 parent 9ad0a86 commit 674e88a
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 33 deletions.
2 changes: 2 additions & 0 deletions scripts/moduleReport.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const moduleNames = [
'XHRPolling',
'XHRStreaming',
'WebSocketTransport',
'XHRRequest',
'FetchRequest',
];

// List of all free-standing functions exported by the library along with the
Expand Down
5 changes: 5 additions & 0 deletions src/common/lib/client/baseclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Rest } from './rest';
import { IUntypedCryptoStatic } from 'common/types/ICryptoStatic';
import { throwMissingModuleError } from '../util/utils';
import { MsgPack } from 'common/types/msgpack';
import { HTTPRequestImplementations } from 'platform/web/lib/http/http';

type BatchResult<T> = API.Types.BatchResult<T>;
type BatchPublishSpec = API.Types.BatchPublishSpec;
Expand All @@ -41,8 +42,12 @@ class BaseClient {
private readonly _rest: Rest | null;
readonly _Crypto: IUntypedCryptoStatic | null;
readonly _MsgPack: MsgPack | null;
// Extra HTTP request implementations available to this client, in addition to those in web’s Http.bundledRequestImplementations
readonly _additionalHTTPRequestImplementations: HTTPRequestImplementations;

constructor(options: ClientOptions | string, modules: ModulesMap) {
this._additionalHTTPRequestImplementations = modules;

if (!options) {
const msg = 'no options provided';
Logger.logAction(Logger.LOG_ERROR, 'BaseClient()', msg);
Expand Down
4 changes: 4 additions & 0 deletions src/common/lib/client/modulesmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { IUntypedCryptoStatic } from '../../types/ICryptoStatic';
import { MsgPack } from 'common/types/msgpack';
import RealtimePresence from './realtimepresence';
import { TransportInitialiser } from '../transport/connectionmanager';
import XHRRequest from 'platform/web/lib/http/request/xhrrequest';
import fetchRequest from 'platform/web/lib/http/request/fetchrequest';

export interface ModulesMap {
Rest?: typeof Rest;
Expand All @@ -12,6 +14,8 @@ export interface ModulesMap {
WebSocketTransport?: TransportInitialiser;
XHRPolling?: TransportInitialiser;
XHRStreaming?: TransportInitialiser;
XHRRequest?: typeof XHRRequest;
FetchRequest?: typeof fetchRequest;
}

export const allCommonModules: ModulesMap = { Rest };
3 changes: 3 additions & 0 deletions src/platform/nativescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { getDefaults } from '../../common/lib/util/defaults';
import WebStorage from './lib/util/webstorage';
import PlatformDefaults from '../web/lib/util/defaults';
import msgpack from '../web/lib/util/msgpack';
import { defaultBundledRequestImplementations } from '../web/lib/http/request';

const Crypto = createCryptoClass(Config, BufferUtils);

Expand All @@ -34,6 +35,8 @@ for (const clientClass of [DefaultRest, DefaultRealtime]) {
clientClass._MsgPack = msgpack;
}

Http.bundledRequestImplementations = defaultBundledRequestImplementations;

Logger.initLogHandlers();

Platform.Defaults = getDefaults(PlatformDefaults);
Expand Down
3 changes: 3 additions & 0 deletions src/platform/react-native/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getDefaults } from '../../common/lib/util/defaults';
import WebStorage from '../web/lib/util/webstorage';
import PlatformDefaults from '../web/lib/util/defaults';
import msgpack from '../web/lib/util/msgpack';
import { defaultBundledRequestImplementations } from '../web/lib/http/request';

const Config = configFactory(BufferUtils);

Expand All @@ -34,6 +35,8 @@ for (const clientClass of [DefaultRest, DefaultRealtime]) {
clientClass._MsgPack = msgpack;
}

Http.bundledRequestImplementations = defaultBundledRequestImplementations;

Logger.initLogHandlers();

Platform.Defaults = getDefaults(PlatformDefaults);
Expand Down
3 changes: 3 additions & 0 deletions src/platform/web-noencryption/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { getDefaults } from '../../common/lib/util/defaults';
import WebStorage from '../web/lib/util/webstorage';
import PlatformDefaults from '../web/lib/util/defaults';
import msgpack from '../web/lib/util/msgpack';
import { defaultBundledRequestImplementations } from '../web/lib/http/request';

Platform.Crypto = null;
Platform.BufferUtils = BufferUtils;
Expand All @@ -28,6 +29,8 @@ for (const clientClass of [DefaultRest, DefaultRealtime]) {
clientClass._MsgPack = msgpack;
}

Http.bundledRequestImplementations = defaultBundledRequestImplementations;

Logger.initLogHandlers();

Platform.Defaults = getDefaults(PlatformDefaults);
Expand Down
3 changes: 3 additions & 0 deletions src/platform/web/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getDefaults } from '../../common/lib/util/defaults';
import WebStorage from './lib/util/webstorage';
import PlatformDefaults from './lib/util/defaults';
import msgpack from './lib/util/msgpack';
import { defaultBundledRequestImplementations } from './lib/http/request';

const Crypto = createCryptoClass(Config, BufferUtils);

Expand All @@ -32,6 +33,8 @@ for (const clientClass of [DefaultRest, DefaultRealtime]) {
clientClass._MsgPack = msgpack;
}

Http.bundledRequestImplementations = defaultBundledRequestImplementations;

Logger.initLogHandlers();

Platform.Defaults = getDefaults(PlatformDefaults);
Expand Down
45 changes: 36 additions & 9 deletions src/platform/web/lib/http/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import Platform from 'common/platform';
import * as Utils from 'common/lib/util/utils';
import Defaults from 'common/lib/util/defaults';
import ErrorInfo, { PartialErrorInfo } from 'common/lib/types/errorinfo';
import { IHttpStatic, RequestCallback, RequestParams } from 'common/types/http';
import { RequestCallback, RequestParams } from 'common/types/http';
import HttpMethods from 'common/constants/HttpMethods';
import BaseClient from 'common/lib/client/baseclient';
import BaseRealtime from 'common/lib/client/baserealtime';
import XHRRequest from './request/xhrrequest';
import XHRStates from 'common/constants/XHRStates';
import Logger from 'common/lib/util/logger';
import { StandardCallback } from 'common/types/utils';
import fetchRequest from './request/fetchrequest';
import { isSuccessCode } from 'common/constants/HttpStatusCodes';
import { ModulesMap } from 'common/lib/client/modulesmap';

export type HTTPRequestImplementations = Pick<ModulesMap, 'XHRRequest' | 'FetchRequest'>;

function shouldFallback(errorInfo: ErrorInfo) {
const statusCode = errorInfo.statusCode as number;
Expand Down Expand Up @@ -39,10 +40,20 @@ function getHosts(client: BaseClient): string[] {
return Defaults.getHosts(client.options);
}

const Http: IHttpStatic = class {
function createMissingImplementationError() {
return new ErrorInfo(
'No HTTP request module provided. Provide at least one of the FetchRequest or XHRRequest modules.',
400,
40000
);
}

const Http = class {
static methods = [HttpMethods.Get, HttpMethods.Delete, HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch];
static methodsWithoutBody = [HttpMethods.Get, HttpMethods.Delete];
static methodsWithBody = [HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch];
// HTTP request implementations that are available even without a BaseClient object (needed by some tests which directly instantiate `Http` without a client)
static bundledRequestImplementations: HTTPRequestImplementations;
checksInProgress: Array<StandardCallback<boolean>> | null = null;
private client: BaseClient | null;

Expand All @@ -51,7 +62,20 @@ const Http: IHttpStatic = class {
const connectivityCheckUrl = client?.options.connectivityCheckUrl || Defaults.connectivityCheckUrl;
const connectivityCheckParams = client?.options.connectivityCheckParams ?? null;
const connectivityUrlIsDefault = !client?.options.connectivityCheckUrl;
if (Platform.Config.xhrSupported) {

const requestImplementations = {
...Http.bundledRequestImplementations,
...client?._additionalHTTPRequestImplementations,
};
const xhrRequestImplementation = requestImplementations.XHRRequest;
const fetchRequestImplementation = requestImplementations.FetchRequest;
const hasImplementation = !!(xhrRequestImplementation || fetchRequestImplementation);

if (!hasImplementation) {
throw createMissingImplementationError();
}

if (Platform.Config.xhrSupported && xhrRequestImplementation) {
this.supportsAuthHeaders = true;
this.Request = function (
method: HttpMethods,
Expand All @@ -61,7 +85,7 @@ const Http: IHttpStatic = class {
body: unknown,
callback: RequestCallback
) {
const req = XHRRequest.createRequest(
const req = xhrRequestImplementation.createRequest(
uri,
headers,
params,
Expand Down Expand Up @@ -104,10 +128,10 @@ const Http: IHttpStatic = class {
);
};
}
} else if (Platform.Config.fetchSupported) {
} else if (Platform.Config.fetchSupported && fetchRequestImplementation) {
this.supportsAuthHeaders = true;
this.Request = (method, uri, headers, params, body, callback) => {
fetchRequest(method, client ?? null, uri, headers, params, body, callback);
fetchRequestImplementation(method, client ?? null, uri, headers, params, body, callback);
};
this.checkConnectivity = function (callback: (err: ErrorInfo | null, connectivity: boolean) => void) {
Logger.logAction(Logger.LOG_MICRO, '(Fetch)Http.checkConnectivity()', 'Sending; ' + connectivityCheckUrl);
Expand All @@ -119,7 +143,10 @@ const Http: IHttpStatic = class {
};
} else {
this.Request = (method, uri, headers, params, body, callback) => {
callback(new PartialErrorInfo('no supported HTTP transports available', null, 400), null);
const error = hasImplementation
? new PartialErrorInfo('no supported HTTP transports available', null, 400)
: createMissingImplementationError();
callback(error, null);
};
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/platform/web/lib/http/request/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { HTTPRequestImplementations } from '../http';
import XHRRequest from './xhrrequest';
import fetchRequest from './fetchrequest';

export const defaultBundledRequestImplementations: HTTPRequestImplementations = {
XHRRequest: XHRRequest,
FetchRequest: fetchRequest,
};

export const modulesBundledRequestImplementations: HTTPRequestImplementations = {};
4 changes: 4 additions & 0 deletions src/platform/web/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import Logger from '../../common/lib/util/logger';
import { getDefaults } from '../../common/lib/util/defaults';
import WebStorage from './lib/util/webstorage';
import PlatformDefaults from './lib/util/defaults';
import { modulesBundledRequestImplementations } from './lib/http/request';

Platform.BufferUtils = BufferUtils;
Platform.Http = Http;
Platform.Config = Config;
Platform.Transports = ModulesTransports;
Platform.WebStorage = WebStorage;

Http.bundledRequestImplementations = modulesBundledRequestImplementations;

Logger.initLogHandlers();

Platform.Defaults = getDefaults(PlatformDefaults);
Expand All @@ -45,5 +48,6 @@ export * from './modules/presencemessage';
export * from './modules/msgpack';
export * from './modules/realtimepresence';
export * from './modules/transports';
export * from './modules/http';
export { Rest } from '../../common/lib/client/rest';
export { BaseRest, BaseRealtime, ErrorInfo };
2 changes: 2 additions & 0 deletions src/platform/web/modules/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as XHRRequest } from '../lib/http/request/xhrrequest';
export { default as FetchRequest } from '../lib/http/request/fetchrequest';
Loading

0 comments on commit 674e88a

Please sign in to comment.