Skip to content

Commit 6338800

Browse files
committed
fix: handle gcf, verify already provided request.body
1 parent 8423803 commit 6338800

File tree

5 files changed

+123
-12
lines changed

5 files changed

+123
-12
lines changed

src/middleware/node/get-payload.ts

-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ type IncomingMessage = any;
1414
export function getPayload(request: IncomingMessage): Promise<string> {
1515
// If request.body already exists we can stop here
1616
// See https://github.com/octokit/webhooks.js/pull/23
17-
18-
if (request.body) return Promise.resolve(request.body);
19-
2017
return new Promise((resolve, reject) => {
2118
let data = "";
2219

src/middleware/node/middleware.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,28 @@ export async function middleware(
9393
}, 9000).unref();
9494

9595
try {
96-
const payload = await getPayload(request);
96+
let payload: string;
97+
let body: { [key: string]: any } | undefined;
98+
99+
const bodyType = typeof request.body;
100+
101+
if (bodyType === "string") {
102+
payload = request.body;
103+
} else if (bodyType === "object") {
104+
body = request.body;
105+
payload =
106+
request.rawBody instanceof Buffer
107+
? request.rawBody.toString("utf8")
108+
: JSON.stringify(request.body);
109+
} else {
110+
payload = await getPayload(request);
111+
}
97112

98113
await webhooks.verifyAndReceive({
99114
id: id,
100115
name: eventName as any,
101116
payload,
117+
body,
102118
signature: signatureSHA256,
103119
});
104120
clearTimeout(timeout);

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type EmitterWebhookEvent<
1818
export type EmitterWebhookEventWithStringPayloadAndSignature = {
1919
id: string;
2020
name: EmitterWebhookEventName;
21+
body?: { [key: string]: any };
2122
payload: string;
2223
signature: string;
2324
};

src/verify-and-receive.ts

+13-8
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,23 @@ export async function verifyAndReceive(
2929
);
3030
}
3131

32-
let payload: EmitterWebhookEvent["payload"];
32+
if (event.body) {
33+
return state.eventHandler.receive({
34+
id: event.id,
35+
name: event.name,
36+
payload: event.body as EmitterWebhookEvent["payload"],
37+
});
38+
}
39+
3340
try {
34-
payload = JSON.parse(event.payload);
41+
return state.eventHandler.receive({
42+
id: event.id,
43+
name: event.name,
44+
payload: JSON.parse(event.payload) as EmitterWebhookEvent["payload"],
45+
});
3546
} catch (error: any) {
3647
error.message = "Invalid JSON";
3748
error.status = 400;
3849
throw new AggregateError([error]);
3950
}
40-
41-
return state.eventHandler.receive({
42-
id: event.id,
43-
name: event.name,
44-
payload,
45-
});
4651
}

test/integration/node-middleware.test.ts

+92
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,98 @@ describe("createNodeMiddleware(webhooks)", () => {
107107
server.close();
108108
});
109109

110+
test("request.body is already an Object (e.g. GCF)", async () => {
111+
expect.assertions(3);
112+
113+
const webhooks = new Webhooks({
114+
secret: "mySecret",
115+
});
116+
const dataChunks: any[] = [];
117+
const middleware = createNodeMiddleware(webhooks);
118+
119+
const server = createServer((req, res) => {
120+
req.once("data", (chunk) => dataChunks.push(chunk));
121+
req.once("end", () => {
122+
// @ts-expect-error - TS2339: Property 'body' does not exist on type 'IncomingMessage'.
123+
req.body = JSON.parse(Buffer.concat(dataChunks));
124+
middleware(req, res);
125+
});
126+
}).listen();
127+
128+
webhooks.on("push", (event) => {
129+
expect(event.id).toBe("123e4567-e89b-12d3-a456-426655440000");
130+
});
131+
132+
// @ts-expect-error complains about { port } although it's included in returned AddressInfo interface
133+
const { port } = server.address();
134+
135+
const response = await fetch(
136+
`http://localhost:${port}/api/github/webhooks`,
137+
{
138+
method: "POST",
139+
headers: {
140+
"Content-Type": "application/json",
141+
"X-GitHub-Delivery": "123e4567-e89b-12d3-a456-426655440000",
142+
"X-GitHub-Event": "push",
143+
"X-Hub-Signature-256": signatureSha256,
144+
},
145+
body: pushEventPayload,
146+
},
147+
);
148+
149+
expect(response.status).toEqual(200);
150+
expect(await response.text()).toEqual("ok\n");
151+
152+
server.close();
153+
});
154+
155+
test("request.body is already an Object and has request.rawBody as Buffer (e.g. GCF)", async () => {
156+
expect.assertions(3);
157+
158+
const webhooks = new Webhooks({
159+
secret: "mySecret",
160+
});
161+
const dataChunks: any[] = [];
162+
const middleware = createNodeMiddleware(webhooks);
163+
164+
const server = createServer((req, res) => {
165+
req.once("data", (chunk) => dataChunks.push(chunk));
166+
req.once("end", () => {
167+
// @ts-expect-error - TS2339: Property 'rawBody' does not exist on type 'IncomingMessage'.
168+
req.rawBody = Buffer.concat(dataChunks);
169+
// @ts-expect-error - TS2339: Property 'body' does not exist on type 'IncomingMessage'.
170+
req.body = JSON.parse(req.rawBody);
171+
middleware(req, res);
172+
});
173+
}).listen();
174+
175+
webhooks.on("push", (event) => {
176+
expect(event.id).toBe("123e4567-e89b-12d3-a456-426655440000");
177+
});
178+
179+
// @ts-expect-error complains about { port } although it's included in returned AddressInfo interface
180+
const { port } = server.address();
181+
182+
const response = await fetch(
183+
`http://localhost:${port}/api/github/webhooks`,
184+
{
185+
method: "POST",
186+
headers: {
187+
"Content-Type": "application/json",
188+
"X-GitHub-Delivery": "123e4567-e89b-12d3-a456-426655440000",
189+
"X-GitHub-Event": "push",
190+
"X-Hub-Signature-256": signatureSha256,
191+
},
192+
body: pushEventPayload,
193+
},
194+
);
195+
196+
expect(response.status).toEqual(200);
197+
expect(await response.text()).toEqual("ok\n");
198+
199+
server.close();
200+
});
201+
110202
test("Handles invalid Content-Type", async () => {
111203
const webhooks = new Webhooks({
112204
secret: "mySecret",

0 commit comments

Comments
 (0)