Skip to content

Commit af95ce2

Browse files
authored
Merge e6f8263 into dd6a8f0
2 parents dd6a8f0 + e6f8263 commit af95ce2

File tree

14 files changed

+119
-7
lines changed

14 files changed

+119
-7
lines changed

.changeset/nice-plants-thank.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@firebase/database-compat": patch
3+
"@firebase/database": patch
4+
"@firebase/firestore": patch
5+
"@firebase/functions": patch
6+
"@firebase/storage": patch
7+
"@firebase/util": patch
8+
---
9+
10+
Auto Enable SSL for Firebase Studio

common/api-review/util.api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ export function isBrowserExtension(): boolean;
269269
// @public
270270
export function isCloudflareWorker(): boolean;
271271

272+
// @public
273+
export function isCloudWorkstation(host: string): boolean;
274+
272275
// Warning: (ae-missing-release-tag) "isElectron" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
273276
//
274277
// @public

packages/database-compat/test/database.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,17 @@ describe('Database Tests', () => {
292292
expect((db as any)._delegate._repo.repoInfo_.isUsingEmulator).to.be.false;
293293
});
294294

295+
it('uses ssl when useEmulator is called with ssl specified', () => {
296+
const db = firebase.database();
297+
const cloudWorkstation = 'abc.cloudworkstations.dev';
298+
db.useEmulator(cloudWorkstation, 80);
299+
expect((db as any)._delegate._repo.repoInfo_.isUsingEmulator).to.be.true;
300+
expect((db as any)._delegate._repo.repoInfo_.host).to.equal(
301+
`${cloudWorkstation}:80`
302+
);
303+
expect((db as any)._delegate._repo.repoInfo_.secure).to.be.true;
304+
});
305+
295306
it('cannot call useEmulator after use', () => {
296307
const db = (firebase as any).database();
297308

packages/database/src/api/Database.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ import {
2929
createMockUserToken,
3030
deepEqual,
3131
EmulatorMockTokenOptions,
32-
getDefaultEmulatorHostnameAndPort
32+
getDefaultEmulatorHostnameAndPort,
33+
isCloudWorkstation
3334
} from '@firebase/util';
3435

3536
import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider';
@@ -89,9 +90,12 @@ function repoManagerApplyEmulatorSettings(
8990
emulatorOptions: RepoInfoEmulatorOptions,
9091
tokenProvider?: AuthTokenProvider
9192
): void {
93+
const portIndex = hostAndPort.lastIndexOf(':');
94+
const host = hostAndPort.substring(0, portIndex);
95+
const useSsl = isCloudWorkstation(host);
9296
repo.repoInfo_ = new RepoInfo(
9397
hostAndPort,
94-
/* secure= */ false,
98+
/* secure= */ useSsl,
9599
repo.repoInfo_.namespace,
96100
repo.repoInfo_.webSocketOnly,
97101
repo.repoInfo_.nodeAdmin,
@@ -352,6 +356,7 @@ export function connectDatabaseEmulator(
352356
): void {
353357
db = getModularInstance(db);
354358
db._checkNotDeleted('useEmulator');
359+
355360
const hostAndPort = `${host}:${port}`;
356361
const repo = db._repoInternal;
357362
if (db._instanceStarted) {

packages/firestore/externs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"packages/util/dist/src/compat.d.ts",
3434
"packages/util/dist/src/global.d.ts",
3535
"packages/util/dist/src/obj.d.ts",
36+
"packages/util/dist/src/url.d.ts",
3637
"packages/firestore/src/protos/firestore_bundle_proto.ts",
3738
"packages/firestore/src/protos/firestore_proto_api.ts",
3839
"packages/firestore/src/util/error.ts",

packages/firestore/src/lite-api/database.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import {
2626
createMockUserToken,
2727
deepEqual,
2828
EmulatorMockTokenOptions,
29-
getDefaultEmulatorHostnameAndPort
29+
getDefaultEmulatorHostnameAndPort,
30+
isCloudWorkstation
3031
} from '@firebase/util';
3132

3233
import {
@@ -325,6 +326,7 @@ export function connectFirestoreEmulator(
325326
} = {}
326327
): void {
327328
firestore = cast(firestore, Firestore);
329+
const useSsl = isCloudWorkstation(host);
328330
const settings = firestore._getSettings();
329331
const existingConfig = {
330332
...settings,
@@ -340,7 +342,7 @@ export function connectFirestoreEmulator(
340342
const newConfig = {
341343
...settings,
342344
host: newHostSetting,
343-
ssl: false,
345+
ssl: useSsl,
344346
emulatorOptions: options
345347
};
346348
// No-op if the new configuration matches the current configuration. This supports SSR

packages/firestore/test/unit/api/database.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,20 @@ describe('Settings', () => {
564564
expect(db._getEmulatorOptions()).to.equal(emulatorOptions);
565565
});
566566

567+
it('sets ssl to true if cloud workstation host', () => {
568+
// Use a new instance of Firestore in order to configure settings.
569+
const db = newTestFirestore();
570+
const emulatorOptions = { mockUserToken: 'test' };
571+
const workstationHost = 'abc.cloudworkstations.dev';
572+
connectFirestoreEmulator(db, workstationHost, 9000, emulatorOptions);
573+
574+
expect(db._getSettings().host).to.exist.and.to.equal(
575+
`${workstationHost}:9000`
576+
);
577+
expect(db._getSettings().ssl).to.exist.and.to.be.true;
578+
expect(db._getEmulatorOptions()).to.equal(emulatorOptions);
579+
});
580+
567581
it('prefers host from useEmulator to host from settings', () => {
568582
// Use a new instance of Firestore in order to configure settings.
569583
const db = newTestFirestore();

packages/functions/src/service.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ describe('Firebase Functions > Service', () => {
4747
'http://localhost:5005/my-project/us-central1/foo'
4848
);
4949
});
50+
it('can use emulator with SSL', () => {
51+
service = createTestService(app);
52+
const workstationHost = 'abc.cloudworkstations.dev';
53+
connectFunctionsEmulator(service, workstationHost, 5005);
54+
assert.equal(
55+
service._url('foo'),
56+
`https://${workstationHost}:5005/my-project/us-central1/foo`
57+
);
58+
});
5059

5160
it('correctly sets region', () => {
5261
service = createTestService(app, 'my-region');

packages/functions/src/service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { Provider } from '@firebase/component';
3030
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
3131
import { MessagingInternalComponentName } from '@firebase/messaging-interop-types';
3232
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
33+
import { isCloudWorkstation } from '@firebase/util';
3334

3435
export const DEFAULT_REGION = 'us-central1';
3536

@@ -174,7 +175,10 @@ export function connectFunctionsEmulator(
174175
host: string,
175176
port: number
176177
): void {
177-
functionsInstance.emulatorOrigin = `http://${host}:${port}`;
178+
const useSsl = isCloudWorkstation(host);
179+
functionsInstance.emulatorOrigin = `http${
180+
useSsl ? 's' : ''
181+
}://${host}:${port}`;
178182
}
179183

180184
/**

packages/storage/src/service.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ import {
4242
} from './implementation/error';
4343
import { validateNumber } from './implementation/type';
4444
import { FirebaseStorage } from './public-types';
45-
import { createMockUserToken, EmulatorMockTokenOptions } from '@firebase/util';
45+
import {
46+
createMockUserToken,
47+
EmulatorMockTokenOptions,
48+
isCloudWorkstation
49+
} from '@firebase/util';
4650
import { Connection, ConnectionType } from './implementation/connection';
4751

4852
export function isUrl(path?: string): boolean {
@@ -141,7 +145,8 @@ export function connectStorageEmulator(
141145
} = {}
142146
): void {
143147
storage.host = `${host}:${port}`;
144-
storage._protocol = 'http';
148+
const useSsl = isCloudWorkstation(host);
149+
storage._protocol = useSsl ? 'https' : 'http';
145150
const { mockUserToken } = options;
146151
if (mockUserToken) {
147152
storage._overrideAuthToken =

packages/storage/test/unit/service.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,28 @@ GOOG4-RSA-SHA256`
248248
expect(service._protocol).to.equal('http');
249249
void getDownloadURL(ref(service, 'test.png'));
250250
});
251+
it('sets emulator host correctly with ssl', done => {
252+
function newSend(connection: TestingConnection, url: string): void {
253+
// Expect emulator host to be in url of storage operations requests,
254+
// in this case getDownloadURL.
255+
expect(url).to.match(/^https:\/\/test\.cloudworkstations\.dev:1234.+/);
256+
connection.abort();
257+
injectTestConnection(null);
258+
done();
259+
}
260+
261+
injectTestConnection(() => newTestConnection(newSend));
262+
const service = new FirebaseStorageImpl(
263+
testShared.fakeApp,
264+
testShared.fakeAuthProvider,
265+
testShared.fakeAppCheckTokenProvider
266+
);
267+
const workstationHost = 'test.cloudworkstations.dev';
268+
connectStorageEmulator(service, workstationHost, 1234);
269+
expect(service.host).to.equal(`${workstationHost}:1234`);
270+
expect(service._protocol).to.equal('https');
271+
void getDownloadURL(ref(service, 'test.png'));
272+
});
251273
it('sets mock user token string if specified', done => {
252274
const mockUserToken = 'my-mock-user-token';
253275
function newSend(

packages/util/index.node.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ export * from './src/exponential_backoff';
4242
export * from './src/formatters';
4343
export * from './src/compat';
4444
export * from './src/global';
45+
export * from './src/url';

packages/util/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,4 @@ export * from './src/exponential_backoff';
3737
export * from './src/formatters';
3838
export * from './src/compat';
3939
export * from './src/global';
40+
export * from './src/url';

packages/util/src/url.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @license
3+
* Copyright 2025 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+
/**
19+
* Checks whether host is a cloud workstation or not.
20+
* @public
21+
*/
22+
export function isCloudWorkstation(host: string): boolean {
23+
return host.endsWith('.cloudworkstations.dev');
24+
}

0 commit comments

Comments
 (0)