From 160bde7c8cbb35e1424e10769b79b6657900b92c Mon Sep 17 00:00:00 2001 From: James Daniels Date: Mon, 5 Aug 2024 16:05:25 -0400 Subject: [PATCH 1/5] PoC of detected an unguarded ng run() --- .../firebase-frameworks/src/angular/index.ts | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/firebase-frameworks/src/angular/index.ts b/packages/firebase-frameworks/src/angular/index.ts index e38d5265..2e9bed24 100644 --- a/packages/firebase-frameworks/src/angular/index.ts +++ b/packages/firebase-frameworks/src/angular/index.ts @@ -1,14 +1,36 @@ import type { Request } from "firebase-functions/v2/https"; import type { Response } from "express"; -import { handle as expressHandle } from "../express/index.js"; import { basename, join, normalize, relative } from "path"; -import { createReadStream } from "fs"; +import { createReadStream, existsSync } from "fs"; import { mediaTypes } from "@hapi/accept"; +import { pathToFileURL } from "url"; const LOCALE_FORMATS = [/^ALL_[a-z]+$/, /^[a-z]+_ALL$/, /^[a-z]+(_[a-z]+)?$/]; const NG_BROWSER_OUTPUT_PATH = process.env.__NG_BROWSER_OUTPUT_PATH__; +const expressHandle = new Promise<[(typeof import("../express/index.js"))["handle"], string?]>( + (resolve) => { + setTimeout(() => { + const port = process.env.PORT; + const socket = `express.sock`; + process.env.PORT = socket; + // can't import from express, it's too lazy. alt we could export app from bootstrap + import( + `${pathToFileURL(process.cwd())}/dist/firebase-app-hosting-angular/server/server.mjs` + ).then(({ app }) => { + setTimeout(() => { + if (existsSync(socket)) { + resolve([app, socket]); + } + resolve([app]); + process.env.PORT = port; + }, 0); + }); + }, 0); + }, +); + export const handle = async (req: Request, res: Response) => { if (basename(req.path) === "__image__") { const { src, locale = "" } = req.query; @@ -33,6 +55,10 @@ export const handle = async (req: Request, res: Response) => { res.setHeader("Vary", "Accept, Accept-Encoding"); createReadStream(normalizedPath).pipe(pipeline).pipe(res); } else { - await expressHandle(req, res); + const [handle, socket] = await expressHandle; + if (socket) { + throw new Error("TODO proxy to express.sock"); + } + handle(req, res); } }; From 24a78b068f0d8d371f0930c7798910b62f5434d3 Mon Sep 17 00:00:00 2001 From: James Daniels Date: Mon, 5 Aug 2024 16:45:58 -0400 Subject: [PATCH 2/5] Proxy the requests --- .../firebase-frameworks/src/angular/index.ts | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/firebase-frameworks/src/angular/index.ts b/packages/firebase-frameworks/src/angular/index.ts index 2e9bed24..4d980daa 100644 --- a/packages/firebase-frameworks/src/angular/index.ts +++ b/packages/firebase-frameworks/src/angular/index.ts @@ -5,11 +5,13 @@ import { basename, join, normalize, relative } from "path"; import { createReadStream, existsSync } from "fs"; import { mediaTypes } from "@hapi/accept"; import { pathToFileURL } from "url"; +import { incomingMessageFromExpress } from "../utils.js"; +import { request as httpRequest } from "http"; const LOCALE_FORMATS = [/^ALL_[a-z]+$/, /^[a-z]+_ALL$/, /^[a-z]+(_[a-z]+)?$/]; const NG_BROWSER_OUTPUT_PATH = process.env.__NG_BROWSER_OUTPUT_PATH__; -const expressHandle = new Promise<[(typeof import("../express/index.js"))["handle"], string?]>( +const expressHandle = new Promise<[(typeof import("../express/index.js"))["handle"]?, string?]>( (resolve) => { setTimeout(() => { const port = process.env.PORT; @@ -21,11 +23,11 @@ const expressHandle = new Promise<[(typeof import("../express/index.js"))["handl ).then(({ app }) => { setTimeout(() => { if (existsSync(socket)) { - resolve([app, socket]); + resolve([undefined, socket]); } - resolve([app]); - process.env.PORT = port; - }, 0); + resolve([app()]); + }, 10); + process.env.PORT = port; }); }, 0); }, @@ -55,10 +57,25 @@ export const handle = async (req: Request, res: Response) => { res.setHeader("Vary", "Accept, Accept-Encoding"); createReadStream(normalizedPath).pipe(pipeline).pipe(res); } else { - const [handle, socket] = await expressHandle; - if (socket) { - throw new Error("TODO proxy to express.sock"); + const [handle, socketPath] = await expressHandle; + if (socketPath) { + const incomingMessage = incomingMessageFromExpress(req); + const proxyRequest = httpRequest({ ...incomingMessage, socketPath }, (response) => { + const { statusCode, statusMessage, headers } = response; + if (!statusCode) { + console.error("No status code."); + res.end(500); + } + res.writeHead(statusCode!, statusMessage, headers); + response.pipe(res); + }); + req.pipe(proxyRequest); + proxyRequest.on("error", (err) => { + console.error(err); + res.end(500); + }); + } else { + handle!(req, res); } - handle(req, res); } }; From 2952ef853b6682af5173cbc300d5baabbb4d5ea0 Mon Sep 17 00:00:00 2001 From: James Daniels Date: Mon, 5 Aug 2024 16:50:53 -0400 Subject: [PATCH 3/5] Comments --- packages/firebase-frameworks/src/angular/index.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/firebase-frameworks/src/angular/index.ts b/packages/firebase-frameworks/src/angular/index.ts index 4d980daa..c04985f9 100644 --- a/packages/firebase-frameworks/src/angular/index.ts +++ b/packages/firebase-frameworks/src/angular/index.ts @@ -14,10 +14,14 @@ const NG_BROWSER_OUTPUT_PATH = process.env.__NG_BROWSER_OUTPUT_PATH__; const expressHandle = new Promise<[(typeof import("../express/index.js"))["handle"]?, string?]>( (resolve) => { setTimeout(() => { + // We could just change the PORT to something else, but it seems you can't fire up two + // Angular Express servers listening to the same port for whatever reason... maybe we can + // find the root cause. + // In the meantime use a socket. const port = process.env.PORT; const socket = `express.sock`; process.env.PORT = socket; - // can't import from express, it's too lazy. alt we could export app from bootstrap + // can't import from express, it's too lazy. alt we could export/import app from bootstrap import( `${pathToFileURL(process.cwd())}/dist/firebase-app-hosting-angular/server/server.mjs` ).then(({ app }) => { @@ -26,7 +30,7 @@ const expressHandle = new Promise<[(typeof import("../express/index.js"))["handl resolve([undefined, socket]); } resolve([app()]); - }, 10); + }, 10); // don't like the arbitrary wait here, is there a better way? process.env.PORT = port; }); }, 0); @@ -75,6 +79,7 @@ export const handle = async (req: Request, res: Response) => { res.end(500); }); } else { + // TODO fix the types handle!(req, res); } } From de891a3027670a08a019e9f2404548fbaa3d298c Mon Sep 17 00:00:00 2001 From: James Daniels Date: Mon, 5 Aug 2024 16:51:46 -0400 Subject: [PATCH 4/5] fix comment --- packages/firebase-frameworks/src/angular/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firebase-frameworks/src/angular/index.ts b/packages/firebase-frameworks/src/angular/index.ts index c04985f9..5ba4fbdc 100644 --- a/packages/firebase-frameworks/src/angular/index.ts +++ b/packages/firebase-frameworks/src/angular/index.ts @@ -15,7 +15,7 @@ const expressHandle = new Promise<[(typeof import("../express/index.js"))["handl (resolve) => { setTimeout(() => { // We could just change the PORT to something else, but it seems you can't fire up two - // Angular Express servers listening to the same port for whatever reason... maybe we can + // Angular Express servers on the same directory for whatever reason... maybe we can // find the root cause. // In the meantime use a socket. const port = process.env.PORT; From bf7c6fc52bf8eca0562e08372e9345fe95d6927f Mon Sep 17 00:00:00 2001 From: James Daniels Date: Mon, 5 Aug 2024 16:57:39 -0400 Subject: [PATCH 5/5] Simpler --- .../firebase-frameworks/src/angular/index.ts | 66 +++++-------------- 1 file changed, 17 insertions(+), 49 deletions(-) diff --git a/packages/firebase-frameworks/src/angular/index.ts b/packages/firebase-frameworks/src/angular/index.ts index 5ba4fbdc..63c21832 100644 --- a/packages/firebase-frameworks/src/angular/index.ts +++ b/packages/firebase-frameworks/src/angular/index.ts @@ -2,40 +2,27 @@ import type { Request } from "firebase-functions/v2/https"; import type { Response } from "express"; import { basename, join, normalize, relative } from "path"; -import { createReadStream, existsSync } from "fs"; +import { createReadStream } from "fs"; import { mediaTypes } from "@hapi/accept"; import { pathToFileURL } from "url"; -import { incomingMessageFromExpress } from "../utils.js"; -import { request as httpRequest } from "http"; const LOCALE_FORMATS = [/^ALL_[a-z]+$/, /^[a-z]+_ALL$/, /^[a-z]+(_[a-z]+)?$/]; const NG_BROWSER_OUTPUT_PATH = process.env.__NG_BROWSER_OUTPUT_PATH__; -const expressHandle = new Promise<[(typeof import("../express/index.js"))["handle"]?, string?]>( - (resolve) => { - setTimeout(() => { - // We could just change the PORT to something else, but it seems you can't fire up two - // Angular Express servers on the same directory for whatever reason... maybe we can - // find the root cause. - // In the meantime use a socket. - const port = process.env.PORT; - const socket = `express.sock`; - process.env.PORT = socket; - // can't import from express, it's too lazy. alt we could export/import app from bootstrap - import( - `${pathToFileURL(process.cwd())}/dist/firebase-app-hosting-angular/server/server.mjs` - ).then(({ app }) => { - setTimeout(() => { - if (existsSync(socket)) { - resolve([undefined, socket]); - } - resolve([app()]); - }, 10); // don't like the arbitrary wait here, is there a better way? - process.env.PORT = port; - }); - }, 0); - }, -); +const expressHandle = new Promise<(typeof import("../express/index.js"))["handle"]>((resolve) => { + setTimeout(() => { + const port = process.env.PORT; + const socket = `express.sock`; + process.env.PORT = socket; + // can't import from express, it's too lazy. alt we could export/import app from bootstrap + import( + `${pathToFileURL(process.cwd())}/dist/firebase-app-hosting-angular/server/server.mjs` + ).then(({ app }) => { + process.env.PORT = port; + resolve(app()); + }); + }, 0); +}); export const handle = async (req: Request, res: Response) => { if (basename(req.path) === "__image__") { @@ -61,26 +48,7 @@ export const handle = async (req: Request, res: Response) => { res.setHeader("Vary", "Accept, Accept-Encoding"); createReadStream(normalizedPath).pipe(pipeline).pipe(res); } else { - const [handle, socketPath] = await expressHandle; - if (socketPath) { - const incomingMessage = incomingMessageFromExpress(req); - const proxyRequest = httpRequest({ ...incomingMessage, socketPath }, (response) => { - const { statusCode, statusMessage, headers } = response; - if (!statusCode) { - console.error("No status code."); - res.end(500); - } - res.writeHead(statusCode!, statusMessage, headers); - response.pipe(res); - }); - req.pipe(proxyRequest); - proxyRequest.on("error", (err) => { - console.error(err); - res.end(500); - }); - } else { - // TODO fix the types - handle!(req, res); - } + const handle = await expressHandle; + await handle(req, res); } };