Skip to content

Commit 9c6808f

Browse files
authored
Add forceWebSockets() and forceLongPolling() (#6171)
* Implemented forceWebSockets() and forceLongPolling()
1 parent c873641 commit 9c6808f

File tree

8 files changed

+138
-3
lines changed

8 files changed

+138
-3
lines changed

.changeset/lucky-items-wave.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@firebase/database-compat": minor
3+
"@firebase/database": minor
4+
---
5+
6+
Add `forceWebSockets()` and `forceLongPolling()`

common/api-review/database.api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ export function equalTo(value: number | string | boolean | null, key?: string):
6464
// @public
6565
export type EventType = 'value' | 'child_added' | 'child_changed' | 'child_moved' | 'child_removed';
6666

67+
// @public
68+
export function forceLongPolling(): void;
69+
70+
// @public
71+
export function forceWebSockets(): void;
72+
6773
// @public
6874
export function get(query: Query): Promise<DataSnapshot>;
6975

packages/database-compat/src/api/Database.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import { FirebaseApp } from '@firebase/app-types';
2020
import { FirebaseService } from '@firebase/app-types/private';
2121
import {
22+
forceLongPolling,
23+
forceWebSockets,
2224
goOnline,
2325
connectDatabaseEmulator,
2426
goOffline,
@@ -51,7 +53,9 @@ export class Database implements FirebaseService, Compat<ModularDatabase> {
5153
constructor(readonly _delegate: ModularDatabase, readonly app: FirebaseApp) {}
5254

5355
INTERNAL = {
54-
delete: () => this._delegate._delete()
56+
delete: () => this._delegate._delete(),
57+
forceWebSockets,
58+
forceLongPolling
5559
};
5660

5761
/**

packages/database/src/api.standalone.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export {
2222
enableLogging,
2323
goOffline,
2424
goOnline,
25+
forceWebSockets,
26+
forceLongPolling,
2527
connectDatabaseEmulator
2628
} from './api/Database';
2729
export {

packages/database/src/api/Database.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ import { RepoInfo } from '../core/RepoInfo';
4141
import { parseRepoInfo } from '../core/util/libs/parser';
4242
import { newEmptyPath, pathIsEmpty } from '../core/util/Path';
4343
import {
44+
warn,
4445
fatal,
4546
log,
4647
enableLogging as enableLoggingImpl
4748
} from '../core/util/util';
4849
import { validateUrl } from '../core/util/validation';
50+
import { BrowserPollConnection } from '../realtime/BrowserPollConnection';
51+
import { TransportManager } from '../realtime/TransportManager';
52+
import { WebSocketConnection } from '../realtime/WebSocketConnection';
4953

5054
import { ReferenceImpl } from './Reference_impl';
5155

@@ -271,6 +275,31 @@ export class Database implements _FirebaseService {
271275
}
272276
}
273277

278+
function checkTransportInit() {
279+
if (TransportManager.IS_TRANSPORT_INITIALIZED) {
280+
warn(
281+
'Transport has already been initialized. Please call this function before calling ref or setting up a listener'
282+
);
283+
}
284+
}
285+
286+
/**
287+
* Force the use of websockets instead of longPolling.
288+
*/
289+
export function forceWebSockets() {
290+
checkTransportInit();
291+
BrowserPollConnection.forceDisallow();
292+
}
293+
294+
/**
295+
* Force the use of longPolling instead of websockets. This will be ignored if websocket protocol is used in databaseURL.
296+
*/
297+
export function forceLongPolling() {
298+
checkTransportInit();
299+
WebSocketConnection.forceDisallow();
300+
BrowserPollConnection.forceAllow();
301+
}
302+
274303
/**
275304
* Returns the instance of the Realtime Database SDK that is associated
276305
* with the provided {@link @firebase/app#FirebaseApp}. Initializes a new instance with

packages/database/src/realtime/BrowserPollConnection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ export class BrowserPollConnection implements Transport {
248248
this.addDisconnectPingFrame(this.id, this.password);
249249
}
250250

251-
private static forceAllow_: boolean;
251+
static forceAllow_: boolean;
252252

253253
/**
254254
* Forces long polling to be considered as a potential transport
@@ -257,7 +257,7 @@ export class BrowserPollConnection implements Transport {
257257
BrowserPollConnection.forceAllow_ = true;
258258
}
259259

260-
private static forceDisallow_: boolean;
260+
static forceDisallow_: boolean;
261261

262262
/**
263263
* Forces longpolling to not be considered as a potential transport

packages/database/src/realtime/TransportManager.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,21 @@ import { WebSocketConnection } from './WebSocketConnection';
3232
export class TransportManager {
3333
private transports_: TransportConstructor[];
3434

35+
// Keeps track of whether the TransportManager has already chosen a transport to use
36+
static globalTransportInitialized_ = false;
37+
3538
static get ALL_TRANSPORTS() {
3639
return [BrowserPollConnection, WebSocketConnection];
3740
}
3841

42+
/**
43+
* Returns whether transport has been selected to ensure WebSocketConnection or BrowserPollConnection are not called after
44+
* TransportManager has already set up transports_
45+
*/
46+
static get IS_TRANSPORT_INITIALIZED() {
47+
return this.globalTransportInitialized_;
48+
}
49+
3950
/**
4051
* @param repoInfo - Metadata around the namespace we're connecting to
4152
*/
@@ -68,6 +79,7 @@ export class TransportManager {
6879
transports.push(transport);
6980
}
7081
}
82+
TransportManager.globalTransportInitialized_ = true;
7183
}
7284
}
7385

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @license
3+
* Copyright 2022 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { CONSTANTS } from '@firebase/util';
19+
import { expect, use } from 'chai';
20+
import { createSandbox, SinonSandbox, SinonSpy } from 'sinon';
21+
import sinonChai from 'sinon-chai';
22+
23+
import { forceLongPolling, forceWebSockets } from '../src';
24+
import * as Util from '../src/core/util/util';
25+
import { BrowserPollConnection } from '../src/realtime/BrowserPollConnection';
26+
import { TransportManager } from '../src/realtime/TransportManager';
27+
import { WebSocketConnection } from '../src/realtime/WebSocketConnection';
28+
29+
use(sinonChai);
30+
const transportInitError =
31+
'Transport has already been initialized. Please call this function before calling ref or setting up a listener';
32+
describe('Force Transport', () => {
33+
const oldNodeValue = CONSTANTS.NODE_CLIENT;
34+
let mySandbox: SinonSandbox;
35+
let spyWarn: SinonSpy;
36+
beforeEach(() => {
37+
CONSTANTS.NODE_CLIENT = false;
38+
mySandbox = createSandbox();
39+
spyWarn = mySandbox.spy(Util, 'warn');
40+
});
41+
afterEach(() => {
42+
// Resetting to old values
43+
TransportManager.globalTransportInitialized_ = false;
44+
CONSTANTS.NODE_CLIENT = oldNodeValue;
45+
BrowserPollConnection.forceAllow_ = false;
46+
BrowserPollConnection.forceDisallow_ = true;
47+
WebSocketConnection.forceDisallow_ = false;
48+
mySandbox.restore();
49+
});
50+
it('should enable websockets and disable longPolling', () => {
51+
forceWebSockets();
52+
expect(spyWarn.called).to.equal(false);
53+
expect(WebSocketConnection.isAvailable()).to.equal(true);
54+
expect(BrowserPollConnection.isAvailable()).to.equal(false);
55+
});
56+
it('should throw an error when calling forceWebsockets() if TransportManager has already been initialized', () => {
57+
TransportManager.globalTransportInitialized_ = true;
58+
forceWebSockets();
59+
expect(spyWarn).to.have.been.calledWith(transportInitError);
60+
expect(WebSocketConnection.isAvailable()).to.equal(true);
61+
expect(BrowserPollConnection.isAvailable()).to.equal(false);
62+
});
63+
it('should enable longPolling and disable websockets', () => {
64+
forceLongPolling();
65+
expect(spyWarn.called).to.equal(false);
66+
expect(WebSocketConnection.isAvailable()).to.equal(false);
67+
expect(BrowserPollConnection.isAvailable()).to.equal(true);
68+
});
69+
it('should throw an error when calling forceLongPolling() if TransportManager has already been initialized', () => {
70+
TransportManager.globalTransportInitialized_ = true;
71+
forceLongPolling();
72+
expect(spyWarn).to.have.been.calledWith(transportInitError);
73+
expect(WebSocketConnection.isAvailable()).to.equal(false);
74+
expect(BrowserPollConnection.isAvailable()).to.equal(true);
75+
});
76+
});

0 commit comments

Comments
 (0)