diff --git a/src/event/event.ts b/src/event/event.ts index 8dbf4068..58240e5a 100644 --- a/src/event/event.ts +++ b/src/event/event.ts @@ -9,7 +9,16 @@ import { } from "../utils"; import { H3Response } from "./response"; -const DOUBLE_SLASH_RE = /[/\\]{2,}/g; // TODO: Dedup from request.ts +// TODO: Dedup from request.ts +const DOUBLE_SLASH_RE = /[/\\]{2,}/g; + +// TODO: Dedup from body.ts +const PayloadMethods: Set = new Set([ + "PATCH", + "POST", + "PUT", + "DELETE", +]); export interface NodeEventContext { req: NodeIncomingMessage; @@ -24,6 +33,7 @@ export class H3Event implements Pick { context: H3EventContext = {}; // Request + _request: Request | undefined; _method: HTTPMethod | undefined; _headers: Headers | undefined; _path: string | undefined; @@ -45,6 +55,10 @@ export class H3Event implements Pick { ); } + get _hasBody() { + return PayloadMethods.has(this.method!); + } + get path() { if (!this._path) { this._path = this._originalPath.replace(DOUBLE_SLASH_RE, "/"); @@ -87,7 +101,10 @@ export class H3Event implements Pick { } get body() { - if (!this._body) { + if (!this._hasBody) { + return undefined; + } + if (this._body === undefined) { this._body = new ReadableStream({ start: (controller) => { this.node.req.on("data", (chunk) => { @@ -105,6 +122,20 @@ export class H3Event implements Pick { return this._body; } + /** @experimental */ + get request(): Request { + if (!this._request) { + this._request = new Request(this.url, { + // @ts-ignore Undici option + duplex: "half", + method: this.method, + headers: this.headers, + body: this.body, + }); + } + return this._request; + } + // Implementation of FetchEvent respondWith(r: H3Response | PromiseLike): void { Promise.resolve(r).then((_response) => { diff --git a/test/event.test.ts b/test/event.test.ts index 4eb6d32d..a98731dc 100644 --- a/test/event.test.ts +++ b/test/event.test.ts @@ -78,4 +78,28 @@ describe("Event", () => { expect(result.body).toMatchObject({ bytes: 3 }); }); + + it("can convert to a web request", async () => { + app.use( + "/", + eventHandler(async (event) => { + expect(event.request.method).toBe("POST"); + expect(event.request.headers.get("x-test")).toBe("123"); + // TODO: Find a workaround for Node.js 16 + if (!process.versions.node.startsWith("16")) { + expect(await event.request.text()).toMatchObject( + JSON.stringify({ hello: "world" }) + ); + } + return "200"; + }) + ); + const result = await request + .post("/hello") + .set("x-test", "123") + .set("content-type", "application/json") + .send(JSON.stringify({ hello: "world" })); + + expect(result.text).toBe("200"); + }); });