Skip to content

Commit 3d4ec1a

Browse files
author
Hyunje Jun
authored
Handle rawBody provided by Google Cloud Functions (#101)
* Refactor middleware for readability * Add rawBody support for Google Cloud Functions Resolve #6.
1 parent 6309020 commit 3d4ec1a

File tree

3 files changed

+37
-9
lines changed

3 files changed

+37
-9
lines changed

lib/middleware.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export type Middleware = (
1414
next: NextCallback,
1515
) => void;
1616

17+
function isValidBody(body?: any): body is string | Buffer {
18+
return (body && typeof body === "string") || Buffer.isBuffer(body);
19+
}
20+
1721
export default function middleware(config: Types.MiddlewareConfig): Middleware {
1822
if (!config.channelSecret) {
1923
throw new Error("no channel secret");
@@ -31,7 +35,20 @@ export default function middleware(config: Types.MiddlewareConfig): Middleware {
3135
return;
3236
}
3337

34-
const validate = (body: string | Buffer) => {
38+
let getBody: Promise<string | Buffer>;
39+
if (isValidBody((req as any).rawBody)) {
40+
// rawBody is provided in Google Cloud Functions and others
41+
getBody = Promise.resolve((req as any).rawBody);
42+
} else if (isValidBody(req.body)) {
43+
getBody = Promise.resolve(req.body);
44+
} else {
45+
// body may not be parsed yet, parse it to a buffer
46+
getBody = new Promise(resolve => {
47+
raw({ type: "*/*" })(req as any, res as any, () => resolve(req.body));
48+
});
49+
}
50+
51+
getBody.then(body => {
3552
if (!validateSignature(body, secret, signature)) {
3653
next(
3754
new SignatureValidationFailed(
@@ -50,13 +67,6 @@ export default function middleware(config: Types.MiddlewareConfig): Middleware {
5067
} catch (err) {
5168
next(new JSONParseError(err.message, strBody));
5269
}
53-
};
54-
55-
if (typeof req.body === "string" || Buffer.isBuffer(req.body)) {
56-
return validate(req.body);
57-
}
58-
59-
// if body is not parsed yet, parse it to a buffer
60-
raw({ type: "*/*" })(req as any, res as any, () => validate(req.body));
70+
});
6171
};
6272
}

test/helpers/test-server.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ function listen(port: number, middleware?: express.RequestHandler) {
1919
bodyParser.text({ type: "*/*" })(req, res, next);
2020
} else if (req.path === "/mid-buffer") {
2121
bodyParser.raw({ type: "*/*" })(req, res, next);
22+
} else if (req.path === "/mid-rawbody") {
23+
bodyParser.raw({ type: "*/*" })(req, res, err => {
24+
if (err) return next(err);
25+
(req as any).rawBody = req.body;
26+
delete req.body;
27+
next();
28+
});
2229
} else if (req.path === "/mid-json") {
2330
bodyParser.json({ type: "*/*" })(req, res, next);
2431
} else {

test/middleware.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ describe("middleware", () => {
7272
});
7373
});
7474

75+
it("succeed with pre-parsed buffer in rawBody", () => {
76+
return http()
77+
.post(`/mid-rawbody`, {
78+
events: [webhook],
79+
})
80+
.then(() => {
81+
const req = getRecentReq();
82+
deepEqual(req.body.events, [webhook]);
83+
});
84+
});
85+
7586
it("fails on wrong signature", () => {
7687
return http({
7788
"X-Line-Signature": "qeDy61PbQK+aO97Bs8zjbFgYjQxFruGd13pfXPQoBRU=",

0 commit comments

Comments
 (0)