-
-
Notifications
You must be signed in to change notification settings - Fork 393
Expand file tree
/
Copy pathindex.js
More file actions
126 lines (112 loc) · 3.27 KB
/
index.js
File metadata and controls
126 lines (112 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors.
// SPDX-License-Identifier: MIT
import BusBoy from "@fastify/busboy";
import { createError } from "@middy/util";
const mimePattern =
/^multipart\/form-data; boundary=[a-zA-Z0-9-]{1,70}(; ?charset=[\w-]+)?$/i;
const fieldnamePattern = /(.+)\[(.*)]$/;
const defaults = {
// busboy options as per documentation: https://www.npmjs.com/package/busboy#busboy-methods
busboy: {},
charset: "utf8",
disableContentTypeCheck: false,
disableContentTypeError: false,
};
const httpMultipartBodyParserMiddleware = (opts = {}) => {
const options = { ...defaults, ...opts };
const httpMultipartBodyParserMiddlewareBefore = (request) => {
const { headers, body } = request.event;
const contentType = headers?.["content-type"] ?? headers?.["Content-Type"];
if (!options.disableContentTypeCheck && !mimePattern.test(contentType)) {
if (options.disableContentTypeError) {
return;
}
throw createError(415, "Unsupported Media Type", {
cause: {
package: "@middy/http-multipart-body-parser",
data: contentType,
},
});
}
if (typeof body === "undefined") {
throw createError(
422,
"Invalid or malformed multipart/form-data was provided",
{ cause: { package: "@middy/http-multipart-body-parser", data: body } },
);
}
return parseMultipartData(request.event, options)
.then((multipartData) => {
request.event.body = multipartData;
})
.catch((err) => {
// UnprocessableEntity
throw createError(
422,
"Invalid or malformed multipart/form-data was provided",
{
cause: {
package: "@middy/http-multipart-body-parser",
data: body,
message: err.message,
},
},
);
});
};
return {
before: httpMultipartBodyParserMiddlewareBefore,
};
};
const parseMultipartData = (event, options) => {
const multipartData = Object.create(null);
const charset = event.isBase64Encoded ? "base64" : options.charset;
// header must be lowercase (content-type)
const busboy = BusBoy({
...options.busboy,
headers: {
"content-type":
event.headers?.["content-type"] ?? event.headers?.["Content-Type"],
},
});
return new Promise((resolve, reject) => {
busboy
.on("file", (fieldname, file, filename, encoding, mimetype) => {
const attachment = {
filename,
mimetype,
encoding,
};
const chunks = [];
file.on("data", (data) => {
chunks.push(data);
});
file.on("end", () => {
attachment.truncated = file.truncated;
attachment.content = Buffer.concat(chunks);
if (!multipartData[fieldname]) {
multipartData[fieldname] = attachment;
} else {
const current = multipartData[fieldname];
multipartData[fieldname] = [attachment].concat(current);
}
});
})
.on("field", (fieldname, value) => {
const matches = fieldname.match(fieldnamePattern);
if (!matches) {
multipartData[fieldname] = value;
} else {
if (!multipartData[matches[1]]) {
multipartData[matches[1]] = [];
}
multipartData[matches[1]].push(value);
}
})
.on("finish", () => resolve(multipartData))
.on("error", (e) => reject(e));
busboy.write(event.body, charset);
busboy.end();
});
};
export default httpMultipartBodyParserMiddleware;