diff --git a/CHANGELOG.md b/CHANGELOG.md index 990b8452a9f..769e639da03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,3 @@ +* Fixes a bug where the Functions emulator ignored the "host" configuration (#1722) +* Fixes a bug where the Functions emulator accepted requests to too many paths (#1773) * Print Firebase Console links for Extensions after operations. diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index 913c3a52063..181b5beba45 100644 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -19,22 +19,22 @@ import * as getProjectId from "../getProjectId"; export const VALID_EMULATOR_STRINGS: string[] = ALL_EMULATORS; -export async function checkPortOpen(port: number): Promise { +export async function checkPortOpen(port: number, host: string): Promise { try { - const inUse = await tcpport.check(port); + const inUse = await tcpport.check(port, host); return !inUse; } catch (e) { return false; } } -export async function waitForPortClosed(port: number): Promise { +export async function waitForPortClosed(port: number, host: string): Promise { const interval = 250; const timeout = 30000; try { await tcpport.waitUntilUsed(port, interval, timeout); } catch (e) { - throw new FirebaseError(`TIMEOUT: Port ${port} was not active within ${timeout}ms`); + throw new FirebaseError(`TIMEOUT: Port ${port} on ${host} was not active within ${timeout}ms`); } } @@ -45,8 +45,7 @@ export async function startEmulator(instance: EmulatorInstance): Promise { // Log the command for analytics track("emulators:start", name); - // TODO(samstern): This check should only occur when the host is localhost - const portOpen = await checkPortOpen(info.port); + const portOpen = await checkPortOpen(info.port, info.host); if (!portOpen) { await cleanShutdown(); utils.logWarning(`Port ${info.port} is not open, could not start ${name} emulator.`); diff --git a/src/emulator/emulatorServer.ts b/src/emulator/emulatorServer.ts index b6c9031c6da..4704c8ef346 100644 --- a/src/emulator/emulatorServer.ts +++ b/src/emulator/emulatorServer.ts @@ -11,12 +11,12 @@ export class EmulatorServer { constructor(public instance: EmulatorInstance) {} async start(): Promise { - const port = this.instance.getInfo().port; - const portOpen = await controller.checkPortOpen(port); + const { port, host } = this.instance.getInfo(); + const portOpen = await controller.checkPortOpen(port, host); if (!portOpen) { throw new FirebaseError( - `Port ${port} is not open, could not start ${this.instance.getName()} emulator.` + `Port ${port} is not open on ${host}, could not start ${this.instance.getName()} emulator.` ); } diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 3d86f214a3e..6c4cfc489e0 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -112,10 +112,12 @@ export class FunctionsEmulator implements EmulatorInstance { // The URL for the function that the other emulators (Firestore, etc) use. // TODO(abehaskins): Make the other emulators use the route below and remove this. - const backgroundFunctionRoute = "/functions/projects/:project_id/triggers/:trigger_name"; + const backgroundFunctionRoute = `/functions/projects/${ + this.args.projectId + }/triggers/:trigger_name`; // The URL that the developer sees, this is the same URL that the legacy emulator used. - const httpsFunctionRoute = `/:project_id/:region/:trigger_name`; + const httpsFunctionRoute = `/${this.args.projectId}/:region/:trigger_name`; // A trigger named "foo" needs to respond at "foo" as well as "foo/*" but not "fooBar". const httpsFunctionRoutes = [httpsFunctionRoute, `${httpsFunctionRoute}/*`]; diff --git a/src/emulator/registry.ts b/src/emulator/registry.ts index 2f296c32cc0..fa3495a3d45 100644 --- a/src/emulator/registry.ts +++ b/src/emulator/registry.ts @@ -19,10 +19,11 @@ export class EmulatorRegistry { // Start the emulator and wait for it to grab its assigned port. await instance.start(); - await controller.waitForPortClosed(instance.getInfo().port); - this.set(instance.getName(), instance); const info = instance.getInfo(); + await controller.waitForPortClosed(info.port, info.host); + + this.set(instance.getName(), instance); utils.logLabeledSuccess( instance.getName(), `Emulator started at ${clc.bold.underline(`http://${info.host}:${info.port}`)}` diff --git a/src/test/emulators/functionsEmulator.spec.ts b/src/test/emulators/functionsEmulator.spec.ts index 7c556b65ea9..9423b58ef71 100644 --- a/src/test/emulators/functionsEmulator.spec.ts +++ b/src/test/emulators/functionsEmulator.spec.ts @@ -20,7 +20,7 @@ if ((process.env.DEBUG || "").toLowerCase().indexOf("spec") >= 0) { } const functionsEmulator = new FunctionsEmulator({ - projectId: "", + projectId: "fake-project-id", functionsDir: "", }); const startFunctionRuntime = functionsEmulator.startFunctionRuntime; @@ -107,6 +107,24 @@ describe("FunctionsEmulator-Hub", () => { }); }).timeout(TIMEOUT_LONG); + it("should reject requests to a non-emulator path", async () => { + UseFunctions(() => { + return { + function_id: require("firebase-functions").https.onRequest( + (req: express.Request, res: express.Response) => { + res.json({ path: req.path }); + } + ), + }; + }); + + await supertest( + functionsEmulator.createHubServer(FunctionRuntimeBundles.template, process.execPath) + ) + .get("/foo/bar/baz") + .expect(404); + }).timeout(TIMEOUT_LONG); + it("should rewrite req.path to hide /:project_id/:region/:trigger_id", async () => { UseFunctions(() => { require("firebase-admin").initializeApp();