From c56a1678a727e2237ecbb0127cd131113c44380e Mon Sep 17 00:00:00 2001 From: Chalo Salvador Date: Mon, 19 Jun 2023 14:19:59 +0200 Subject: [PATCH 1/6] Update proxyResponse method --- src/frameworks/utils.ts | 60 +++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/src/frameworks/utils.ts b/src/frameworks/utils.ts index 4c139fc9ab3..cb0a885d1e6 100644 --- a/src/frameworks/utils.ts +++ b/src/frameworks/utils.ts @@ -63,20 +63,47 @@ export async function warnIfCustomBuildScript( } } -function proxyResponse(original: ServerResponse, next: () => void) { - return (response: IncomingMessage | ServerResponse) => { - const { statusCode, statusMessage } = response; - if (!statusCode) { - original.end(); - return; - } - if (statusCode === 404) { - return next(); - } - const headers = "getHeaders" in response ? response.getHeaders() : response.headers; - original.writeHead(statusCode, statusMessage, headers); - response.pipe(original); - }; +export function proxyResponse(req: IncomingMessage, res: ServerResponse, next: () => void) { + const proxiedRes = new ServerResponse(req); + const buffer: [string, any[]][] = []; + proxiedRes.write = new Proxy(proxiedRes.write.bind(proxiedRes), { + apply: (target: any, thisArg, args) => { + target.call(thisArg, ...args); + buffer.push(["write", args]); + }, + }); + proxiedRes.setHeader = new Proxy(proxiedRes.setHeader.bind(proxiedRes), { + apply: (target: any, thisArg, args) => { + target.call(thisArg, ...args); + buffer.push(["setHeader", args]); + }, + }); + proxiedRes.removeHeader = new Proxy(proxiedRes.removeHeader.bind(proxiedRes), { + apply: (target: any, thisArg, args) => { + target.call(thisArg, ...args); + buffer.push(["removeHeader", args]); + }, + }); + proxiedRes.writeHead = new Proxy(proxiedRes.writeHead.bind(proxiedRes), { + apply: (target: any, thisArg, args) => { + target.call(thisArg, ...args); + buffer.push(["writeHead", args]); + }, + }); + proxiedRes.end = new Proxy(proxiedRes.end.bind(proxiedRes), { + apply: (target: any, thisArg, args) => { + target.call(thisArg, ...args); + if (proxiedRes.statusCode === 404) { + next(); + } else { + for (const [fn, args] of buffer) { + (res as any)[fn](...args); + } + res.end(...args); + } + }, + }); + return proxiedRes; } export function simpleProxy(hostOrRequestHandler: string | RequestHandler) { @@ -127,9 +154,8 @@ export function simpleProxy(hostOrRequestHandler: string | RequestHandler) { originalRes.end(); }); } else { - await Promise.resolve(hostOrRequestHandler(originalReq, originalRes, next)); - const proxiedRes = new ServerResponse(originalReq); - proxyResponse(originalRes, next)(proxiedRes); + const proxiedRes = proxyResponse(originalReq, originalRes, next); + await hostOrRequestHandler(originalReq, proxiedRes, next); } }; } From 1f070a7bfa6d6c8f56c516893d8230cf930e33b7 Mon Sep 17 00:00:00 2001 From: Chalo Salvador Date: Wed, 21 Jun 2023 17:18:10 +0200 Subject: [PATCH 2/6] Add proper types to proxyResponse function --- src/frameworks/utils.ts | 64 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/src/frameworks/utils.ts b/src/frameworks/utils.ts index cb0a885d1e6..68e68102eba 100644 --- a/src/frameworks/utils.ts +++ b/src/frameworks/utils.ts @@ -63,36 +63,84 @@ export async function warnIfCustomBuildScript( } } -export function proxyResponse(req: IncomingMessage, res: ServerResponse, next: () => void) { +/** + * Proxy a HTTP response + * It uses the Proxy object to intercept the response and buffer it until the + * response is finished. This allows us to modify the response before sending + * it back to the client. + */ +export function proxyResponse( + req: IncomingMessage, + res: ServerResponse, + next: () => void +): ServerResponse { const proxiedRes = new ServerResponse(req); - const buffer: [string, any[]][] = []; + // Object to store the original response methods + const buffer: [ + string, + Parameters + ][] = []; + + // Proxy the response methods + // The apply handler is called when the method e.g. write, setHeader, etc. is called + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/apply + // The target is the original method + // The thisArg is the proxied response + // The args are the arguments passed to the method proxiedRes.write = new Proxy(proxiedRes.write.bind(proxiedRes), { - apply: (target: any, thisArg, args) => { + apply: ( + target: ServerResponse["write"], + thisArg: ServerResponse, + args: Parameters + ) => { + // call the original write method on the proxied response target.call(thisArg, ...args); + // store the method call in the buffer buffer.push(["write", args]); }, }); + proxiedRes.setHeader = new Proxy(proxiedRes.setHeader.bind(proxiedRes), { - apply: (target: any, thisArg, args) => { + apply: ( + target: ServerResponse["setHeader"], + thisArg: ServerResponse, + args: Parameters + ) => { target.call(thisArg, ...args); buffer.push(["setHeader", args]); }, }); proxiedRes.removeHeader = new Proxy(proxiedRes.removeHeader.bind(proxiedRes), { - apply: (target: any, thisArg, args) => { + apply: ( + target: ServerResponse["removeHeader"], + thisArg: ServerResponse, + args: Parameters + ) => { target.call(thisArg, ...args); buffer.push(["removeHeader", args]); }, }); proxiedRes.writeHead = new Proxy(proxiedRes.writeHead.bind(proxiedRes), { - apply: (target: any, thisArg, args) => { + apply: ( + target: ServerResponse["writeHead"], + thisArg: ServerResponse, + args: Parameters + ) => { target.call(thisArg, ...args); buffer.push(["writeHead", args]); }, }); proxiedRes.end = new Proxy(proxiedRes.end.bind(proxiedRes), { - apply: (target: any, thisArg, args) => { + apply: ( + target: ServerResponse["end"], + thisArg: ServerResponse, + args: Parameters + ) => { + // call the original end method on the proxied response target.call(thisArg, ...args); + // if the proxied response is a 404, call next to continue down the middleware chain + // otherwise, send the buffered response i.e. call the original response methods: write, setHeader, etc. + // and then end the response and clear the buffer if (proxiedRes.statusCode === 404) { next(); } else { @@ -100,9 +148,11 @@ export function proxyResponse(req: IncomingMessage, res: ServerResponse, next: ( (res as any)[fn](...args); } res.end(...args); + buffer.length = 0; } }, }); + return proxiedRes; } From 88a67c9fa3636d1ca097ad5feec9a0a80b94c94b Mon Sep 17 00:00:00 2001 From: Chalo Salvador Date: Wed, 21 Jun 2023 17:21:13 +0200 Subject: [PATCH 3/6] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb2d..355b54d41eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fixes issue that caused firebase emulators:start to crash in Next.js apps (#6005) \ No newline at end of file From 75ef94b096abb77790b8251f84bcbd2cd7ebc32f Mon Sep 17 00:00:00 2001 From: Chalo Salvador Date: Wed, 21 Jun 2023 19:37:53 +0200 Subject: [PATCH 4/6] Changelog formatting --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6bbac2429f..3d7c98fb248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,2 @@ - Run lifecycle hooks for specific codebases. (#6011) -- Fixes issue that caused firebase emulators:start to crash in Next.js apps (#6005) \ No newline at end of file +- Fixes issue that caused firebase emulators:start to crash in Next.js apps (#6005) From 314d248981cddb71e45d32f043a405f2e7cb1b98 Mon Sep 17 00:00:00 2001 From: joehan Date: Wed, 21 Jun 2023 11:13:07 -0700 Subject: [PATCH 5/6] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d7c98fb248..c93ac2e5b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,2 @@ - Run lifecycle hooks for specific codebases. (#6011) -- Fixes issue that caused firebase emulators:start to crash in Next.js apps (#6005) +- Fixed issue causing `firebase emulators:start` to crash in Next.js apps (#6005) From b95db726732b9a481b827f9c605d2699d87f8345 Mon Sep 17 00:00:00 2001 From: Chalo Salvador Date: Thu, 22 Jun 2023 11:42:50 +0200 Subject: [PATCH 6/6] Update firebase-vscode/package-lock.json --- firebase-vscode/package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase-vscode/package-lock.json b/firebase-vscode/package-lock.json index a9525311ad3..56f8dbfade9 100644 --- a/firebase-vscode/package-lock.json +++ b/firebase-vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-vscode", - "version": "0.0.22", + "version": "0.0.22-alpha.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-vscode", - "version": "0.0.22", + "version": "0.0.22-alpha.0", "dependencies": { "@vscode/codicons": "0.0.30", "@vscode/webview-ui-toolkit": "^1.2.1",