diff --git a/src/request.ts b/src/request.ts index 28408ba..b0428a4 100644 --- a/src/request.ts +++ b/src/request.ts @@ -74,8 +74,19 @@ const newRequestFromIncoming = ( } if (!(method === 'GET' || method === 'HEAD')) { - // lazy-consume request body - init.body = Readable.toWeb(incoming) as ReadableStream + if ('rawBody' in incoming && incoming.rawBody instanceof Buffer) { + // In some environments (e.g. firebase functions), the body is already consumed. + // So we need to re-read the request body from `incoming.rawBody` if available. + init.body = new ReadableStream({ + start(controller) { + controller.enqueue(incoming.rawBody) + controller.close() + }, + }) + } else { + // lazy-consume request body + init.body = Readable.toWeb(incoming) as ReadableStream + } } return new Request(url, init) diff --git a/test/request.test.ts b/test/request.test.ts index 999a371..0da2b6b 100644 --- a/test/request.test.ts +++ b/test/request.test.ts @@ -1,4 +1,5 @@ -import type { IncomingMessage } from 'node:http' +import { IncomingMessage } from 'node:http' +import { Socket } from 'node:net' import { newRequest, Request as LightweightRequest, @@ -112,6 +113,29 @@ describe('Request', () => { } as IncomingMessage) }).toThrow(RequestError) }) + + it('Should be create request body from `req.rawBody` if it exists', async () => { + const rawBody = Buffer.from('foo') + const socket = new Socket() + const incomingMessage = new IncomingMessage(socket) + incomingMessage.method = 'POST' + incomingMessage.headers = { + host: 'localhost', + } + incomingMessage.url = '/foo.txt' + ;(incomingMessage as IncomingMessage & { rawBody: Buffer }).rawBody = rawBody + incomingMessage.push(rawBody) + incomingMessage.push(null) + + for await (const chunk of incomingMessage) { + // consume body + expect(chunk).toBeDefined() + } + + const req = newRequest(incomingMessage) + const text = await req.text() + expect(text).toBe('foo') + }) }) describe('GlobalRequest', () => {