1 | import * as http from "node:http";
|
2 | import { Buffer } from "node:buffer";
|
3 | import { JSONParseError, SignatureValidationFailed } from "./exceptions.js";
|
4 | import * as Types from "./types.js";
|
5 | import validateSignature from "./validate-signature.js";
|
6 |
|
7 | export type Request = http.IncomingMessage & { body: any };
|
8 | export type Response = http.ServerResponse;
|
9 | export type NextCallback = (err?: Error) => void;
|
10 |
|
11 | export type Middleware = (
|
12 | req: Request,
|
13 | res: Response,
|
14 | next: NextCallback,
|
15 | ) => void | Promise<void>;
|
16 |
|
17 | function isValidBody(body?: any): body is string | Buffer {
|
18 | return (body && typeof body === "string") || Buffer.isBuffer(body);
|
19 | }
|
20 |
|
21 | const readRequestBody = async (req: http.IncomingMessage): Promise<Buffer> => {
|
22 | const chunks: Buffer[] = [];
|
23 | for await (const chunk of req) {
|
24 | chunks.push(chunk as Buffer);
|
25 | }
|
26 | return Buffer.concat(chunks);
|
27 | };
|
28 |
|
29 | export default function middleware(config: Types.MiddlewareConfig): Middleware {
|
30 | if (!config.channelSecret) {
|
31 | throw new Error("no channel secret");
|
32 | }
|
33 |
|
34 | const secret = config.channelSecret;
|
35 |
|
36 | const _middleware: Middleware = async (req, res, next) => {
|
37 |
|
38 |
|
39 | const signature = req.headers[
|
40 | Types.LINE_SIGNATURE_HTTP_HEADER_NAME
|
41 | ] as string;
|
42 |
|
43 | if (!signature) {
|
44 | next(new SignatureValidationFailed("no signature"));
|
45 | return;
|
46 | }
|
47 |
|
48 | const body = await (async (): Promise<string | Buffer> => {
|
49 | if (isValidBody((req as any).rawBody)) {
|
50 |
|
51 | return (req as any).rawBody;
|
52 | } else if (isValidBody(req.body)) {
|
53 | return req.body;
|
54 | } else {
|
55 |
|
56 | const rawBody = await readRequestBody(req);
|
57 | if (isValidBody(rawBody)) {
|
58 | return rawBody;
|
59 | } else {
|
60 | throw new JSONParseError("Invalid body", { raw: rawBody });
|
61 | }
|
62 | }
|
63 | })();
|
64 |
|
65 | if (!validateSignature(body, secret, signature)) {
|
66 | next(
|
67 | new SignatureValidationFailed("signature validation failed", {
|
68 | signature,
|
69 | }),
|
70 | );
|
71 | return;
|
72 | }
|
73 |
|
74 | const strBody = Buffer.isBuffer(body) ? body.toString() : body;
|
75 |
|
76 | try {
|
77 | req.body = JSON.parse(strBody);
|
78 | next();
|
79 | } catch (err) {
|
80 | const { message } = err;
|
81 |
|
82 | next(new JSONParseError(message, { raw: strBody }));
|
83 | }
|
84 | };
|
85 | return (req, res, next): void => {
|
86 | (<Promise<void>>_middleware(req, res, next)).catch(next);
|
87 | };
|
88 | }
|