Skip to content

fix(hosting): preserve request body when a web frameworks rewrite falls through to a Cloud Function#10760

Merged
leoortizz merged 9 commits into
mainfrom
leoortizz_wfEmulatorPostTimeout
Jul 15, 2026
Merged

fix(hosting): preserve request body when a web frameworks rewrite falls through to a Cloud Function#10760
leoortizz merged 9 commits into
mainfrom
leoortizz_wfEmulatorPostTimeout

Conversation

@leoortizz

Copy link
Copy Markdown
Member

Description

Fixes #5986.

With Web Frameworks enabled, the Hosting emulator forwards every request to the framework's dev server before evaluating rewrites, and that consumes the request body. When the dev server returned a 404 and the request fell through to a function rewrite, the emulator forwarded an empty body while still advertising the original content-length, so the function call hung until it timed out with a 504. GET requests worked; anything with a body failed.

The emulator now keeps a copy of the request body before handing the request to the dev server and replays it if the request falls through to a rewrite. Behavior outside Web Frameworks is unchanged, and this only affects the emulator — production Hosting was never affected.

Scenarios Tested

  • SvelteKit (the original report), Vite, Astro, and Angular apps in the Hosting emulator with a function rewrite: POST/PUT/DELETE with JSON, empty, chunked, and ~200KB bodies now reach the function with the body intact (previously a 60s hang → 504). Verified against the reporter's reproduction repo.
  • The framework's own API routes (e.g. SvelteKit +server, Astro endpoints) still receive request bodies normally.
  • Requests to paths owned by neither the framework nor a rewrite still return a fast 404.
  • Static pages, query strings, and GET rewrites behave as before.
  • Unit tests

Sample Commands

firebase emulators:start

With Web Frameworks enabled, the Hosting emulator forwards every request
to the framework dev server first, which consumes the request body. When
the dev server 404s and the request falls through to a function rewrite,
the proxy forwarded a drained stream while still advertising the
original
content-length, so the function call hung until the 60s timeout (504).
Buffer the body in the dev-server proxy and replay it when the request
cascades to a rewrite. Requests outside Web Frameworks are unaffected.

Fixes #5986
@leoortizz leoortizz changed the title fix(hosting): preserve POST body for emulated function rewrites fix(hosting): preserve request body when a web frameworks rewrite falls through to a Cloud Function Jul 3, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses an issue where a request body is consumed by the framework dev server, causing subsequent cascaded rewrites (like Cloud Functions) to hang due to a drained stream. It introduces request body buffering in simpleProxy and exposes the buffered body on req.rawBody so it can be replayed in proxyRequestHandler. Tests are added to verify this behavior for both standard and chunked requests. Feedback on the PR points out that http.request does not automatically set the Content-Length header when a Buffer is passed to req.end(). Therefore, when deleting transfer-encoding, content-length should be explicitly set to rawBody.length.toString() to avoid falling back to chunked transfer encoding.

Comment thread src/frameworks/utils.ts Outdated
@leoortizz
leoortizz requested review from Yuangwang and annajowang July 3, 2026 21:09
Comment thread src/frameworks/utils.ts Outdated
* Read a request stream into a Buffer so a consumed request body can be replayed
* to a downstream handler.
*/
async function bufferRequestBody(req: IncomingMessage): Promise<Buffer> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we limit the payload size at all? could a large enough payload cause an OOM crash? I believe cloud functions limits to 10MB anyway, should we just limit if here as well for a more realistic dev experience?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn’t limit it before but it’s a simple change, just implemented it. FWICT Web Frameworks’s CF is gen 2, so that is 32MB: https://docs.cloud.google.com/functions/quotas#resource_limits

Comment thread src/frameworks/utils.ts Outdated
// Client aborted / stream errored mid-upload. End gracefully rather
// than hanging the response.
logger.debug("Error buffering request body:", method, path, err);
originalRes.end();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be the originalRes.statusCode be in this case?
should it explicitly be set to something?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call thanks, changed this to return 400 when the response is still writable. I previously tried setting 400 but was having an error since the response wasn't writable anymore, but a simple check fixed it.

Comment thread src/frameworks/utils.ts
let rawBody: Buffer | undefined;
if (!["GET", "HEAD"].includes(method)) {
try {
rawBody = await bufferRequestBody(originalReq);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC this will slow down every single request....could we "Tee" stream this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried it, but the results are pretty much the same across all frameworks. It'd be a rewrite of how we handle the proxy, so I wasn't sure it's worth the risk for this PR. Framework API routes are ~15% faster on large uploads, but rewrites (this PR's/issue case) behave the same.

Since it's emulator/local only and payloads are capped at 32MB, the gain is just a few ms. Happy to implement it if you'd prefer.

@leoortizz
leoortizz requested a review from annajowang July 14, 2026 21:46
@annajowang

Copy link
Copy Markdown
Contributor

thanks! still lgtm, don't have to invest in "Tee" stream for emulator if not worth it

@leoortizz
leoortizz enabled auto-merge (squash) July 15, 2026 17:45
@leoortizz
leoortizz merged commit f86d2fa into main Jul 15, 2026
53 checks passed
@leoortizz
leoortizz deleted the leoortizz_wfEmulatorPostTimeout branch July 15, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hosting rewrite post call to function times out

4 participants