diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e01532047..25bff018be2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- Fixed an issue where Cloud Run rewrites in the Hosting emulator would always hit the live Cloud Run API instead of routing to the local functions emulator. (#10588) - Updated the Firebase Data Connect local toolkit to v3.4.11, which includes the following changes: - [changed] Updated the Golang dependency version to 1.25.11. - Fixed issue where `apptesting:execute` command rejects documented `--test-username`, `--test-password`, and `--test-password-file` options. diff --git a/src/hosting/cloudRunProxy.spec.ts b/src/hosting/cloudRunProxy.spec.ts index 75e21819372..aa88a8bc8bc 100644 --- a/src/hosting/cloudRunProxy.spec.ts +++ b/src/hosting/cloudRunProxy.spec.ts @@ -6,16 +6,21 @@ import * as supertest from "supertest"; import { cloudRunApiOrigin } from "../api"; import cloudRunProxy, { CloudRunProxyOptions, CloudRunProxyRewrite } from "./cloudRunProxy"; +import { EmulatorRegistry } from "../emulator/registry"; +import { Emulators } from "../emulator/types"; +import { FakeEmulator } from "../emulator/testing/fakeEmulator"; describe("cloudRunProxy", () => { const fakeOptions: CloudRunProxyOptions = { project: "project-foo", + targets: [], }; const fakeRewrite: CloudRunProxyRewrite = { run: { serviceId: "helloworld" } }; const cloudRunServiceOrigin = "https://helloworld-hash-uc.a.run.app"; - afterEach(() => { + afterEach(async () => { nock.cleanAll(); + await EmulatorRegistry.stopAll(); }); it("should error when not provided a valid Cloud Run service ID", async () => { @@ -330,4 +335,52 @@ describe("cloudRunProxy", () => { expect(spyMw.calledOnce).to.be.true; }); }); + + it("should route to the local functions emulator when targets includes 'functions'", async () => { + const fakeFunctionsEmulator = new FakeEmulator(Emulators.FUNCTIONS, [ + { address: "127.0.0.1", family: "IPv4", port: 7778 }, + ]); + await EmulatorRegistry.start(fakeFunctionsEmulator); + + nock("http://127.0.0.1:7778") + .get("/project-foo/us-central1/helloworld/") + .reply(200, "local version"); + + const options: CloudRunProxyOptions = { ...fakeOptions, targets: ["functions"] }; + + const mwGenerator = cloudRunProxy(options); + const mw = await mwGenerator(fakeRewrite); + const spyMw = sinon.spy(mw); + + return supertest(spyMw) + .get("/") + .expect(200, "local version") + .then(() => { + expect(spyMw.calledOnce).to.be.true; + }); + }); + + it("should route to the local functions emulator in another region", async () => { + const fakeFunctionsEmulator = new FakeEmulator(Emulators.FUNCTIONS, [ + { address: "127.0.0.1", family: "IPv4", port: 7778 }, + ]); + await EmulatorRegistry.start(fakeFunctionsEmulator); + + nock("http://127.0.0.1:7778") + .get("/project-foo/asia-southeast1/helloworld/") + .reply(200, "local version"); + + const options: CloudRunProxyOptions = { ...fakeOptions, targets: ["functions"] }; + + const mwGenerator = cloudRunProxy(options); + const mw = await mwGenerator({ run: { serviceId: "helloworld", region: "asia-southeast1" } }); + const spyMw = sinon.spy(mw); + + return supertest(spyMw) + .get("/") + .expect(200, "local version") + .then(() => { + expect(spyMw.calledOnce).to.be.true; + }); + }); }); diff --git a/src/hosting/cloudRunProxy.ts b/src/hosting/cloudRunProxy.ts index c0bbb7ccb2b..dde3aa0d162 100644 --- a/src/hosting/cloudRunProxy.ts +++ b/src/hosting/cloudRunProxy.ts @@ -6,9 +6,13 @@ import { errorRequestHandler, proxyRequestHandler } from "./proxy"; import { FirebaseError } from "../error"; import { logger } from "../logger"; import { needProjectId } from "../projectUtils"; +import { EmulatorRegistry } from "../emulator/registry"; +import { Emulators } from "../emulator/types"; +import { FunctionsEmulator } from "../emulator/functionsEmulator"; export interface CloudRunProxyOptions { project?: string; + targets?: string[]; } export interface CloudRunProxyRewrite { @@ -70,6 +74,17 @@ export default function ( logger.info(`[hosting] Cloud Run rewrite ${JSON.stringify(rewrite)} triggered`); const textIdentifier = `Cloud Run service "${rewrite.run.serviceId}" for region "${rewrite.run.region}"`; + + if (options.targets?.includes("functions") && EmulatorRegistry.isRunning(Emulators.FUNCTIONS)) { + const projectId = needProjectId(options); + const url = FunctionsEmulator.getHttpFunctionUrl( + projectId, + rewrite.run.serviceId, + rewrite.run.region, + ); + return proxyRequestHandler(url, `local ${textIdentifier}`); + } + return getCloudRunUrl(rewrite, needProjectId(options)) .then((url) => proxyRequestHandler(url, textIdentifier)) .catch(errorRequestHandler);