@miiajs/multipart 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +17 -0
- package/dist/content-disposition.d.ts +19 -0
- package/dist/content-disposition.d.ts.map +1 -0
- package/dist/content-disposition.js +35 -0
- package/dist/content-disposition.js.map +1 -0
- package/dist/content-type.d.ts +16 -0
- package/dist/content-type.d.ts.map +1 -0
- package/dist/content-type.js +33 -0
- package/dist/content-type.js.map +1 -0
- package/dist/decorators.d.ts +36 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +174 -0
- package/dist/decorators.js.map +1 -0
- package/dist/form.d.ts +11 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +30 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/params.d.ts +11 -0
- package/dist/params.d.ts.map +1 -0
- package/dist/params.js +47 -0
- package/dist/params.js.map +1 -0
- package/dist/parser.d.ts +12 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +585 -0
- package/dist/parser.js.map +1 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
package/dist/parser.js
ADDED
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import { BadRequestException, HttpException, PayloadTooLargeException, UnsupportedMediaTypeException, } from '@miiajs/core';
|
|
2
|
+
import * as MP from 'multipasta';
|
|
3
|
+
import { parseContentDisposition } from './content-disposition.js';
|
|
4
|
+
import { parseContentType, parseMediaType } from './content-type.js';
|
|
5
|
+
const DEFAULT_MAX_FIELD_SIZE = 1024 * 1024;
|
|
6
|
+
const DEFAULT_MAX_FIELD_NAME_SIZE = 100;
|
|
7
|
+
export const DEFAULT_FIELDS_BUDGET = 64 * 1024;
|
|
8
|
+
/**
|
|
9
|
+
* Extra bytes held back on top of the boundary length. The closing delimiter is
|
|
10
|
+
* `\r\n--boundary--\r\n` (boundary + 8), the margin covers any disagreement
|
|
11
|
+
* between how we read the boundary and how multipasta reads it (quotes,
|
|
12
|
+
* quoted-pairs).
|
|
13
|
+
*/
|
|
14
|
+
const TAIL_MARGIN = 16;
|
|
15
|
+
const FALLBACK_BOUNDARY_LENGTH = 70;
|
|
16
|
+
/** RFC 7578 defaults for a part that declares no `Content-Type` of its own. */
|
|
17
|
+
const FIELD_MEDIA_TYPE = 'text/plain';
|
|
18
|
+
const FILE_MEDIA_TYPE = 'application/octet-stream';
|
|
19
|
+
const noop = () => { };
|
|
20
|
+
function createGate() {
|
|
21
|
+
let waiters = [];
|
|
22
|
+
return {
|
|
23
|
+
wait() {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
waiters.push(resolve);
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
wake() {
|
|
29
|
+
if (waiters.length === 0)
|
|
30
|
+
return;
|
|
31
|
+
const pending = waiters;
|
|
32
|
+
waiters = [];
|
|
33
|
+
for (const resolve of pending)
|
|
34
|
+
resolve();
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Streams a `multipart/form-data` request as an async iterator of parts.
|
|
40
|
+
*
|
|
41
|
+
* Backpressure is real: the source is only read when every queue the consumer
|
|
42
|
+
* can still drain is empty. Exactly one part is consumed at a time - moving the
|
|
43
|
+
* iterator forward abandons the previous part and errors its stream rather than
|
|
44
|
+
* closing it, so a half-read upload can never look complete.
|
|
45
|
+
*/
|
|
46
|
+
export function createPartStream(req, options = {}) {
|
|
47
|
+
const contentType = req.headers.get('content-type') ?? '';
|
|
48
|
+
const parsed = parseContentType(contentType);
|
|
49
|
+
if (parsed.mediaType !== 'multipart/form-data') {
|
|
50
|
+
throw new BadRequestException('Expected a multipart/form-data request body');
|
|
51
|
+
}
|
|
52
|
+
const maxFileSize = options.maxFileSize ?? Number.POSITIVE_INFINITY;
|
|
53
|
+
const maxFiles = options.maxFiles ?? Number.POSITIVE_INFINITY;
|
|
54
|
+
const maxFields = options.maxFields ?? Number.POSITIVE_INFINITY;
|
|
55
|
+
const maxFieldSize = options.maxFieldSize ?? DEFAULT_MAX_FIELD_SIZE;
|
|
56
|
+
const maxFieldNameSize = options.maxFieldNameSize ?? DEFAULT_MAX_FIELD_NAME_SIZE;
|
|
57
|
+
const fieldsBudget = options.fieldsBudget ?? DEFAULT_FIELDS_BUDGET;
|
|
58
|
+
const allowedTypes = normalizeAllowedTypes(options.allowedTypes);
|
|
59
|
+
const maxParts = Number.isFinite(maxFiles) && Number.isFinite(maxFields) ? maxFiles + maxFields : Number.POSITIVE_INFINITY;
|
|
60
|
+
const maxTotalSize = resolveMaxTotalSize(options.bodyLimit, maxFileSize, maxFiles, fieldsBudget);
|
|
61
|
+
const tailSize = (parsed.boundary?.length ?? FALLBACK_BOUNDARY_LENGTH) + TAIL_MARGIN;
|
|
62
|
+
const pending = [];
|
|
63
|
+
const partsGate = createGate();
|
|
64
|
+
const liveFiles = new Set();
|
|
65
|
+
let currentFile = null;
|
|
66
|
+
let failure = null;
|
|
67
|
+
let stopped = false;
|
|
68
|
+
let closed = false;
|
|
69
|
+
let advancing = false;
|
|
70
|
+
let doneReceived = false;
|
|
71
|
+
let sourceDone = false;
|
|
72
|
+
let released = false;
|
|
73
|
+
let fileCount = 0;
|
|
74
|
+
let fieldCount = 0;
|
|
75
|
+
let tail = null;
|
|
76
|
+
// ─── Source ────────────────────────────────────────────────────
|
|
77
|
+
const reader = (req.body ?? emptyStream()).getReader();
|
|
78
|
+
let pendingRead = null;
|
|
79
|
+
let filling = null;
|
|
80
|
+
/**
|
|
81
|
+
* The source is never cancelled and never drained: `cancel()` on a
|
|
82
|
+
* `Readable.toWeb` stream tears down the socket and kills the 413 the handler
|
|
83
|
+
* is about to write. We stop reading and let the adapter dispose of
|
|
84
|
+
* the connection.
|
|
85
|
+
*/
|
|
86
|
+
function releaseSource() {
|
|
87
|
+
if (released)
|
|
88
|
+
return;
|
|
89
|
+
released = true;
|
|
90
|
+
if (pendingRead)
|
|
91
|
+
pendingRead.then(noop, noop);
|
|
92
|
+
try {
|
|
93
|
+
reader.releaseLock();
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// an outstanding read still holds the lock; the adapter cleans up
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// ─── Failure latch ─────────────────────────────────────────────
|
|
100
|
+
// multipasta keeps calling `onError` and keeps buffering after the first
|
|
101
|
+
// failure, so the first error stops the bridge for good.
|
|
102
|
+
function fail(error) {
|
|
103
|
+
if (stopped)
|
|
104
|
+
return;
|
|
105
|
+
stopped = true;
|
|
106
|
+
failure = error;
|
|
107
|
+
for (const file of liveFiles)
|
|
108
|
+
errorFile(file, error);
|
|
109
|
+
liveFiles.clear();
|
|
110
|
+
partsGate.wake();
|
|
111
|
+
releaseSource();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* A throw out of `parser.write()` / `parser.end()`. multipasta 0.2.8 decodes
|
|
115
|
+
* an RFC 5987 `filename*` inside `write()`, so `filename*=UTF-8''%%%` raises a
|
|
116
|
+
* `URIError` from under the reading loop, which nothing awaits: unlatched it
|
|
117
|
+
* would hang the request and surface as an unhandled rejection. The header is
|
|
118
|
+
* the client's, so it reads as a malformed request.
|
|
119
|
+
*/
|
|
120
|
+
function failParsing(error) {
|
|
121
|
+
fail(error instanceof HttpException ? error : new BadRequestException('Malformed multipart part headers'));
|
|
122
|
+
}
|
|
123
|
+
function errorFile(file, error) {
|
|
124
|
+
if (file.error)
|
|
125
|
+
return;
|
|
126
|
+
file.error = error;
|
|
127
|
+
file.chunks.length = 0;
|
|
128
|
+
file.controller?.error(error);
|
|
129
|
+
file.gate.wake();
|
|
130
|
+
}
|
|
131
|
+
function limitExceeded(limit, message) {
|
|
132
|
+
return new PayloadTooLargeException(message, { limit });
|
|
133
|
+
}
|
|
134
|
+
// ─── Part metadata ─────────────────────────────────────────────
|
|
135
|
+
// The engine voids a `content-disposition` carrying a character above U+00FF,
|
|
136
|
+
// so the names - and with them the media type of a part that declares none -
|
|
137
|
+
// come from our own reading of the raw headers. The engine calls `isFile`
|
|
138
|
+
// once per part and hands the same `info` to `onFile`/`onField` straight
|
|
139
|
+
// after, so a single slot carries that reading across the three.
|
|
140
|
+
let readInfo = null;
|
|
141
|
+
let readMeta = { name: '', mediaType: FIELD_MEDIA_TYPE, named: false };
|
|
142
|
+
function metaOf(info) {
|
|
143
|
+
if (info !== readInfo) {
|
|
144
|
+
readInfo = info;
|
|
145
|
+
const { name, filename } = parseContentDisposition(info.headers['content-disposition']);
|
|
146
|
+
readMeta = {
|
|
147
|
+
name: name ?? '',
|
|
148
|
+
filename,
|
|
149
|
+
mediaType: mediaTypeOf(info.headers['content-type'], filename !== undefined),
|
|
150
|
+
named: name !== undefined || filename !== undefined,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return readMeta;
|
|
154
|
+
}
|
|
155
|
+
/** The engine's own rule - a filename, or a binary body - on our reading. */
|
|
156
|
+
const isFile = (info) => {
|
|
157
|
+
const meta = metaOf(info);
|
|
158
|
+
return meta.filename !== undefined || meta.mediaType === FILE_MEDIA_TYPE;
|
|
159
|
+
};
|
|
160
|
+
// ─── Parser callbacks ──────────────────────────────────────────
|
|
161
|
+
/**
|
|
162
|
+
* The engine only refuses an unnamed part when it managed to read
|
|
163
|
+
* `form-data` out of the disposition, so a part carrying no
|
|
164
|
+
* `content-disposition` at all - or one it read nothing from - arrives as a
|
|
165
|
+
* field named `''`. Ours is the only reading that sees it.
|
|
166
|
+
*/
|
|
167
|
+
function checkDisposition(meta) {
|
|
168
|
+
if (meta.named)
|
|
169
|
+
return true;
|
|
170
|
+
fail(new BadRequestException('Multipart part is missing a name'));
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
function checkName(name) {
|
|
174
|
+
if (name.length <= maxFieldNameSize)
|
|
175
|
+
return true;
|
|
176
|
+
fail(limitExceeded('maxFieldNameSize', 'Multipart part name is too long'));
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Runs at the head of a file part, before a byte of its body is read: the
|
|
181
|
+
* refusal is what saves the upload, so it cannot wait for the stream.
|
|
182
|
+
*/
|
|
183
|
+
function checkMediaType(mediaType) {
|
|
184
|
+
if (allowedTypes === null || matchesAllowedTypes(allowedTypes, mediaType))
|
|
185
|
+
return true;
|
|
186
|
+
fail(new UnsupportedMediaTypeException(`Media type ${mediaType} is not allowed`, {
|
|
187
|
+
mediaType,
|
|
188
|
+
allowed: allowedTypes,
|
|
189
|
+
}));
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
const onFile = (info) => {
|
|
193
|
+
const meta = metaOf(info);
|
|
194
|
+
if (stopped || !checkDisposition(meta) || !checkName(meta.name) || !checkMediaType(meta.mediaType))
|
|
195
|
+
return noop;
|
|
196
|
+
const file = {
|
|
197
|
+
chunks: [],
|
|
198
|
+
finished: false,
|
|
199
|
+
discarded: false,
|
|
200
|
+
error: null,
|
|
201
|
+
size: 0,
|
|
202
|
+
counted: false,
|
|
203
|
+
gate: createGate(),
|
|
204
|
+
controller: null,
|
|
205
|
+
};
|
|
206
|
+
liveFiles.add(file);
|
|
207
|
+
pending.push({ part: createFilePart(info, meta, file), file });
|
|
208
|
+
partsGate.wake();
|
|
209
|
+
return (chunk) => {
|
|
210
|
+
if (stopped)
|
|
211
|
+
return;
|
|
212
|
+
if (chunk === null) {
|
|
213
|
+
file.finished = true;
|
|
214
|
+
liveFiles.delete(file);
|
|
215
|
+
file.gate.wake();
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (file.discarded || file.error)
|
|
219
|
+
return;
|
|
220
|
+
// A part with `filename=""` and no bytes is an empty file input, not an
|
|
221
|
+
// upload - the count only moves once the part actually carries data.
|
|
222
|
+
if (!file.counted) {
|
|
223
|
+
file.counted = true;
|
|
224
|
+
fileCount++;
|
|
225
|
+
if (fileCount > maxFiles)
|
|
226
|
+
return fail(limitExceeded('maxFiles', 'Too many files'));
|
|
227
|
+
}
|
|
228
|
+
file.size += chunk.length;
|
|
229
|
+
if (file.size > maxFileSize)
|
|
230
|
+
return fail(limitExceeded('maxFileSize', 'File is too large'));
|
|
231
|
+
file.chunks.push(chunk);
|
|
232
|
+
file.gate.wake();
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
const onField = (info, value) => {
|
|
236
|
+
const meta = metaOf(info);
|
|
237
|
+
if (stopped || !checkDisposition(meta) || !checkName(meta.name))
|
|
238
|
+
return;
|
|
239
|
+
fieldCount++;
|
|
240
|
+
if (fieldCount > maxFields)
|
|
241
|
+
return fail(limitExceeded('maxFields', 'Too many fields'));
|
|
242
|
+
pending.push({
|
|
243
|
+
part: { type: 'field', name: meta.name, value: MP.decodeField(info, value), headers: info.headers },
|
|
244
|
+
file: null,
|
|
245
|
+
});
|
|
246
|
+
partsGate.wake();
|
|
247
|
+
};
|
|
248
|
+
const onDone = () => {
|
|
249
|
+
doneReceived = true;
|
|
250
|
+
partsGate.wake();
|
|
251
|
+
for (const file of liveFiles)
|
|
252
|
+
file.gate.wake();
|
|
253
|
+
};
|
|
254
|
+
const parser = MP.make({
|
|
255
|
+
headers: { 'content-type': contentType },
|
|
256
|
+
isFile,
|
|
257
|
+
maxFieldSize,
|
|
258
|
+
maxParts,
|
|
259
|
+
maxTotalSize,
|
|
260
|
+
onField,
|
|
261
|
+
onFile,
|
|
262
|
+
onError: (error) => fail(mapMultipartError(error)),
|
|
263
|
+
onDone,
|
|
264
|
+
});
|
|
265
|
+
// `InvalidBoundary` is reported from `make()` itself, before a single byte is read.
|
|
266
|
+
if (failure)
|
|
267
|
+
throw failure;
|
|
268
|
+
// ─── Reading loop ──────────────────────────────────────────────
|
|
269
|
+
function needsMore() {
|
|
270
|
+
if (stopped || sourceDone || doneReceived)
|
|
271
|
+
return false;
|
|
272
|
+
if (pending.length > 0)
|
|
273
|
+
return false;
|
|
274
|
+
if (currentFile && !currentFile.discarded && currentFile.chunks.length > 0)
|
|
275
|
+
return false;
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Holds back the last `boundary + 16` bytes until the next chunk arrives, so
|
|
280
|
+
* the closing `--boundary--` can never be split across two `write()` calls -
|
|
281
|
+
* multipasta 0.2.8 mistakes a one-byte remainder for a header block.
|
|
282
|
+
*/
|
|
283
|
+
function feed(chunk) {
|
|
284
|
+
if (stopped || doneReceived)
|
|
285
|
+
return;
|
|
286
|
+
let buf = chunk;
|
|
287
|
+
if (tail !== null && tail.length > 0) {
|
|
288
|
+
const merged = new Uint8Array(tail.length + chunk.length);
|
|
289
|
+
merged.set(tail);
|
|
290
|
+
merged.set(chunk, tail.length);
|
|
291
|
+
buf = merged;
|
|
292
|
+
}
|
|
293
|
+
if (buf.length <= tailSize) {
|
|
294
|
+
tail = buf;
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const cut = buf.length - tailSize;
|
|
298
|
+
tail = buf.subarray(cut);
|
|
299
|
+
parser.write(buf.subarray(0, cut));
|
|
300
|
+
}
|
|
301
|
+
function flushTail() {
|
|
302
|
+
const rest = tail;
|
|
303
|
+
tail = null;
|
|
304
|
+
if (rest && rest.length > 0 && !stopped && !doneReceived)
|
|
305
|
+
parser.write(rest);
|
|
306
|
+
}
|
|
307
|
+
async function runFill() {
|
|
308
|
+
while (needsMore()) {
|
|
309
|
+
let result;
|
|
310
|
+
try {
|
|
311
|
+
const read = reader.read();
|
|
312
|
+
pendingRead = read;
|
|
313
|
+
result = await read;
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
// Source errors travel untouched: the adapter body ceiling rejects with
|
|
317
|
+
// an `Error` named `PayloadTooLargeError` that core maps to 413 by name.
|
|
318
|
+
if (!released)
|
|
319
|
+
fail(error);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
pendingRead = null;
|
|
324
|
+
}
|
|
325
|
+
if (stopped)
|
|
326
|
+
return;
|
|
327
|
+
if (result.done) {
|
|
328
|
+
sourceDone = true;
|
|
329
|
+
try {
|
|
330
|
+
flushTail();
|
|
331
|
+
if (!stopped && !doneReceived)
|
|
332
|
+
parser.end();
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
failParsing(error);
|
|
336
|
+
}
|
|
337
|
+
if (!stopped && !doneReceived)
|
|
338
|
+
fail(new BadRequestException('Malformed multipart/form-data body'));
|
|
339
|
+
partsGate.wake();
|
|
340
|
+
for (const file of liveFiles)
|
|
341
|
+
file.gate.wake();
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
feed(result.value);
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
failParsing(error);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Single-flight: both the iterator and a part stream wake the gate, and two
|
|
355
|
+
* concurrent `reader.read()` calls would feed the parser in microtask order.
|
|
356
|
+
* The re-check on release covers a consumer that drained a queue while the
|
|
357
|
+
* loop was already on its way out.
|
|
358
|
+
*/
|
|
359
|
+
function fill() {
|
|
360
|
+
if (filling || stopped || sourceDone || doneReceived)
|
|
361
|
+
return;
|
|
362
|
+
filling = runFill().finally(() => {
|
|
363
|
+
filling = null;
|
|
364
|
+
if (needsMore())
|
|
365
|
+
fill();
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
// ─── Parts ─────────────────────────────────────────────────────
|
|
369
|
+
function createFilePart(info, meta, file) {
|
|
370
|
+
const stream = new ReadableStream({
|
|
371
|
+
start: (controller) => {
|
|
372
|
+
file.controller = controller;
|
|
373
|
+
},
|
|
374
|
+
pull: async (controller) => {
|
|
375
|
+
for (;;) {
|
|
376
|
+
if (file.error)
|
|
377
|
+
throw file.error;
|
|
378
|
+
const chunk = file.chunks.shift();
|
|
379
|
+
if (chunk !== undefined) {
|
|
380
|
+
controller.enqueue(chunk);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (file.finished) {
|
|
384
|
+
controller.close();
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (failure)
|
|
388
|
+
throw failure;
|
|
389
|
+
fill();
|
|
390
|
+
await file.gate.wait();
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
cancel: () => {
|
|
394
|
+
file.discarded = true;
|
|
395
|
+
file.chunks.length = 0;
|
|
396
|
+
liveFiles.delete(file);
|
|
397
|
+
file.gate.wake();
|
|
398
|
+
fill();
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
// Cached the way `ctx.json()` caches the body: the stream is drained once,
|
|
402
|
+
// and a second call hands back the same bytes instead of an empty read of a
|
|
403
|
+
// stream that is already closed.
|
|
404
|
+
let bytes = null;
|
|
405
|
+
return {
|
|
406
|
+
type: 'file',
|
|
407
|
+
name: meta.name,
|
|
408
|
+
filename: meta.filename,
|
|
409
|
+
mediaType: meta.mediaType,
|
|
410
|
+
headers: info.headers,
|
|
411
|
+
stream,
|
|
412
|
+
bytes: () => (bytes ??= readAll(stream)),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Moving on abandons whatever is left of the previous part. The stream is
|
|
417
|
+
* errored, not closed: a closed stream would hand the consumer a silently
|
|
418
|
+
* truncated file.
|
|
419
|
+
*
|
|
420
|
+
* Every part the consumer did not read to the end is errored, including one
|
|
421
|
+
* whose bytes had all arrived already - a small part delivered in a single
|
|
422
|
+
* chunk sits complete in the stream's queue, and letting that one through
|
|
423
|
+
* would make the contract depend on how the body happened to be split.
|
|
424
|
+
*/
|
|
425
|
+
function discardCurrent() {
|
|
426
|
+
const file = currentFile;
|
|
427
|
+
currentFile = null;
|
|
428
|
+
if (!file || file.discarded || file.error)
|
|
429
|
+
return;
|
|
430
|
+
file.discarded = true;
|
|
431
|
+
liveFiles.delete(file);
|
|
432
|
+
errorFile(file, new Error('Multipart part was abandoned before it was fully consumed'));
|
|
433
|
+
}
|
|
434
|
+
const iterator = {
|
|
435
|
+
[Symbol.asyncIterator]() {
|
|
436
|
+
return iterator;
|
|
437
|
+
},
|
|
438
|
+
async next() {
|
|
439
|
+
if (closed)
|
|
440
|
+
return { value: undefined, done: true };
|
|
441
|
+
// Two `next()` calls in flight would both wait on the same gate while the
|
|
442
|
+
// first part they woke for keeps the reading loop from asking for more -
|
|
443
|
+
// a deadlock. Parts are consumed one at a time, so say so.
|
|
444
|
+
if (advancing) {
|
|
445
|
+
throw new Error('Multipart parts are consumed one at a time - await the previous next() before the next one');
|
|
446
|
+
}
|
|
447
|
+
advancing = true;
|
|
448
|
+
try {
|
|
449
|
+
discardCurrent();
|
|
450
|
+
for (;;) {
|
|
451
|
+
if (failure)
|
|
452
|
+
throw failure;
|
|
453
|
+
const entry = pending.shift();
|
|
454
|
+
if (entry) {
|
|
455
|
+
currentFile = entry.file;
|
|
456
|
+
// A part stream issues its first `pull` when it is built, which can
|
|
457
|
+
// be long before the consumer receives the part. That pull may find
|
|
458
|
+
// the loop mid-flight, and its `fill()` is dropped; the loop's own
|
|
459
|
+
// re-check then still sees this entry queued and stops reading. Only
|
|
460
|
+
// handing the part over releases the queue, so the retry belongs here.
|
|
461
|
+
fill();
|
|
462
|
+
return { value: entry.part, done: false };
|
|
463
|
+
}
|
|
464
|
+
if (doneReceived || sourceDone) {
|
|
465
|
+
releaseSource();
|
|
466
|
+
return { value: undefined, done: true };
|
|
467
|
+
}
|
|
468
|
+
fill();
|
|
469
|
+
await partsGate.wait();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
finally {
|
|
473
|
+
advancing = false;
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
async return() {
|
|
477
|
+
closed = true;
|
|
478
|
+
discardCurrent();
|
|
479
|
+
stopped = true;
|
|
480
|
+
releaseSource();
|
|
481
|
+
return { value: undefined, done: true };
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
return iterator;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Media type of a part, without its parameters.
|
|
488
|
+
*
|
|
489
|
+
* RFC 7578 leaves `Content-Type` optional on a part and reads an absent one as
|
|
490
|
+
* `text/plain`, or as `application/octet-stream` for a part carrying a
|
|
491
|
+
* filename. The engine applies that same rule to its own reading of
|
|
492
|
+
* `content-disposition`, so a file with a non latin-1 filename and no header of
|
|
493
|
+
* its own comes back as text; ours reads the filename from the raw header.
|
|
494
|
+
*/
|
|
495
|
+
function mediaTypeOf(header, hasFilename) {
|
|
496
|
+
const fallback = hasFilename ? FILE_MEDIA_TYPE : FIELD_MEDIA_TYPE;
|
|
497
|
+
// A part with two `content-type` headers arrives as an array; the engine
|
|
498
|
+
// reads nothing out of that shape either.
|
|
499
|
+
if (typeof header !== 'string')
|
|
500
|
+
return fallback;
|
|
501
|
+
return parseMediaType(header) || fallback;
|
|
502
|
+
}
|
|
503
|
+
/** Read the way a part's media type is, so `Image/PNG` in the options matches. */
|
|
504
|
+
function normalizeAllowedTypes(allowed) {
|
|
505
|
+
return allowed === undefined ? null : allowed.map((entry) => entry.trim().toLowerCase());
|
|
506
|
+
}
|
|
507
|
+
function matchesAllowedTypes(allowed, mediaType) {
|
|
508
|
+
for (const entry of allowed) {
|
|
509
|
+
// `*/*` is the conventional spelling of "anything", not a subtype wildcard.
|
|
510
|
+
if (entry === mediaType || entry === '*/*')
|
|
511
|
+
return true;
|
|
512
|
+
if (entry.endsWith('/*') && mediaType.startsWith(entry.slice(0, -1)))
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
function resolveMaxTotalSize(bodyLimit, maxFileSize, maxFiles, fieldsBudget) {
|
|
518
|
+
if (bodyLimit !== undefined)
|
|
519
|
+
return bodyLimit;
|
|
520
|
+
if (Number.isFinite(maxFileSize) && Number.isFinite(maxFiles))
|
|
521
|
+
return maxFileSize * maxFiles + fieldsBudget;
|
|
522
|
+
return Number.POSITIVE_INFINITY;
|
|
523
|
+
}
|
|
524
|
+
function mapMultipartError(error) {
|
|
525
|
+
switch (error._tag) {
|
|
526
|
+
case 'ReachedLimit':
|
|
527
|
+
switch (error.limit) {
|
|
528
|
+
case 'MaxParts':
|
|
529
|
+
return new PayloadTooLargeException('Too many parts', { limit: 'maxParts' });
|
|
530
|
+
case 'MaxTotalSize':
|
|
531
|
+
return new PayloadTooLargeException('Multipart body is too large', { limit: 'bodyLimit' });
|
|
532
|
+
case 'MaxFieldSize':
|
|
533
|
+
return new PayloadTooLargeException('Field value is too large', { limit: 'maxFieldSize' });
|
|
534
|
+
default:
|
|
535
|
+
return new PayloadTooLargeException('Multipart part is too large', { limit: 'maxPartSize' });
|
|
536
|
+
}
|
|
537
|
+
case 'InvalidBoundary':
|
|
538
|
+
return new BadRequestException('Missing or invalid multipart boundary');
|
|
539
|
+
case 'BadHeaders':
|
|
540
|
+
return new BadRequestException('Malformed multipart part headers');
|
|
541
|
+
case 'InvalidDisposition':
|
|
542
|
+
return new BadRequestException('Multipart part is missing a name');
|
|
543
|
+
default:
|
|
544
|
+
return new BadRequestException('Malformed multipart/form-data body');
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function emptyStream() {
|
|
548
|
+
return new ReadableStream({
|
|
549
|
+
start(controller) {
|
|
550
|
+
controller.close();
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
async function readAll(stream) {
|
|
555
|
+
const reader = stream.getReader();
|
|
556
|
+
const chunks = [];
|
|
557
|
+
let total = 0;
|
|
558
|
+
try {
|
|
559
|
+
for (;;) {
|
|
560
|
+
const { done, value } = await reader.read();
|
|
561
|
+
if (done)
|
|
562
|
+
break;
|
|
563
|
+
chunks.push(value);
|
|
564
|
+
total += value.length;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
finally {
|
|
568
|
+
try {
|
|
569
|
+
reader.releaseLock();
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
// reader already released by the stream
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (chunks.length === 1)
|
|
576
|
+
return chunks[0];
|
|
577
|
+
const out = new Uint8Array(total);
|
|
578
|
+
let offset = 0;
|
|
579
|
+
for (const chunk of chunks) {
|
|
580
|
+
out.set(chunk, offset);
|
|
581
|
+
offset += chunk.length;
|
|
582
|
+
}
|
|
583
|
+
return out;
|
|
584
|
+
}
|
|
585
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { RequestContext } from '@miiajs/core';
|
|
2
|
+
export interface MultipartOptions {
|
|
3
|
+
/** Max bytes per file part, counted by the bridge. Default: `Infinity`. */
|
|
4
|
+
maxFileSize?: number;
|
|
5
|
+
/** Max number of non-empty file parts. Default: `Infinity`. */
|
|
6
|
+
maxFiles?: number;
|
|
7
|
+
/** Max number of field parts. Default: `Infinity`. */
|
|
8
|
+
maxFields?: number;
|
|
9
|
+
/** Max bytes of a single field value. Default: 1 MiB. */
|
|
10
|
+
maxFieldSize?: number;
|
|
11
|
+
/** Max length of a part name. Default: 100. */
|
|
12
|
+
maxFieldNameSize?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Media types a file part may carry - an exact `image/png` or a subtype
|
|
15
|
+
* wildcard `image/*`. Entries are read the way a part's media type is:
|
|
16
|
+
* trimmed and lower-cased. Default: anything.
|
|
17
|
+
*
|
|
18
|
+
* Only **file** parts are checked. RFC 7578 reads a part that declares no
|
|
19
|
+
* `Content-Type` as `text/plain`, so a list like `['image/png']` would
|
|
20
|
+
* otherwise reject the form's ordinary text fields; a file declaring nothing
|
|
21
|
+
* is compared as `application/octet-stream`. An empty list allows no file at
|
|
22
|
+
* all.
|
|
23
|
+
*
|
|
24
|
+
* The check runs at the start of a part, before its body is read, and a part
|
|
25
|
+
* outside the list ends the request with a `415`. The header is written by
|
|
26
|
+
* the client, so this is an early refusal, not proof of what the bytes are.
|
|
27
|
+
*/
|
|
28
|
+
allowedTypes?: string[];
|
|
29
|
+
/** Budget added to the derived body limit to cover field parts. Default: 64 KiB. */
|
|
30
|
+
fieldsBudget?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Explicit ceiling for the whole body. When omitted it is derived from
|
|
33
|
+
* `maxFileSize * maxFiles + fieldsBudget` if both are finite, otherwise the
|
|
34
|
+
* adapter ceiling is the only limit.
|
|
35
|
+
*/
|
|
36
|
+
bodyLimit?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface FilePart {
|
|
39
|
+
readonly type: 'file';
|
|
40
|
+
readonly name: string;
|
|
41
|
+
readonly filename?: string;
|
|
42
|
+
readonly mediaType: string;
|
|
43
|
+
readonly headers: Record<string, string | string[]>;
|
|
44
|
+
readonly stream: ReadableStream<Uint8Array>;
|
|
45
|
+
bytes(): Promise<Uint8Array>;
|
|
46
|
+
}
|
|
47
|
+
export interface FieldPart {
|
|
48
|
+
readonly type: 'field';
|
|
49
|
+
readonly name: string;
|
|
50
|
+
readonly value: string;
|
|
51
|
+
readonly headers: Record<string, string | string[]>;
|
|
52
|
+
}
|
|
53
|
+
export type MultipartPart = FilePart | FieldPart;
|
|
54
|
+
export interface FormResult {
|
|
55
|
+
files: Record<string, File[]>;
|
|
56
|
+
fields: Record<string, string>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Request context of a route carrying `@Multipart`. Both members are attached
|
|
60
|
+
* by that decorator's middleware - on a route without it they are `undefined`.
|
|
61
|
+
*/
|
|
62
|
+
export interface MultipartContext extends RequestContext {
|
|
63
|
+
readonly parts: AsyncIterableIterator<MultipartPart>;
|
|
64
|
+
form<T = FormResult>(): Promise<T>;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=types.d.ts.map
|