Skip to content

Stop using WebChannelConnection in Lite SDK #3482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config/webpack.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ module.exports = {
new webpack.NormalModuleReplacementPlugin(
FIRESTORE_PLATFORM_RE,
resource => {
const targetPlatform = process.env.TEST_PLATFORM || 'browser';
resource.request = resource.request.replace(
FIRESTORE_PLATFORM_RE,
'$1/platform/browser/$2.ts'
`$1/platform/${targetPlatform}/$2.ts`
);
}
),
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/firestore/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function getTestFiles(argv) {
} else if (argv.integration) {
return [legcayIntegrationTests];
} else if (argv.lite) {
process.env.TEST_PLATFORM = 'browser_lite';
return [liteIntegrationTests];
} else if (argv.exp) {
return [expIntegrationTests];
Expand Down
3 changes: 2 additions & 1 deletion packages/firestore/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"gendeps:exp": "../../scripts/exp/extract-deps.sh --types ./exp-types/index.d.ts --bundle ./dist/exp/tmp.js --output ./exp/dependencies.json",
"pregendeps:lite": "node scripts/build-bundle.js --input ./lite/index.ts --output ./dist/lite/tmp.js",
"gendeps:lite": "../../scripts/exp/extract-deps.sh --types ./lite-types/index.d.ts --bundle ./dist/lite/tmp.js --output ./lite/dependencies.json",
"test:lite": "node ./scripts/run-tests.js --emulator --main=lite/index.ts 'lite/test/**/*.test.ts'",
"test:lite": "node ./scripts/run-tests.js --emulator --platform node_lite --main=lite/index.ts 'lite/test/**/*.test.ts'",
"test:lite:browser": "karma start --single-run --lite",
"test:lite:browser:debug": "karma start --single-run --lite --auto-watch",
"test:exp": "node ./scripts/run-tests.js --emulator --main=exp/index.ts test/integration/api/*.test.ts",
Expand Down Expand Up @@ -63,6 +63,7 @@
"@firebase/webchannel-wrapper": "0.2.41",
"@grpc/grpc-js": "^1.0.0",
"@grpc/proto-loader": "^0.5.0",
"node-fetch": "2.6.0",
"tslib": "^1.11.1"
},
"peerDependencies": {
Expand Down
9 changes: 6 additions & 3 deletions packages/firestore/rollup.config.lite.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const allBuilds = [
format: 'es',
sourcemap: true
},
plugins: [alias(util.generateAliasConfig('node')), ...nodePlugins],
plugins: [alias(util.generateAliasConfig('node_lite')), ...nodePlugins],
external: util.resolveNodeExterns,
treeshake: {
moduleSideEffects: false
Expand Down Expand Up @@ -111,7 +111,10 @@ const allBuilds = [
format: 'es',
sourcemap: true
},
plugins: [alias(util.generateAliasConfig('browser')), ...browserPlugins],
plugins: [
alias(util.generateAliasConfig('browser_lite')),
...browserPlugins
],
external: util.resolveBrowserExterns,
treeshake: {
moduleSideEffects: false
Expand All @@ -125,7 +128,7 @@ const allBuilds = [
format: 'es',
sourcemap: true
},
plugins: [alias(util.generateAliasConfig('rn')), ...browserPlugins],
plugins: [alias(util.generateAliasConfig('rn_lite')), ...browserPlugins],
external: util.resolveBrowserExterns,
treeshake: {
moduleSideEffects: false
Expand Down
2 changes: 1 addition & 1 deletion packages/firestore/scripts/run-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/exports.__esModule=true;var yargs=require("yargs");var path_1=require("path");var child_process_promise_1=require("child-process-promise");var argv=yargs.options({main:{type:"string",demandOption:true},emulator:{type:"boolean"},persistence:{type:"boolean"}}).argv;var nyc=path_1.resolve(__dirname,"../../../node_modules/.bin/nyc");var mocha=path_1.resolve(__dirname,"../../../node_modules/.bin/mocha");process.env.TS_NODE_CACHE="NO";process.env.TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}';var args=[mocha,"--require","ts-node/register","--require",argv.main,"--config","../../config/mocharc.node.js"];if(argv.emulator){process.env.FIRESTORE_EMULATOR_PORT="8080";process.env.FIRESTORE_EMULATOR_PROJECT_ID="test-emulator"}if(argv.persistence){process.env.USE_MOCK_PERSISTENCE="YES";args.push("--require","test/util/node_persistence.ts")}args=args.concat(argv._);var childProcess=child_process_promise_1.spawn(nyc,args,{stdio:"inherit",cwd:process.cwd()}).childProcess;process.once("exit",(function(){return childProcess.kill()}));process.once("SIGINT",(function(){return childProcess.kill("SIGINT")}));process.once("SIGTERM",(function(){return childProcess.kill("SIGTERM")}));
*/exports.__esModule=true;var yargs=require("yargs");var path_1=require("path");var child_process_promise_1=require("child-process-promise");var argv=yargs.options({main:{type:"string",demandOption:true},platform:{type:"string",default:"node"},emulator:{type:"boolean"},persistence:{type:"boolean"}}).argv;var nyc=path_1.resolve(__dirname,"../../../node_modules/.bin/nyc");var mocha=path_1.resolve(__dirname,"../../../node_modules/.bin/mocha");process.env.TS_NODE_CACHE="NO";process.env.TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}';process.env.TEST_PLATFORM=argv.platform;var args=[mocha,"--require","ts-node/register","--require",argv.main,"--config","../../config/mocharc.node.js"];if(argv.emulator){process.env.FIRESTORE_EMULATOR_PORT="8080";process.env.FIRESTORE_EMULATOR_PROJECT_ID="test-emulator"}if(argv.persistence){process.env.USE_MOCK_PERSISTENCE="YES";args.push("--require","test/util/node_persistence.ts")}args=args.concat(argv._);var childProcess=child_process_promise_1.spawn(nyc,args,{stdio:"inherit",cwd:process.cwd()}).childProcess;process.once("exit",(function(){return childProcess.kill()}));process.once("SIGINT",(function(){return childProcess.kill("SIGINT")}));process.once("SIGTERM",(function(){return childProcess.kill("SIGTERM")}));
5 changes: 5 additions & 0 deletions packages/firestore/scripts/run-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ const argv = yargs.options({
type: 'string',
demandOption: true
},
platform: {
type: 'string',
default: 'node'
},
emulator: {
type: 'boolean'
},
Expand All @@ -37,6 +41,7 @@ const mocha = resolve(__dirname, '../../../node_modules/.bin/mocha');

process.env.TS_NODE_CACHE = 'NO';
process.env.TS_NODE_COMPILER_OPTIONS = '{"module":"commonjs"}';
process.env.TEST_PLATFORM = argv.platform;

let args = [
mocha,
Expand Down
32 changes: 6 additions & 26 deletions packages/firestore/src/platform/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,21 @@
* limitations under the License.
*/

import { isNode, isReactNative } from '@firebase/util';

import * as node from './node/base64';
import * as rn from './rn/base64';
import * as browser from './browser/base64';
// This file is only used under ts-node.
// eslint-disable-next-line @typescript-eslint/no-require-imports
const platform = require(`./${process.env.TEST_PLATFORM ?? 'node'}/base64`);

/** Converts a Base64 encoded string to a binary string. */
export function decodeBase64(encoded: string): string {
if (isNode()) {
return node.decodeBase64(encoded);
} else if (isReactNative()) {
return rn.decodeBase64(encoded);
} else {
return browser.decodeBase64(encoded);
}
return platform.decodeBase64(encoded);
}

/** Converts a binary string to a Base64 encoded string. */
export function encodeBase64(raw: string): string {
if (isNode()) {
return node.encodeBase64(raw);
} else if (isReactNative()) {
return rn.encodeBase64(raw);
} else {
return browser.encodeBase64(raw);
}
return platform.encodeBase64(raw);
}

/** True if and only if the Base64 conversion functions are available. */
export function isBase64Available(): boolean {
if (isNode()) {
return node.isBase64Available();
} else if (isReactNative()) {
return rn.isBase64Available();
} else {
return browser.isBase64Available();
}
return platform.isBase64Available();
}
110 changes: 12 additions & 98 deletions packages/firestore/src/platform/browser/webchannel_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,79 +35,40 @@ import {
} from '@firebase/util';

import { Token } from '../../api/credentials';
import { DatabaseId, DatabaseInfo } from '../../core/database_info';
import { SDK_VERSION } from '../../core/version';
import { Connection, Stream } from '../../remote/connection';
import { DatabaseInfo } from '../../core/database_info';
import { Stream } from '../../remote/connection';
import {
mapCodeFromRpcStatus,
mapCodeFromHttpResponseErrorStatus
} from '../../remote/rpc_error';
import { StreamBridge } from '../../remote/stream_bridge';
import { debugAssert, fail, hardAssert } from '../../util/assert';
import { fail, hardAssert } from '../../util/assert';
import { Code, FirestoreError } from '../../util/error';
import { logDebug, logWarn } from '../../util/log';
import { Indexable } from '../../util/misc';
import { Rejecter, Resolver } from '../../util/promise';
import { StringMap } from '../../util/types';
import { RestConnection } from '../../remote/rest_connection';

const LOG_TAG = 'Connection';

const RPC_STREAM_SERVICE = 'google.firestore.v1.Firestore';
const RPC_URL_VERSION = 'v1';

/**
* Maps RPC names to the corresponding REST endpoint name.
* Uses Object Literal notation to avoid renaming.
*/
const RPC_NAME_REST_MAPPING: { [key: string]: string } = {};
RPC_NAME_REST_MAPPING['BatchGetDocuments'] = 'batchGet';
RPC_NAME_REST_MAPPING['Commit'] = 'commit';
RPC_NAME_REST_MAPPING['RunQuery'] = 'runQuery';

// TODO(b/38203344): The SDK_VERSION is set independently from Firebase because
// we are doing out-of-band releases. Once we release as part of Firebase, we
// should use the Firebase version instead.
const X_GOOG_API_CLIENT_VALUE = 'gl-js/ fire/' + SDK_VERSION;

const XHR_TIMEOUT_SECS = 15;

export class WebChannelConnection implements Connection {
private readonly databaseId: DatabaseId;
private readonly baseUrl: string;
export class WebChannelConnection extends RestConnection {
private readonly forceLongPolling: boolean;

constructor(info: DatabaseInfo) {
this.databaseId = info.databaseId;
const proto = info.ssl ? 'https' : 'http';
this.baseUrl = proto + '://' + info.host;
super(info);
this.forceLongPolling = info.forceLongPolling;
}

/**
* Modifies the headers for a request, adding any authorization token if
* present and any additional headers for the request.
*/
private modifyHeadersForRequest(
headers: StringMap,
token: Token | null
): void {
if (token) {
for (const header in token.authHeaders) {
if (token.authHeaders.hasOwnProperty(header)) {
headers[header] = token.authHeaders[header];
}
}
}
headers['X-Goog-Api-Client'] = X_GOOG_API_CLIENT_VALUE;
}

invokeRPC<Req, Resp>(
protected performRPCRequest<Req, Resp>(
rpcName: string,
request: Req,
token: Token | null
url: string,
headers: StringMap,
body: Req
): Promise<Resp> {
const url = this.makeUrl(rpcName);

return new Promise((resolve: Resolver<Resp>, reject: Rejecter) => {
const xhr = new XhrIo();
xhr.listenOnce(EventType.COMPLETE, () => {
Expand Down Expand Up @@ -161,7 +122,6 @@ export class WebChannelConnection implements Connection {
} else {
// If we received an HTTP_ERROR but there's no status code,
// it's most probably a connection issue
logDebug(LOG_TAG, 'RPC "' + rpcName + '" failed');
reject(
new FirestoreError(Code.UNAVAILABLE, 'Connection failed.')
);
Expand All @@ -184,37 +144,11 @@ export class WebChannelConnection implements Connection {
}
});

// The database field is already encoded in URL. Specifying it again in
// the body is not necessary in production, and will cause duplicate field
// errors in the Firestore Emulator. Let's remove it.
const jsonObj = ({ ...request } as unknown) as Indexable;
delete jsonObj.database;

const requestString = JSON.stringify(jsonObj);
logDebug(LOG_TAG, 'XHR sending: ', url + ' ' + requestString);
// Content-Type: text/plain will avoid preflight requests which might
// mess with CORS and redirects by proxies. If we add custom headers
// we will need to change this code to potentially use the
// $httpOverwrite parameter supported by ESF to avoid
// triggering preflight requests.
const headers: StringMap = { 'Content-Type': 'text/plain' };

this.modifyHeadersForRequest(headers, token);

const requestString = JSON.stringify(body);
xhr.send(url, 'POST', requestString, headers, XHR_TIMEOUT_SECS);
});
}

invokeStreamingRPC<Req, Resp>(
rpcName: string,
request: Req,
token: Token | null
): Promise<Resp[]> {
// The REST API automatically aggregates all of the streamed results, so we
// can just use the normal invoke() method.
return this.invokeRPC<Req, Resp[]>(rpcName, request, token);
}

openStream<Req, Resp>(
rpcName: string,
token: Token | null
Expand Down Expand Up @@ -283,7 +217,7 @@ export class WebChannelConnection implements Connection {
}

const url = urlParts.join('');
logDebug(LOG_TAG, 'Creating WebChannel: ' + url + ' ' + request);
logDebug(LOG_TAG, 'Creating WebChannel: ' + url, request);
const channel = webchannelTransport.createWebChannel(url, request);

// WebChannel supports sending the first message with the handshake - saving
Expand Down Expand Up @@ -420,24 +354,4 @@ export class WebChannelConnection implements Connection {
}, 0);
return streamBridge;
}

// visible for testing
makeUrl(rpcName: string): string {
const urlRpcName = RPC_NAME_REST_MAPPING[rpcName];
debugAssert(
urlRpcName !== undefined,
'Unknown REST mapping for: ' + rpcName
);
return (
this.baseUrl +
'/' +
RPC_URL_VERSION +
'/projects/' +
this.databaseId.projectId +
'/databases/' +
this.databaseId.database +
'/documents:' +
urlRpcName
);
}
}
18 changes: 18 additions & 0 deletions packages/firestore/src/platform/browser_lite/base64.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from '../browser/base64';
27 changes: 27 additions & 0 deletions packages/firestore/src/platform/browser_lite/connection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { DatabaseInfo } from '../../core/database_info';
import { Connection } from '../../remote/connection';
import { FetchConnection } from './fetch_connection';

export { newConnectivityMonitor } from '../browser/connection';

/** Initializes the HTTP connection for the REST API. */
export function newConnection(databaseInfo: DatabaseInfo): Promise<Connection> {
return Promise.resolve(new FetchConnection(databaseInfo, fetch.bind(null)));
}
18 changes: 18 additions & 0 deletions packages/firestore/src/platform/browser_lite/dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from '../browser/dom';
Loading