Skip to content

Add GMPID Header to Firestore #3888

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 14 commits into from
Mar 30, 2021
5 changes: 5 additions & 0 deletions .changeset/honest-hounds-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@firebase/firestore": patch
---

Added new internal HTTP header to all network requests.
1 change: 1 addition & 0 deletions packages/firestore/exp/src/api/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export class FirebaseFirestore
await this._receivedInitialUser.promise;
const databaseInfo = new DatabaseInfo(
this._databaseId,
this.app.options.appId || '',
this._persistenceKey,
settings.host ?? DEFAULT_HOST,
settings.ssl ?? DEFAULT_SSL,
Expand Down
1 change: 1 addition & 0 deletions packages/firestore/lite/src/api/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export function getDatastore(firestore: FirebaseFirestore): Datastore {
const settings = firestore._getSettings();
const databaseInfo = new DatabaseInfo(
firestore._databaseId,
firestore.app.options.appId || '',
firestore._persistenceKey,
settings.host ?? DEFAULT_HOST,
settings.ssl ?? DEFAULT_SSL,
Expand Down
1 change: 1 addition & 0 deletions packages/firestore/src/api/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ export class Firestore implements PublicFirestore, FirebaseService {
private makeDatabaseInfo(): DatabaseInfo {
return new DatabaseInfo(
this._databaseId,
this._firebaseApp?.options.appId || '',
this._persistenceKey,
this._settings.host,
this._settings.ssl,
Expand Down
2 changes: 2 additions & 0 deletions packages/firestore/src/core/database_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class DatabaseInfo {
* persistenceKey.
*
* @param databaseId The database to use.
* @param appId The Firebase App Id.
* @param persistenceKey A unique identifier for this Firestore's local
* storage (used in conjunction with the databaseId).
* @param host The Firestore backend host to connect to.
Expand All @@ -32,6 +33,7 @@ export class DatabaseInfo {
*/
constructor(
readonly databaseId: DatabaseId,
readonly appId: string,
readonly persistenceKey: string,
readonly host: string,
readonly ssl: boolean,
Expand Down
29 changes: 23 additions & 6 deletions packages/firestore/src/platform/node/grpc_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ import { SDK_VERSION } from '../../core/version';
const LOG_TAG = 'Connection';
const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;

function createMetadata(databasePath: string, token: Token | null): Metadata {
function createMetadata(
databasePath: string,
token: Token | null,
appId: string
): Metadata {
hardAssert(
token === null || token.type === 'OAuth',
'If provided, token must be OAuth'
Expand All @@ -51,10 +55,11 @@ function createMetadata(databasePath: string, token: Token | null): Metadata {
}
}
}
metadata.set('x-goog-api-client', X_GOOG_API_CLIENT_VALUE);
metadata.set('X-Firebase-GMPID', appId);
metadata.set('X-Goog-Api-Client', X_GOOG_API_CLIENT_VALUE);
// This header is used to improve routing and project isolation by the
// backend.
metadata.set('google-cloud-resource-prefix', databasePath);
metadata.set('Google-Cloud-Resource-Prefix', databasePath);
return metadata;
}

Expand Down Expand Up @@ -101,7 +106,11 @@ export class GrpcConnection implements Connection {
token: Token | null
): Promise<Resp> {
const stub = this.ensureActiveStub();
const metadata = createMetadata(this.databasePath, token);
const metadata = createMetadata(
this.databasePath,
token,
this.databaseInfo.appId
);
const jsonRequest = { database: this.databasePath, ...request };

return nodePromise((callback: NodeCallback<Resp>) => {
Expand Down Expand Up @@ -146,7 +155,11 @@ export class GrpcConnection implements Connection {
request
);
const stub = this.ensureActiveStub();
const metadata = createMetadata(this.databasePath, token);
const metadata = createMetadata(
this.databasePath,
token,
this.databaseInfo.appId
);
const jsonRequest = { ...request, database: this.databasePath };
const stream = stub[rpcName](jsonRequest, metadata);
stream.on('data', (response: Resp) => {
Expand All @@ -172,7 +185,11 @@ export class GrpcConnection implements Connection {
token: Token | null
): Stream<Req, Resp> {
const stub = this.ensureActiveStub();
const metadata = createMetadata(this.databasePath, token);
const metadata = createMetadata(
this.databasePath,
token,
this.databaseInfo.appId
);
const grpcStream = stub[rpcName](metadata);

let closed = false;
Expand Down
1 change: 1 addition & 0 deletions packages/firestore/src/remote/rest_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export abstract class RestConnection implements Connection {
token: Token | null
): void {
headers['X-Goog-Api-Client'] = X_GOOG_API_CLIENT_VALUE;
headers['X-Firebase-GMPID'] = this.databaseInfo.appId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: capitalization of the header name is different between this file and grpc_connection. Is this intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal was to match the surrounding code. Let me fix the GRPC headers.


// Content-Type: text/plain will avoid preflight requests which might
// mess with CORS and redirects by proxies. If we add custom headers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export function asyncQueue(db: firestore.FirebaseFirestore): AsyncQueue {
export function getDefaultDatabaseInfo(): DatabaseInfo {
return new DatabaseInfo(
new DatabaseId(DEFAULT_PROJECT_ID),
'test-app-id',
'persistenceKey',
DEFAULT_SETTINGS.host!,
!!DEFAULT_SETTINGS.ssl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const MOCK_SEQUENCE_NUMBER_SYNCER: SequenceNumberSyncer = {
export const TEST_PROJECT = 'test-project';
export const TEST_DATABASE_ID = new DatabaseId(TEST_PROJECT);
export const TEST_PERSISTENCE_KEY = '[PersistenceTestHelpers]';
export const TEST_APP_ID = 'test-app-id';

/** The persistence prefix used for testing in IndexedBD and LocalStorage. */
export const TEST_PERSISTENCE_PREFIX = indexedDbStoragePrefix(
Expand Down
2 changes: 2 additions & 0 deletions packages/firestore/test/unit/remote/rest_connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export class TestRestConnection extends RestConnection {
describe('RestConnection', () => {
const testDatabaseInfo = new DatabaseInfo(
new DatabaseId('testproject'),
'test-app-id',
'persistenceKey',
'example.com',
/*ssl=*/ false,
Expand Down Expand Up @@ -90,6 +91,7 @@ describe('RestConnection', () => {
expect(connection.lastHeaders).to.deep.equal({
'Authorization': 'Bearer owner',
'Content-Type': 'text/plain',
'X-Firebase-GMPID': 'test-app-id',
'X-Goog-Api-Client': `gl-js/ fire/${SDK_VERSION}`
});
});
Expand Down
2 changes: 2 additions & 0 deletions packages/firestore/test/unit/specs/spec_test_runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ import { encodeWatchChange } from '../../util/spec_test_helpers';
import {
clearTestPersistence,
INDEXEDDB_TEST_DATABASE_NAME,
TEST_APP_ID,
TEST_DATABASE_ID,
TEST_PERSISTENCE_KEY,
TEST_SERIALIZER
Expand Down Expand Up @@ -238,6 +239,7 @@ abstract class TestRunner {
this.clientId = `client${clientIndex}`;
this.databaseInfo = new DatabaseInfo(
TEST_DATABASE_ID,
TEST_APP_ID,
TEST_PERSISTENCE_KEY,
'host',
/*ssl=*/ false,
Expand Down