@jsenv/server 15.5.3 → 16.0.1
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/README.md +79 -86
- package/dist/html/4xx.html +196 -0
- package/dist/html/500.html +17 -0
- package/dist/html/route_inspector.html +2435 -0
- package/dist/js/formidable.js +17 -0
- package/dist/js/vendors.js +2001 -0
- package/dist/js/ws.js +6863 -0
- package/dist/jsenv_server.js +8829 -0
- package/package.json +19 -9
- package/src/content_negotiation/pick_content_version.js +63 -0
- package/src/{fetch_filesystem.js → filesystem/fetch_filesystem.js} +57 -72
- package/src/{serve_directory.js → filesystem/serve_directory.js} +1 -2
- package/src/interfacing_with_node/from_node_request.js +455 -0
- package/src/interfacing_with_node/get_observable_value_type.js +43 -0
- package/src/interfacing_with_node/observable.js +91 -75
- package/src/interfacing_with_node/observable_from.js +34 -0
- package/src/interfacing_with_node/observable_from_file_handle.js +30 -0
- package/src/interfacing_with_node/observable_from_node_stream.js +44 -46
- package/src/interfacing_with_node/observable_from_node_web_readable_stream.js +32 -0
- package/src/interfacing_with_node/observable_from_promise.js +13 -0
- package/src/interfacing_with_node/write_node_response.js +80 -35
- package/src/internal/colorizeResponseStatus.js +2 -0
- package/src/internal/headers_composition.js +42 -15
- package/src/internal/multiple-header.js +27 -12
- package/src/internal/object_composition.js +2 -2
- package/src/internal/response_composition.js +16 -3
- package/src/main.js +15 -11
- package/src/progressive_response.js +38 -0
- package/src/replace_placeholder_in_html.js +12 -0
- package/src/request_body_handling.js +22 -0
- package/src/router/router.js +878 -0
- package/src/server_events.js +431 -0
- package/src/service_controller.js +79 -73
- package/src/services/autoreload_on_server_restart/client/autoreload_on_server_restart.js +125 -0
- package/src/services/autoreload_on_server_restart/jsenv_service_autoreload_on_server_restart.js +45 -0
- package/src/services/cors/jsenv_service_cors.js +10 -18
- package/src/services/default_body_4xx_5xx/client/4xx.html +65 -0
- package/src/services/default_body_4xx_5xx/jsenv_service_default_body_4xx_5xx.js +82 -0
- package/src/services/error_handler/client/500.html +17 -0
- package/src/services/error_handler/jsenv_service_error_handler.js +47 -28
- package/src/services/internal_client_files/jsenv_service_internal_client_files.js +34 -0
- package/src/services/open_file/jsenv_service_open_file.js +37 -0
- package/src/services/route_inspector/client/code_block.js +215 -0
- package/src/services/route_inspector/client/route_inspector.html +1363 -0
- package/src/services/route_inspector/jsenv_service_route_inspector.js +39 -0
- package/src/start_server.js +575 -607
- package/src/web_socket_response.js +53 -0
- package/src/from_fetch_response.js +0 -15
- package/src/interfacing_with_node/body.js +0 -81
- package/src/interfacing_with_node/request_factory.js +0 -125
- package/src/readRequestBody.js +0 -32
- package/src/server_timing/timing_measure.js +0 -26
- package/src/service_composition/path_to_regexp.js +0 -436
- package/src/service_composition/routing.js +0 -31
- package/src/sse/sse_room.js +0 -313
|
@@ -0,0 +1,2001 @@
|
|
|
1
|
+
import { PassThrough, Transform, Stream } from "node:stream";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import fsPromises from "node:fs/promises";
|
|
5
|
+
import { EventEmitter } from "node:events";
|
|
6
|
+
import { StringDecoder } from "node:string_decoder";
|
|
7
|
+
import require$$0 from "node:domain";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import require$$1, { createHash } from "node:crypto";
|
|
10
|
+
|
|
11
|
+
/* eslint-disable no-underscore-dangle */
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PersistentFile extends EventEmitter {
|
|
15
|
+
constructor({ filepath, newFilename, originalFilename, mimetype, hashAlgorithm }) {
|
|
16
|
+
super();
|
|
17
|
+
|
|
18
|
+
this.lastModifiedDate = null;
|
|
19
|
+
Object.assign(this, { filepath, newFilename, originalFilename, mimetype, hashAlgorithm });
|
|
20
|
+
|
|
21
|
+
this.size = 0;
|
|
22
|
+
this._writeStream = null;
|
|
23
|
+
|
|
24
|
+
if (typeof this.hashAlgorithm === 'string') {
|
|
25
|
+
this.hash = require$$1.createHash(this.hashAlgorithm);
|
|
26
|
+
} else {
|
|
27
|
+
this.hash = null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
open() {
|
|
32
|
+
this._writeStream = fs.createWriteStream(this.filepath);
|
|
33
|
+
this._writeStream.on('error', (err) => {
|
|
34
|
+
this.emit('error', err);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
toJSON() {
|
|
39
|
+
const json = {
|
|
40
|
+
size: this.size,
|
|
41
|
+
filepath: this.filepath,
|
|
42
|
+
newFilename: this.newFilename,
|
|
43
|
+
mimetype: this.mimetype,
|
|
44
|
+
mtime: this.lastModifiedDate,
|
|
45
|
+
length: this.length,
|
|
46
|
+
originalFilename: this.originalFilename,
|
|
47
|
+
};
|
|
48
|
+
if (this.hash && this.hash !== '') {
|
|
49
|
+
json.hash = this.hash;
|
|
50
|
+
}
|
|
51
|
+
return json;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
toString() {
|
|
55
|
+
return `PersistentFile: ${this.newFilename}, Original: ${this.originalFilename}, Path: ${this.filepath}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
write(buffer, cb) {
|
|
59
|
+
if (this.hash) {
|
|
60
|
+
this.hash.update(buffer);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this._writeStream.closed) {
|
|
64
|
+
cb();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this._writeStream.write(buffer, () => {
|
|
69
|
+
this.lastModifiedDate = new Date();
|
|
70
|
+
this.size += buffer.length;
|
|
71
|
+
this.emit('progress', this.size);
|
|
72
|
+
cb();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
end(cb) {
|
|
77
|
+
if (this.hash) {
|
|
78
|
+
this.hash = this.hash.digest('hex');
|
|
79
|
+
}
|
|
80
|
+
this._writeStream.end(() => {
|
|
81
|
+
this.emit('end');
|
|
82
|
+
cb();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
destroy() {
|
|
87
|
+
this._writeStream.destroy();
|
|
88
|
+
const filepath = this.filepath;
|
|
89
|
+
setTimeout(function () {
|
|
90
|
+
fs.unlink(filepath, () => {});
|
|
91
|
+
}, 1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* eslint-disable no-underscore-dangle */
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class VolatileFile extends EventEmitter {
|
|
99
|
+
constructor({ filepath, newFilename, originalFilename, mimetype, hashAlgorithm, createFileWriteStream }) {
|
|
100
|
+
super();
|
|
101
|
+
|
|
102
|
+
this.lastModifiedDate = null;
|
|
103
|
+
Object.assign(this, { filepath, newFilename, originalFilename, mimetype, hashAlgorithm, createFileWriteStream });
|
|
104
|
+
|
|
105
|
+
this.size = 0;
|
|
106
|
+
this._writeStream = null;
|
|
107
|
+
|
|
108
|
+
if (typeof this.hashAlgorithm === 'string') {
|
|
109
|
+
this.hash = createHash(this.hashAlgorithm);
|
|
110
|
+
} else {
|
|
111
|
+
this.hash = null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
open() {
|
|
116
|
+
this._writeStream = this.createFileWriteStream(this);
|
|
117
|
+
this._writeStream.on('error', (err) => {
|
|
118
|
+
this.emit('error', err);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
destroy() {
|
|
123
|
+
this._writeStream.destroy();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
toJSON() {
|
|
127
|
+
const json = {
|
|
128
|
+
size: this.size,
|
|
129
|
+
newFilename: this.newFilename,
|
|
130
|
+
length: this.length,
|
|
131
|
+
originalFilename: this.originalFilename,
|
|
132
|
+
mimetype: this.mimetype,
|
|
133
|
+
};
|
|
134
|
+
if (this.hash && this.hash !== '') {
|
|
135
|
+
json.hash = this.hash;
|
|
136
|
+
}
|
|
137
|
+
return json;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
toString() {
|
|
141
|
+
return `VolatileFile: ${this.originalFilename}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
write(buffer, cb) {
|
|
145
|
+
if (this.hash) {
|
|
146
|
+
this.hash.update(buffer);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (this._writeStream.closed || this._writeStream.destroyed) {
|
|
150
|
+
cb();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this._writeStream.write(buffer, () => {
|
|
155
|
+
this.size += buffer.length;
|
|
156
|
+
this.emit('progress', this.size);
|
|
157
|
+
cb();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
end(cb) {
|
|
162
|
+
if (this.hash) {
|
|
163
|
+
this.hash = this.hash.digest('hex');
|
|
164
|
+
}
|
|
165
|
+
this._writeStream.end(() => {
|
|
166
|
+
this.emit('end');
|
|
167
|
+
cb();
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for(var r=256,n=[];r--;)n[r]=(r+256).toString(16).substring(1);function hexoid(r){r=r||16;var t="",o=0;return function(){if(!t||256===o){for(t="",o=(1+r)/2|0;o--;)t+=n[256*Math.random()|0];t=t.substring(o=0,r-2);}return t+n[o++]}}
|
|
173
|
+
|
|
174
|
+
function getDefaultExportFromCjs$1 (x) {
|
|
175
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
var once = {exports: {}};
|
|
179
|
+
|
|
180
|
+
var wrappy_1$1;
|
|
181
|
+
var hasRequiredWrappy$1;
|
|
182
|
+
|
|
183
|
+
function requireWrappy$1 () {
|
|
184
|
+
if (hasRequiredWrappy$1) return wrappy_1$1;
|
|
185
|
+
hasRequiredWrappy$1 = 1;
|
|
186
|
+
// Returns a wrapper function that returns a wrapped callback
|
|
187
|
+
// The wrapper function should do some stuff, and return a
|
|
188
|
+
// presumably different callback function.
|
|
189
|
+
// This makes sure that own properties are retained, so that
|
|
190
|
+
// decorations and such are not lost along the way.
|
|
191
|
+
wrappy_1$1 = wrappy;
|
|
192
|
+
function wrappy (fn, cb) {
|
|
193
|
+
if (fn && cb) return wrappy(fn)(cb)
|
|
194
|
+
|
|
195
|
+
if (typeof fn !== 'function')
|
|
196
|
+
throw new TypeError('need wrapper function')
|
|
197
|
+
|
|
198
|
+
Object.keys(fn).forEach(function (k) {
|
|
199
|
+
wrapper[k] = fn[k];
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return wrapper
|
|
203
|
+
|
|
204
|
+
function wrapper() {
|
|
205
|
+
var args = new Array(arguments.length);
|
|
206
|
+
for (var i = 0; i < args.length; i++) {
|
|
207
|
+
args[i] = arguments[i];
|
|
208
|
+
}
|
|
209
|
+
var ret = fn.apply(this, args);
|
|
210
|
+
var cb = args[args.length-1];
|
|
211
|
+
if (typeof ret === 'function' && ret !== cb) {
|
|
212
|
+
Object.keys(cb).forEach(function (k) {
|
|
213
|
+
ret[k] = cb[k];
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return ret
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return wrappy_1$1;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
var hasRequiredOnce;
|
|
223
|
+
|
|
224
|
+
function requireOnce () {
|
|
225
|
+
if (hasRequiredOnce) return once.exports;
|
|
226
|
+
hasRequiredOnce = 1;
|
|
227
|
+
var wrappy = requireWrappy$1();
|
|
228
|
+
once.exports = wrappy(once$1);
|
|
229
|
+
once.exports.strict = wrappy(onceStrict);
|
|
230
|
+
|
|
231
|
+
once$1.proto = once$1(function () {
|
|
232
|
+
Object.defineProperty(Function.prototype, 'once', {
|
|
233
|
+
value: function () {
|
|
234
|
+
return once$1(this)
|
|
235
|
+
},
|
|
236
|
+
configurable: true
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
Object.defineProperty(Function.prototype, 'onceStrict', {
|
|
240
|
+
value: function () {
|
|
241
|
+
return onceStrict(this)
|
|
242
|
+
},
|
|
243
|
+
configurable: true
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
function once$1 (fn) {
|
|
248
|
+
var f = function () {
|
|
249
|
+
if (f.called) return f.value
|
|
250
|
+
f.called = true;
|
|
251
|
+
return f.value = fn.apply(this, arguments)
|
|
252
|
+
};
|
|
253
|
+
f.called = false;
|
|
254
|
+
return f
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function onceStrict (fn) {
|
|
258
|
+
var f = function () {
|
|
259
|
+
if (f.called)
|
|
260
|
+
throw new Error(f.onceError)
|
|
261
|
+
f.called = true;
|
|
262
|
+
return f.value = fn.apply(this, arguments)
|
|
263
|
+
};
|
|
264
|
+
var name = fn.name || 'Function wrapped with `once`';
|
|
265
|
+
f.onceError = name + " shouldn't be called more than once";
|
|
266
|
+
f.called = false;
|
|
267
|
+
return f
|
|
268
|
+
}
|
|
269
|
+
return once.exports;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
var onceExports = requireOnce();
|
|
273
|
+
var __jsenv_default_import__$1 = /*@__PURE__*/getDefaultExportFromCjs$1(onceExports);
|
|
274
|
+
|
|
275
|
+
onceExports.strict;
|
|
276
|
+
|
|
277
|
+
function getDefaultExportFromCjs (x) {
|
|
278
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
var wrappy_1;
|
|
282
|
+
var hasRequiredWrappy;
|
|
283
|
+
|
|
284
|
+
function requireWrappy () {
|
|
285
|
+
if (hasRequiredWrappy) return wrappy_1;
|
|
286
|
+
hasRequiredWrappy = 1;
|
|
287
|
+
// Returns a wrapper function that returns a wrapped callback
|
|
288
|
+
// The wrapper function should do some stuff, and return a
|
|
289
|
+
// presumably different callback function.
|
|
290
|
+
// This makes sure that own properties are retained, so that
|
|
291
|
+
// decorations and such are not lost along the way.
|
|
292
|
+
wrappy_1 = wrappy;
|
|
293
|
+
function wrappy (fn, cb) {
|
|
294
|
+
if (fn && cb) return wrappy(fn)(cb)
|
|
295
|
+
|
|
296
|
+
if (typeof fn !== 'function')
|
|
297
|
+
throw new TypeError('need wrapper function')
|
|
298
|
+
|
|
299
|
+
Object.keys(fn).forEach(function (k) {
|
|
300
|
+
wrapper[k] = fn[k];
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
return wrapper
|
|
304
|
+
|
|
305
|
+
function wrapper() {
|
|
306
|
+
var args = new Array(arguments.length);
|
|
307
|
+
for (var i = 0; i < args.length; i++) {
|
|
308
|
+
args[i] = arguments[i];
|
|
309
|
+
}
|
|
310
|
+
var ret = fn.apply(this, args);
|
|
311
|
+
var cb = args[args.length-1];
|
|
312
|
+
if (typeof ret === 'function' && ret !== cb) {
|
|
313
|
+
Object.keys(cb).forEach(function (k) {
|
|
314
|
+
ret[k] = cb[k];
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
return ret
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return wrappy_1;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
var raw;
|
|
324
|
+
var hasRequiredRaw;
|
|
325
|
+
|
|
326
|
+
function requireRaw () {
|
|
327
|
+
if (hasRequiredRaw) return raw;
|
|
328
|
+
hasRequiredRaw = 1;
|
|
329
|
+
|
|
330
|
+
var domain; // The domain module is executed on demand
|
|
331
|
+
var hasSetImmediate = typeof setImmediate === "function";
|
|
332
|
+
|
|
333
|
+
// Use the fastest means possible to execute a task in its own turn, with
|
|
334
|
+
// priority over other events including network IO events in Node.js.
|
|
335
|
+
//
|
|
336
|
+
// An exception thrown by a task will permanently interrupt the processing of
|
|
337
|
+
// subsequent tasks. The higher level `asap` function ensures that if an
|
|
338
|
+
// exception is thrown by a task, that the task queue will continue flushing as
|
|
339
|
+
// soon as possible, but if you use `rawAsap` directly, you are responsible to
|
|
340
|
+
// either ensure that no exceptions are thrown from your task, or to manually
|
|
341
|
+
// call `rawAsap.requestFlush` if an exception is thrown.
|
|
342
|
+
raw = rawAsap;
|
|
343
|
+
function rawAsap(task) {
|
|
344
|
+
if (!queue.length) {
|
|
345
|
+
requestFlush();
|
|
346
|
+
flushing = true;
|
|
347
|
+
}
|
|
348
|
+
// Avoids a function call
|
|
349
|
+
queue[queue.length] = task;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
var queue = [];
|
|
353
|
+
// Once a flush has been requested, no further calls to `requestFlush` are
|
|
354
|
+
// necessary until the next `flush` completes.
|
|
355
|
+
var flushing = false;
|
|
356
|
+
// The position of the next task to execute in the task queue. This is
|
|
357
|
+
// preserved between calls to `flush` so that it can be resumed if
|
|
358
|
+
// a task throws an exception.
|
|
359
|
+
var index = 0;
|
|
360
|
+
// If a task schedules additional tasks recursively, the task queue can grow
|
|
361
|
+
// unbounded. To prevent memory excaustion, the task queue will periodically
|
|
362
|
+
// truncate already-completed tasks.
|
|
363
|
+
var capacity = 1024;
|
|
364
|
+
|
|
365
|
+
// The flush function processes all tasks that have been scheduled with
|
|
366
|
+
// `rawAsap` unless and until one of those tasks throws an exception.
|
|
367
|
+
// If a task throws an exception, `flush` ensures that its state will remain
|
|
368
|
+
// consistent and will resume where it left off when called again.
|
|
369
|
+
// However, `flush` does not make any arrangements to be called again if an
|
|
370
|
+
// exception is thrown.
|
|
371
|
+
function flush() {
|
|
372
|
+
while (index < queue.length) {
|
|
373
|
+
var currentIndex = index;
|
|
374
|
+
// Advance the index before calling the task. This ensures that we will
|
|
375
|
+
// begin flushing on the next task the task throws an error.
|
|
376
|
+
index = index + 1;
|
|
377
|
+
queue[currentIndex].call();
|
|
378
|
+
// Prevent leaking memory for long chains of recursive calls to `asap`.
|
|
379
|
+
// If we call `asap` within tasks scheduled by `asap`, the queue will
|
|
380
|
+
// grow, but to avoid an O(n) walk for every task we execute, we don't
|
|
381
|
+
// shift tasks off the queue after they have been executed.
|
|
382
|
+
// Instead, we periodically shift 1024 tasks off the queue.
|
|
383
|
+
if (index > capacity) {
|
|
384
|
+
// Manually shift all values starting at the index back to the
|
|
385
|
+
// beginning of the queue.
|
|
386
|
+
for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) {
|
|
387
|
+
queue[scan] = queue[scan + index];
|
|
388
|
+
}
|
|
389
|
+
queue.length -= index;
|
|
390
|
+
index = 0;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
queue.length = 0;
|
|
394
|
+
index = 0;
|
|
395
|
+
flushing = false;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
rawAsap.requestFlush = requestFlush;
|
|
399
|
+
function requestFlush() {
|
|
400
|
+
// Ensure flushing is not bound to any domain.
|
|
401
|
+
// It is not sufficient to exit the domain, because domains exist on a stack.
|
|
402
|
+
// To execute code outside of any domain, the following dance is necessary.
|
|
403
|
+
var parentDomain = process.domain;
|
|
404
|
+
if (parentDomain) {
|
|
405
|
+
if (!domain) {
|
|
406
|
+
// Lazy execute the domain module.
|
|
407
|
+
// Only employed if the user elects to use domains.
|
|
408
|
+
domain = require$$0;
|
|
409
|
+
}
|
|
410
|
+
domain.active = process.domain = null;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// `setImmediate` is slower that `process.nextTick`, but `process.nextTick`
|
|
414
|
+
// cannot handle recursion.
|
|
415
|
+
// `requestFlush` will only be called recursively from `asap.js`, to resume
|
|
416
|
+
// flushing after an error is thrown into a domain.
|
|
417
|
+
// Conveniently, `setImmediate` was introduced in the same version
|
|
418
|
+
// `process.nextTick` started throwing recursion errors.
|
|
419
|
+
if (flushing && hasSetImmediate) {
|
|
420
|
+
setImmediate(flush);
|
|
421
|
+
} else {
|
|
422
|
+
process.nextTick(flush);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (parentDomain) {
|
|
426
|
+
domain.active = process.domain = parentDomain;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return raw;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
var asap_1;
|
|
433
|
+
var hasRequiredAsap;
|
|
434
|
+
|
|
435
|
+
function requireAsap () {
|
|
436
|
+
if (hasRequiredAsap) return asap_1;
|
|
437
|
+
hasRequiredAsap = 1;
|
|
438
|
+
|
|
439
|
+
var rawAsap = requireRaw();
|
|
440
|
+
var freeTasks = [];
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Calls a task as soon as possible after returning, in its own event, with
|
|
444
|
+
* priority over IO events. An exception thrown in a task can be handled by
|
|
445
|
+
* `process.on("uncaughtException") or `domain.on("error")`, but will otherwise
|
|
446
|
+
* crash the process. If the error is handled, all subsequent tasks will
|
|
447
|
+
* resume.
|
|
448
|
+
*
|
|
449
|
+
* @param {{call}} task A callable object, typically a function that takes no
|
|
450
|
+
* arguments.
|
|
451
|
+
*/
|
|
452
|
+
asap_1 = asap;
|
|
453
|
+
function asap(task) {
|
|
454
|
+
var rawTask;
|
|
455
|
+
if (freeTasks.length) {
|
|
456
|
+
rawTask = freeTasks.pop();
|
|
457
|
+
} else {
|
|
458
|
+
rawTask = new RawTask();
|
|
459
|
+
}
|
|
460
|
+
rawTask.task = task;
|
|
461
|
+
rawTask.domain = process.domain;
|
|
462
|
+
rawAsap(rawTask);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function RawTask() {
|
|
466
|
+
this.task = null;
|
|
467
|
+
this.domain = null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
RawTask.prototype.call = function () {
|
|
471
|
+
if (this.domain) {
|
|
472
|
+
this.domain.enter();
|
|
473
|
+
}
|
|
474
|
+
var threw = true;
|
|
475
|
+
try {
|
|
476
|
+
this.task.call();
|
|
477
|
+
threw = false;
|
|
478
|
+
// If the task throws an exception (presumably) Node.js restores the
|
|
479
|
+
// domain stack for the next event.
|
|
480
|
+
if (this.domain) {
|
|
481
|
+
this.domain.exit();
|
|
482
|
+
}
|
|
483
|
+
} finally {
|
|
484
|
+
// We use try/finally and a threw flag to avoid messing up stack traces
|
|
485
|
+
// when we catch and release errors.
|
|
486
|
+
if (threw) {
|
|
487
|
+
// In Node.js, uncaught exceptions are considered fatal errors.
|
|
488
|
+
// Re-throw them to interrupt flushing!
|
|
489
|
+
// Ensure that flushing continues if an uncaught exception is
|
|
490
|
+
// suppressed listening process.on("uncaughtException") or
|
|
491
|
+
// domain.on("error").
|
|
492
|
+
rawAsap.requestFlush();
|
|
493
|
+
}
|
|
494
|
+
// If the task threw an error, we do not want to exit the domain here.
|
|
495
|
+
// Exiting the domain would prevent the domain from catching the error.
|
|
496
|
+
this.task = null;
|
|
497
|
+
this.domain = null;
|
|
498
|
+
freeTasks.push(this);
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
return asap_1;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
var dezalgo_1;
|
|
505
|
+
var hasRequiredDezalgo;
|
|
506
|
+
|
|
507
|
+
function requireDezalgo () {
|
|
508
|
+
if (hasRequiredDezalgo) return dezalgo_1;
|
|
509
|
+
hasRequiredDezalgo = 1;
|
|
510
|
+
var wrappy = requireWrappy();
|
|
511
|
+
dezalgo_1 = wrappy(dezalgo);
|
|
512
|
+
|
|
513
|
+
var asap = requireAsap();
|
|
514
|
+
|
|
515
|
+
function dezalgo (cb) {
|
|
516
|
+
var sync = true;
|
|
517
|
+
asap(function () {
|
|
518
|
+
sync = false;
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
return function zalgoSafe() {
|
|
522
|
+
var args = arguments;
|
|
523
|
+
var me = this;
|
|
524
|
+
if (sync)
|
|
525
|
+
asap(function() {
|
|
526
|
+
cb.apply(me, args);
|
|
527
|
+
});
|
|
528
|
+
else
|
|
529
|
+
cb.apply(me, args);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return dezalgo_1;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
var dezalgoExports = requireDezalgo();
|
|
536
|
+
var __jsenv_default_import__ = /*@__PURE__*/getDefaultExportFromCjs(dezalgoExports);
|
|
537
|
+
|
|
538
|
+
class OctetStreamParser extends PassThrough {
|
|
539
|
+
constructor(options = {}) {
|
|
540
|
+
super();
|
|
541
|
+
this.globalOptions = { ...options };
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/* eslint-disable no-underscore-dangle */
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
const octetStreamType = 'octet-stream';
|
|
549
|
+
// the `options` is also available through the `options` / `formidable.options`
|
|
550
|
+
async function plugin$3(formidable, options) {
|
|
551
|
+
// the `this` context is always formidable, as the first argument of a plugin
|
|
552
|
+
// but this allows us to customize/test each plugin
|
|
553
|
+
|
|
554
|
+
/* istanbul ignore next */
|
|
555
|
+
const self = this || formidable;
|
|
556
|
+
|
|
557
|
+
if (/octet-stream/i.test(self.headers['content-type'])) {
|
|
558
|
+
await init$2.call(self, self, options);
|
|
559
|
+
}
|
|
560
|
+
return self;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Note that it's a good practice (but it's up to you) to use the `this.options` instead
|
|
564
|
+
// of the passed `options` (second) param, because when you decide
|
|
565
|
+
// to test the plugin you can pass custom `this` context to it (and so `this.options`)
|
|
566
|
+
async function init$2(_self, _opts) {
|
|
567
|
+
this.type = octetStreamType;
|
|
568
|
+
const originalFilename = this.headers['x-file-name'];
|
|
569
|
+
const mimetype = this.headers['content-type'];
|
|
570
|
+
|
|
571
|
+
const thisPart = {
|
|
572
|
+
originalFilename,
|
|
573
|
+
mimetype,
|
|
574
|
+
};
|
|
575
|
+
const newFilename = this._getNewName(thisPart);
|
|
576
|
+
const filepath = this._joinDirectoryName(newFilename);
|
|
577
|
+
const file = await this._newFile({
|
|
578
|
+
newFilename,
|
|
579
|
+
filepath,
|
|
580
|
+
originalFilename,
|
|
581
|
+
mimetype,
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
this.emit('fileBegin', originalFilename, file);
|
|
585
|
+
file.open();
|
|
586
|
+
this.openedFiles.push(file);
|
|
587
|
+
this._flushing += 1;
|
|
588
|
+
|
|
589
|
+
this._parser = new OctetStreamParser(this.options);
|
|
590
|
+
|
|
591
|
+
// Keep track of writes that haven't finished so we don't emit the file before it's done being written
|
|
592
|
+
let outstandingWrites = 0;
|
|
593
|
+
|
|
594
|
+
this._parser.on('data', (buffer) => {
|
|
595
|
+
this.pause();
|
|
596
|
+
outstandingWrites += 1;
|
|
597
|
+
|
|
598
|
+
file.write(buffer, () => {
|
|
599
|
+
outstandingWrites -= 1;
|
|
600
|
+
this.resume();
|
|
601
|
+
|
|
602
|
+
if (this.ended) {
|
|
603
|
+
this._parser.emit('doneWritingFile');
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
this._parser.on('end', () => {
|
|
609
|
+
this._flushing -= 1;
|
|
610
|
+
this.ended = true;
|
|
611
|
+
|
|
612
|
+
const done = () => {
|
|
613
|
+
file.end(() => {
|
|
614
|
+
this.emit('file', 'file', file);
|
|
615
|
+
this._maybeEnd();
|
|
616
|
+
});
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
if (outstandingWrites === 0) {
|
|
620
|
+
done();
|
|
621
|
+
} else {
|
|
622
|
+
this._parser.once('doneWritingFile', done);
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
return this;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/* eslint-disable no-underscore-dangle */
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
// This is a buffering parser, have a look at StreamingQuerystring.js for a streaming parser
|
|
633
|
+
class QuerystringParser extends Transform {
|
|
634
|
+
constructor(options = {}) {
|
|
635
|
+
super({ readableObjectMode: true });
|
|
636
|
+
this.globalOptions = { ...options };
|
|
637
|
+
this.buffer = '';
|
|
638
|
+
this.bufferLength = 0;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
_transform(buffer, encoding, callback) {
|
|
642
|
+
this.buffer += buffer.toString('ascii');
|
|
643
|
+
this.bufferLength = this.buffer.length;
|
|
644
|
+
callback();
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
_flush(callback) {
|
|
648
|
+
const fields = new URLSearchParams(this.buffer);
|
|
649
|
+
for (const [key, value] of fields) {
|
|
650
|
+
this.push({
|
|
651
|
+
key,
|
|
652
|
+
value,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
this.buffer = '';
|
|
656
|
+
callback();
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/* eslint-disable no-underscore-dangle */
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
const querystringType = 'urlencoded';
|
|
664
|
+
// the `options` is also available through the `this.options` / `formidable.options`
|
|
665
|
+
function plugin$2(formidable, options) {
|
|
666
|
+
// the `this` context is always formidable, as the first argument of a plugin
|
|
667
|
+
// but this allows us to customize/test each plugin
|
|
668
|
+
|
|
669
|
+
/* istanbul ignore next */
|
|
670
|
+
const self = this || formidable;
|
|
671
|
+
|
|
672
|
+
if (/urlencoded/i.test(self.headers['content-type'])) {
|
|
673
|
+
init$1.call(self, self, options);
|
|
674
|
+
}
|
|
675
|
+
return self;
|
|
676
|
+
}
|
|
677
|
+
// Note that it's a good practice (but it's up to you) to use the `this.options` instead
|
|
678
|
+
// of the passed `options` (second) param, because when you decide
|
|
679
|
+
// to test the plugin you can pass custom `this` context to it (and so `this.options`)
|
|
680
|
+
function init$1(_self, _opts) {
|
|
681
|
+
this.type = querystringType;
|
|
682
|
+
|
|
683
|
+
const parser = new QuerystringParser(this.options);
|
|
684
|
+
|
|
685
|
+
parser.on('data', ({ key, value }) => {
|
|
686
|
+
this.emit('field', key, value);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
parser.once('end', () => {
|
|
690
|
+
this.ended = true;
|
|
691
|
+
this._maybeEnd();
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
this._parser = parser;
|
|
695
|
+
|
|
696
|
+
return this;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const missingPlugin = 1000;
|
|
700
|
+
const pluginFunction = 1001;
|
|
701
|
+
const aborted = 1002;
|
|
702
|
+
const noParser = 1003;
|
|
703
|
+
const uninitializedParser = 1004;
|
|
704
|
+
const filenameNotString = 1005;
|
|
705
|
+
const maxFieldsSizeExceeded = 1006;
|
|
706
|
+
const maxFieldsExceeded = 1007;
|
|
707
|
+
const smallerThanMinFileSize = 1008;
|
|
708
|
+
const biggerThanTotalMaxFileSize = 1009;
|
|
709
|
+
const noEmptyFiles = 1010;
|
|
710
|
+
const missingContentType = 1011;
|
|
711
|
+
const malformedMultipart = 1012;
|
|
712
|
+
const missingMultipartBoundary = 1013;
|
|
713
|
+
const unknownTransferEncoding = 1014;
|
|
714
|
+
const maxFilesExceeded = 1015;
|
|
715
|
+
const biggerThanMaxFileSize = 1016;
|
|
716
|
+
const pluginFailed = 1017;
|
|
717
|
+
const cannotCreateDir = 1018;
|
|
718
|
+
|
|
719
|
+
const FormidableError = class extends Error {
|
|
720
|
+
constructor(message, internalCode, httpCode = 500) {
|
|
721
|
+
super(message);
|
|
722
|
+
this.code = internalCode;
|
|
723
|
+
this.httpCode = httpCode;
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
/* eslint-disable no-fallthrough */
|
|
728
|
+
/* eslint-disable no-bitwise */
|
|
729
|
+
/* eslint-disable no-plusplus */
|
|
730
|
+
/* eslint-disable no-underscore-dangle */
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
let s = 0;
|
|
734
|
+
const STATE = {
|
|
735
|
+
PARSER_UNINITIALIZED: s++,
|
|
736
|
+
START: s++,
|
|
737
|
+
START_BOUNDARY: s++,
|
|
738
|
+
HEADER_FIELD_START: s++,
|
|
739
|
+
HEADER_FIELD: s++,
|
|
740
|
+
HEADER_VALUE_START: s++,
|
|
741
|
+
HEADER_VALUE: s++,
|
|
742
|
+
HEADER_VALUE_ALMOST_DONE: s++,
|
|
743
|
+
HEADERS_ALMOST_DONE: s++,
|
|
744
|
+
PART_DATA_START: s++,
|
|
745
|
+
PART_DATA: s++,
|
|
746
|
+
PART_END: s++,
|
|
747
|
+
END: s++,
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
let f = 1;
|
|
751
|
+
const FBOUNDARY = { PART_BOUNDARY: f, LAST_BOUNDARY: (f *= 2) };
|
|
752
|
+
|
|
753
|
+
const LF = 10;
|
|
754
|
+
const CR = 13;
|
|
755
|
+
const SPACE = 32;
|
|
756
|
+
const HYPHEN = 45;
|
|
757
|
+
const COLON = 58;
|
|
758
|
+
const A = 97;
|
|
759
|
+
const Z = 122;
|
|
760
|
+
|
|
761
|
+
function lower(c) {
|
|
762
|
+
return c | 0x20;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const STATES = {};
|
|
766
|
+
|
|
767
|
+
Object.keys(STATE).forEach((stateName) => {
|
|
768
|
+
STATES[stateName] = STATE[stateName];
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
class MultipartParser extends Transform {
|
|
772
|
+
constructor(options = {}) {
|
|
773
|
+
super({ readableObjectMode: true });
|
|
774
|
+
this.boundary = null;
|
|
775
|
+
this.boundaryChars = null;
|
|
776
|
+
this.lookbehind = null;
|
|
777
|
+
this.bufferLength = 0;
|
|
778
|
+
this.state = STATE.PARSER_UNINITIALIZED;
|
|
779
|
+
|
|
780
|
+
this.globalOptions = { ...options };
|
|
781
|
+
this.index = null;
|
|
782
|
+
this.flags = 0;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
_endUnexpected() {
|
|
786
|
+
return new FormidableError(
|
|
787
|
+
`MultipartParser.end(): stream ended unexpectedly: ${this.explain()}`,
|
|
788
|
+
malformedMultipart,
|
|
789
|
+
400,
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
_flush(done) {
|
|
794
|
+
if (
|
|
795
|
+
(this.state === STATE.HEADER_FIELD_START && this.index === 0) ||
|
|
796
|
+
(this.state === STATE.PART_DATA && this.index === this.boundary.length)
|
|
797
|
+
) {
|
|
798
|
+
this._handleCallback('partEnd');
|
|
799
|
+
this._handleCallback('end');
|
|
800
|
+
done();
|
|
801
|
+
} else if (this.state !== STATE.END) {
|
|
802
|
+
done(this._endUnexpected());
|
|
803
|
+
} else {
|
|
804
|
+
done();
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
initWithBoundary(str) {
|
|
809
|
+
this.boundary = Buffer.from(`\r\n--${str}`);
|
|
810
|
+
this.lookbehind = Buffer.alloc(this.boundary.length + 8);
|
|
811
|
+
this.state = STATE.START;
|
|
812
|
+
this.boundaryChars = {};
|
|
813
|
+
|
|
814
|
+
for (let i = 0; i < this.boundary.length; i++) {
|
|
815
|
+
this.boundaryChars[this.boundary[i]] = true;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// eslint-disable-next-line max-params
|
|
820
|
+
_handleCallback(name, buf, start, end) {
|
|
821
|
+
if (start !== undefined && start === end) {
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
this.push({ name, buffer: buf, start, end });
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// eslint-disable-next-line max-statements
|
|
828
|
+
_transform(buffer, _, done) {
|
|
829
|
+
let i = 0;
|
|
830
|
+
let prevIndex = this.index;
|
|
831
|
+
let { index, state, flags } = this;
|
|
832
|
+
const { lookbehind, boundary, boundaryChars } = this;
|
|
833
|
+
const boundaryLength = boundary.length;
|
|
834
|
+
const boundaryEnd = boundaryLength - 1;
|
|
835
|
+
this.bufferLength = buffer.length;
|
|
836
|
+
let c = null;
|
|
837
|
+
let cl = null;
|
|
838
|
+
|
|
839
|
+
const setMark = (name, idx) => {
|
|
840
|
+
this[`${name}Mark`] = typeof idx === 'number' ? idx : i;
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
const clearMarkSymbol = (name) => {
|
|
844
|
+
delete this[`${name}Mark`];
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
const dataCallback = (name, shouldClear) => {
|
|
848
|
+
const markSymbol = `${name}Mark`;
|
|
849
|
+
if (!(markSymbol in this)) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (!shouldClear) {
|
|
854
|
+
this._handleCallback(name, buffer, this[markSymbol], buffer.length);
|
|
855
|
+
setMark(name, 0);
|
|
856
|
+
} else {
|
|
857
|
+
this._handleCallback(name, buffer, this[markSymbol], i);
|
|
858
|
+
clearMarkSymbol(name);
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
for (i = 0; i < this.bufferLength; i++) {
|
|
863
|
+
c = buffer[i];
|
|
864
|
+
switch (state) {
|
|
865
|
+
case STATE.PARSER_UNINITIALIZED:
|
|
866
|
+
done(this._endUnexpected());
|
|
867
|
+
return;
|
|
868
|
+
case STATE.START:
|
|
869
|
+
index = 0;
|
|
870
|
+
state = STATE.START_BOUNDARY;
|
|
871
|
+
case STATE.START_BOUNDARY:
|
|
872
|
+
if (index === boundary.length - 2) {
|
|
873
|
+
if (c === HYPHEN) {
|
|
874
|
+
flags |= FBOUNDARY.LAST_BOUNDARY;
|
|
875
|
+
} else if (c !== CR) {
|
|
876
|
+
done(this._endUnexpected());
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
index++;
|
|
880
|
+
break;
|
|
881
|
+
} else if (index - 1 === boundary.length - 2) {
|
|
882
|
+
if (flags & FBOUNDARY.LAST_BOUNDARY && c === HYPHEN) {
|
|
883
|
+
this._handleCallback('end');
|
|
884
|
+
state = STATE.END;
|
|
885
|
+
flags = 0;
|
|
886
|
+
} else if (!(flags & FBOUNDARY.LAST_BOUNDARY) && c === LF) {
|
|
887
|
+
index = 0;
|
|
888
|
+
this._handleCallback('partBegin');
|
|
889
|
+
state = STATE.HEADER_FIELD_START;
|
|
890
|
+
} else {
|
|
891
|
+
done(this._endUnexpected());
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
break;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (c !== boundary[index + 2]) {
|
|
898
|
+
index = -2;
|
|
899
|
+
}
|
|
900
|
+
if (c === boundary[index + 2]) {
|
|
901
|
+
index++;
|
|
902
|
+
}
|
|
903
|
+
break;
|
|
904
|
+
case STATE.HEADER_FIELD_START:
|
|
905
|
+
state = STATE.HEADER_FIELD;
|
|
906
|
+
setMark('headerField');
|
|
907
|
+
index = 0;
|
|
908
|
+
case STATE.HEADER_FIELD:
|
|
909
|
+
if (c === CR) {
|
|
910
|
+
clearMarkSymbol('headerField');
|
|
911
|
+
state = STATE.HEADERS_ALMOST_DONE;
|
|
912
|
+
break;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
index++;
|
|
916
|
+
if (c === HYPHEN) {
|
|
917
|
+
break;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if (c === COLON) {
|
|
921
|
+
if (index === 1) {
|
|
922
|
+
// empty header field
|
|
923
|
+
done(this._endUnexpected());
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
dataCallback('headerField', true);
|
|
927
|
+
state = STATE.HEADER_VALUE_START;
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
cl = lower(c);
|
|
932
|
+
if (cl < A || cl > Z) {
|
|
933
|
+
done(this._endUnexpected());
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
break;
|
|
937
|
+
case STATE.HEADER_VALUE_START:
|
|
938
|
+
if (c === SPACE) {
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
setMark('headerValue');
|
|
943
|
+
state = STATE.HEADER_VALUE;
|
|
944
|
+
case STATE.HEADER_VALUE:
|
|
945
|
+
if (c === CR) {
|
|
946
|
+
dataCallback('headerValue', true);
|
|
947
|
+
this._handleCallback('headerEnd');
|
|
948
|
+
state = STATE.HEADER_VALUE_ALMOST_DONE;
|
|
949
|
+
}
|
|
950
|
+
break;
|
|
951
|
+
case STATE.HEADER_VALUE_ALMOST_DONE:
|
|
952
|
+
if (c !== LF) {
|
|
953
|
+
done(this._endUnexpected());
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
state = STATE.HEADER_FIELD_START;
|
|
957
|
+
break;
|
|
958
|
+
case STATE.HEADERS_ALMOST_DONE:
|
|
959
|
+
if (c !== LF) {
|
|
960
|
+
done(this._endUnexpected());
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
this._handleCallback('headersEnd');
|
|
965
|
+
state = STATE.PART_DATA_START;
|
|
966
|
+
break;
|
|
967
|
+
case STATE.PART_DATA_START:
|
|
968
|
+
state = STATE.PART_DATA;
|
|
969
|
+
setMark('partData');
|
|
970
|
+
case STATE.PART_DATA:
|
|
971
|
+
prevIndex = index;
|
|
972
|
+
|
|
973
|
+
if (index === 0) {
|
|
974
|
+
// boyer-moore derived algorithm to safely skip non-boundary data
|
|
975
|
+
i += boundaryEnd;
|
|
976
|
+
while (i < this.bufferLength && !(buffer[i] in boundaryChars)) {
|
|
977
|
+
i += boundaryLength;
|
|
978
|
+
}
|
|
979
|
+
i -= boundaryEnd;
|
|
980
|
+
c = buffer[i];
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
if (index < boundary.length) {
|
|
984
|
+
if (boundary[index] === c) {
|
|
985
|
+
if (index === 0) {
|
|
986
|
+
dataCallback('partData', true);
|
|
987
|
+
}
|
|
988
|
+
index++;
|
|
989
|
+
} else {
|
|
990
|
+
index = 0;
|
|
991
|
+
}
|
|
992
|
+
} else if (index === boundary.length) {
|
|
993
|
+
index++;
|
|
994
|
+
if (c === CR) {
|
|
995
|
+
// CR = part boundary
|
|
996
|
+
flags |= FBOUNDARY.PART_BOUNDARY;
|
|
997
|
+
} else if (c === HYPHEN) {
|
|
998
|
+
// HYPHEN = end boundary
|
|
999
|
+
flags |= FBOUNDARY.LAST_BOUNDARY;
|
|
1000
|
+
} else {
|
|
1001
|
+
index = 0;
|
|
1002
|
+
}
|
|
1003
|
+
} else if (index - 1 === boundary.length) {
|
|
1004
|
+
if (flags & FBOUNDARY.PART_BOUNDARY) {
|
|
1005
|
+
index = 0;
|
|
1006
|
+
if (c === LF) {
|
|
1007
|
+
// unset the PART_BOUNDARY flag
|
|
1008
|
+
flags &= ~FBOUNDARY.PART_BOUNDARY;
|
|
1009
|
+
this._handleCallback('partEnd');
|
|
1010
|
+
this._handleCallback('partBegin');
|
|
1011
|
+
state = STATE.HEADER_FIELD_START;
|
|
1012
|
+
break;
|
|
1013
|
+
}
|
|
1014
|
+
} else if (flags & FBOUNDARY.LAST_BOUNDARY) {
|
|
1015
|
+
if (c === HYPHEN) {
|
|
1016
|
+
this._handleCallback('partEnd');
|
|
1017
|
+
this._handleCallback('end');
|
|
1018
|
+
state = STATE.END;
|
|
1019
|
+
flags = 0;
|
|
1020
|
+
} else {
|
|
1021
|
+
index = 0;
|
|
1022
|
+
}
|
|
1023
|
+
} else {
|
|
1024
|
+
index = 0;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (index > 0) {
|
|
1029
|
+
// when matching a possible boundary, keep a lookbehind reference
|
|
1030
|
+
// in case it turns out to be a false lead
|
|
1031
|
+
lookbehind[index - 1] = c;
|
|
1032
|
+
} else if (prevIndex > 0) {
|
|
1033
|
+
// if our boundary turned out to be rubbish, the captured lookbehind
|
|
1034
|
+
// belongs to partData
|
|
1035
|
+
this._handleCallback('partData', lookbehind, 0, prevIndex);
|
|
1036
|
+
prevIndex = 0;
|
|
1037
|
+
setMark('partData');
|
|
1038
|
+
|
|
1039
|
+
// reconsider the current character even so it interrupted the sequence
|
|
1040
|
+
// it could be the beginning of a new sequence
|
|
1041
|
+
i--;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
break;
|
|
1045
|
+
case STATE.END:
|
|
1046
|
+
break;
|
|
1047
|
+
default:
|
|
1048
|
+
done(this._endUnexpected());
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
dataCallback('headerField');
|
|
1054
|
+
dataCallback('headerValue');
|
|
1055
|
+
dataCallback('partData');
|
|
1056
|
+
|
|
1057
|
+
this.index = index;
|
|
1058
|
+
this.state = state;
|
|
1059
|
+
this.flags = flags;
|
|
1060
|
+
|
|
1061
|
+
done();
|
|
1062
|
+
return this.bufferLength;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
explain() {
|
|
1066
|
+
return `state = ${MultipartParser.stateToString(this.state)}`;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// eslint-disable-next-line consistent-return
|
|
1071
|
+
MultipartParser.stateToString = (stateNumber) => {
|
|
1072
|
+
// eslint-disable-next-line no-restricted-syntax, guard-for-in
|
|
1073
|
+
for (const stateName in STATE) {
|
|
1074
|
+
const number = STATE[stateName];
|
|
1075
|
+
if (number === stateNumber) return stateName;
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
const MultipartParser$1 = Object.assign(MultipartParser, { STATES });
|
|
1080
|
+
|
|
1081
|
+
/* eslint-disable no-underscore-dangle */
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
const multipartType = 'multipart';
|
|
1085
|
+
// the `options` is also available through the `options` / `formidable.options`
|
|
1086
|
+
function plugin$1(formidable, options) {
|
|
1087
|
+
// the `this` context is always formidable, as the first argument of a plugin
|
|
1088
|
+
// but this allows us to customize/test each plugin
|
|
1089
|
+
|
|
1090
|
+
/* istanbul ignore next */
|
|
1091
|
+
const self = this || formidable;
|
|
1092
|
+
|
|
1093
|
+
// NOTE: we (currently) support both multipart/form-data and multipart/related
|
|
1094
|
+
const multipart = /multipart/i.test(self.headers['content-type']);
|
|
1095
|
+
|
|
1096
|
+
if (multipart) {
|
|
1097
|
+
const m = self.headers['content-type'].match(
|
|
1098
|
+
/boundary=(?:"([^"]+)"|([^;]+))/i,
|
|
1099
|
+
);
|
|
1100
|
+
if (m) {
|
|
1101
|
+
const initMultipart = createInitMultipart(m[1] || m[2]);
|
|
1102
|
+
initMultipart.call(self, self, options); // lgtm [js/superfluous-trailing-arguments]
|
|
1103
|
+
} else {
|
|
1104
|
+
const err = new FormidableError(
|
|
1105
|
+
'bad content-type header, no multipart boundary',
|
|
1106
|
+
missingMultipartBoundary,
|
|
1107
|
+
400,
|
|
1108
|
+
);
|
|
1109
|
+
self._error(err);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return self;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// Note that it's a good practice (but it's up to you) to use the `this.options` instead
|
|
1116
|
+
// of the passed `options` (second) param, because when you decide
|
|
1117
|
+
// to test the plugin you can pass custom `this` context to it (and so `this.options`)
|
|
1118
|
+
function createInitMultipart(boundary) {
|
|
1119
|
+
return function initMultipart() {
|
|
1120
|
+
this.type = multipartType;
|
|
1121
|
+
|
|
1122
|
+
const parser = new MultipartParser$1(this.options);
|
|
1123
|
+
let headerField;
|
|
1124
|
+
let headerValue;
|
|
1125
|
+
let part;
|
|
1126
|
+
|
|
1127
|
+
parser.initWithBoundary(boundary);
|
|
1128
|
+
|
|
1129
|
+
// eslint-disable-next-line max-statements, consistent-return
|
|
1130
|
+
parser.on('data', async ({ name, buffer, start, end }) => {
|
|
1131
|
+
if (name === 'partBegin') {
|
|
1132
|
+
part = new Stream();
|
|
1133
|
+
part.readable = true;
|
|
1134
|
+
part.headers = {};
|
|
1135
|
+
part.name = null;
|
|
1136
|
+
part.originalFilename = null;
|
|
1137
|
+
part.mimetype = null;
|
|
1138
|
+
|
|
1139
|
+
part.transferEncoding = this.options.encoding;
|
|
1140
|
+
part.transferBuffer = '';
|
|
1141
|
+
|
|
1142
|
+
headerField = '';
|
|
1143
|
+
headerValue = '';
|
|
1144
|
+
} else if (name === 'headerField') {
|
|
1145
|
+
headerField += buffer.toString(this.options.encoding, start, end);
|
|
1146
|
+
} else if (name === 'headerValue') {
|
|
1147
|
+
headerValue += buffer.toString(this.options.encoding, start, end);
|
|
1148
|
+
} else if (name === 'headerEnd') {
|
|
1149
|
+
headerField = headerField.toLowerCase();
|
|
1150
|
+
part.headers[headerField] = headerValue;
|
|
1151
|
+
|
|
1152
|
+
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
|
|
1153
|
+
const m = headerValue.match(
|
|
1154
|
+
// eslint-disable-next-line no-useless-escape
|
|
1155
|
+
/\bname=("([^"]*)"|([^\(\)<>@,;:\\"\/\[\]\?=\{\}\s\t/]+))/i,
|
|
1156
|
+
);
|
|
1157
|
+
if (headerField === 'content-disposition') {
|
|
1158
|
+
if (m) {
|
|
1159
|
+
part.name = m[2] || m[3] || '';
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
part.originalFilename = this._getFileName(headerValue);
|
|
1163
|
+
} else if (headerField === 'content-type') {
|
|
1164
|
+
part.mimetype = headerValue;
|
|
1165
|
+
} else if (headerField === 'content-transfer-encoding') {
|
|
1166
|
+
part.transferEncoding = headerValue.toLowerCase();
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
headerField = '';
|
|
1170
|
+
headerValue = '';
|
|
1171
|
+
} else if (name === 'headersEnd') {
|
|
1172
|
+
switch (part.transferEncoding) {
|
|
1173
|
+
case 'binary':
|
|
1174
|
+
case '7bit':
|
|
1175
|
+
case '8bit':
|
|
1176
|
+
case 'utf-8': {
|
|
1177
|
+
const dataPropagation = (ctx) => {
|
|
1178
|
+
if (ctx.name === 'partData') {
|
|
1179
|
+
part.emit('data', ctx.buffer.slice(ctx.start, ctx.end));
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
const dataStopPropagation = (ctx) => {
|
|
1183
|
+
if (ctx.name === 'partEnd') {
|
|
1184
|
+
part.emit('end');
|
|
1185
|
+
parser.off('data', dataPropagation);
|
|
1186
|
+
parser.off('data', dataStopPropagation);
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
parser.on('data', dataPropagation);
|
|
1190
|
+
parser.on('data', dataStopPropagation);
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
1193
|
+
case 'base64': {
|
|
1194
|
+
const dataPropagation = (ctx) => {
|
|
1195
|
+
if (ctx.name === 'partData') {
|
|
1196
|
+
part.transferBuffer += ctx.buffer
|
|
1197
|
+
.slice(ctx.start, ctx.end)
|
|
1198
|
+
.toString('ascii');
|
|
1199
|
+
|
|
1200
|
+
/*
|
|
1201
|
+
four bytes (chars) in base64 converts to three bytes in binary
|
|
1202
|
+
encoding. So we should always work with a number of bytes that
|
|
1203
|
+
can be divided by 4, it will result in a number of bytes that
|
|
1204
|
+
can be divided vy 3.
|
|
1205
|
+
*/
|
|
1206
|
+
const offset = parseInt(part.transferBuffer.length / 4, 10) * 4;
|
|
1207
|
+
part.emit(
|
|
1208
|
+
'data',
|
|
1209
|
+
Buffer.from(
|
|
1210
|
+
part.transferBuffer.substring(0, offset),
|
|
1211
|
+
'base64',
|
|
1212
|
+
),
|
|
1213
|
+
);
|
|
1214
|
+
part.transferBuffer = part.transferBuffer.substring(offset);
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
const dataStopPropagation = (ctx) => {
|
|
1218
|
+
if (ctx.name === 'partEnd') {
|
|
1219
|
+
part.emit('data', Buffer.from(part.transferBuffer, 'base64'));
|
|
1220
|
+
part.emit('end');
|
|
1221
|
+
parser.off('data', dataPropagation);
|
|
1222
|
+
parser.off('data', dataStopPropagation);
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
parser.on('data', dataPropagation);
|
|
1226
|
+
parser.on('data', dataStopPropagation);
|
|
1227
|
+
break;
|
|
1228
|
+
}
|
|
1229
|
+
default:
|
|
1230
|
+
return this._error(
|
|
1231
|
+
new FormidableError(
|
|
1232
|
+
'unknown transfer-encoding',
|
|
1233
|
+
unknownTransferEncoding,
|
|
1234
|
+
501,
|
|
1235
|
+
),
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
this._parser.pause();
|
|
1239
|
+
await this.onPart(part);
|
|
1240
|
+
this._parser.resume();
|
|
1241
|
+
} else if (name === 'end') {
|
|
1242
|
+
this.ended = true;
|
|
1243
|
+
this._maybeEnd();
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
this._parser = parser;
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/* eslint-disable no-underscore-dangle */
|
|
1252
|
+
|
|
1253
|
+
|
|
1254
|
+
class JSONParser extends Transform {
|
|
1255
|
+
constructor(options = {}) {
|
|
1256
|
+
super({ readableObjectMode: true });
|
|
1257
|
+
this.chunks = [];
|
|
1258
|
+
this.globalOptions = { ...options };
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
_transform(chunk, encoding, callback) {
|
|
1262
|
+
this.chunks.push(String(chunk)); // todo consider using a string decoder
|
|
1263
|
+
callback();
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
_flush(callback) {
|
|
1267
|
+
try {
|
|
1268
|
+
const fields = JSON.parse(this.chunks.join(''));
|
|
1269
|
+
this.push(fields);
|
|
1270
|
+
} catch (e) {
|
|
1271
|
+
callback(e);
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
this.chunks = null;
|
|
1275
|
+
callback();
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/* eslint-disable no-underscore-dangle */
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
const jsonType = 'json';
|
|
1283
|
+
// the `options` is also available through the `this.options` / `formidable.options`
|
|
1284
|
+
function plugin(formidable, options) {
|
|
1285
|
+
// the `this` context is always formidable, as the first argument of a plugin
|
|
1286
|
+
// but this allows us to customize/test each plugin
|
|
1287
|
+
|
|
1288
|
+
/* istanbul ignore next */
|
|
1289
|
+
const self = this || formidable;
|
|
1290
|
+
|
|
1291
|
+
if (/json/i.test(self.headers['content-type'])) {
|
|
1292
|
+
init.call(self, self, options);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
return self;
|
|
1296
|
+
}
|
|
1297
|
+
// Note that it's a good practice (but it's up to you) to use the `this.options` instead
|
|
1298
|
+
// of the passed `options` (second) param, because when you decide
|
|
1299
|
+
// to test the plugin you can pass custom `this` context to it (and so `this.options`)
|
|
1300
|
+
function init(_self, _opts) {
|
|
1301
|
+
this.type = jsonType;
|
|
1302
|
+
|
|
1303
|
+
const parser = new JSONParser(this.options);
|
|
1304
|
+
|
|
1305
|
+
parser.on('data', (fields) => {
|
|
1306
|
+
this.fields = fields;
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
parser.once('end', () => {
|
|
1310
|
+
this.ended = true;
|
|
1311
|
+
this._maybeEnd();
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
this._parser = parser;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
/* eslint-disable no-underscore-dangle */
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
class DummyParser extends Transform {
|
|
1321
|
+
constructor(incomingForm, options = {}) {
|
|
1322
|
+
super();
|
|
1323
|
+
this.globalOptions = { ...options };
|
|
1324
|
+
this.incomingForm = incomingForm;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
_flush(callback) {
|
|
1328
|
+
this.incomingForm.ended = true;
|
|
1329
|
+
this.incomingForm._maybeEnd();
|
|
1330
|
+
callback();
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
/* eslint-disable class-methods-use-this */
|
|
1335
|
+
/* eslint-disable no-underscore-dangle */
|
|
1336
|
+
|
|
1337
|
+
|
|
1338
|
+
const toHexoId = hexoid(25);
|
|
1339
|
+
const DEFAULT_OPTIONS = {
|
|
1340
|
+
maxFields: 1000,
|
|
1341
|
+
maxFieldsSize: 20 * 1024 * 1024,
|
|
1342
|
+
maxFiles: Infinity,
|
|
1343
|
+
maxFileSize: 200 * 1024 * 1024,
|
|
1344
|
+
maxTotalFileSize: undefined,
|
|
1345
|
+
minFileSize: 1,
|
|
1346
|
+
allowEmptyFiles: false,
|
|
1347
|
+
createDirsFromUploads: false,
|
|
1348
|
+
keepExtensions: false,
|
|
1349
|
+
encoding: 'utf-8',
|
|
1350
|
+
hashAlgorithm: false,
|
|
1351
|
+
uploadDir: os.tmpdir(),
|
|
1352
|
+
enabledPlugins: [plugin$3, plugin$2, plugin$1, plugin],
|
|
1353
|
+
fileWriteStreamHandler: null,
|
|
1354
|
+
defaultInvalidName: 'invalid-name',
|
|
1355
|
+
filter(_part) {
|
|
1356
|
+
return true;
|
|
1357
|
+
},
|
|
1358
|
+
filename: undefined,
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1361
|
+
function hasOwnProp(obj, key) {
|
|
1362
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
const decorateForceSequential = function (promiseCreator) {
|
|
1367
|
+
/* forces a function that returns a promise to be sequential
|
|
1368
|
+
useful for fs for example */
|
|
1369
|
+
let lastPromise = Promise.resolve();
|
|
1370
|
+
return async function (...x) {
|
|
1371
|
+
const promiseWeAreWaitingFor = lastPromise;
|
|
1372
|
+
let currentPromise;
|
|
1373
|
+
let callback;
|
|
1374
|
+
// we need to change lastPromise before await anything,
|
|
1375
|
+
// otherwise 2 calls might wait the same thing
|
|
1376
|
+
lastPromise = new Promise(function (resolve) {
|
|
1377
|
+
callback = resolve;
|
|
1378
|
+
});
|
|
1379
|
+
await promiseWeAreWaitingFor;
|
|
1380
|
+
currentPromise = promiseCreator(...x);
|
|
1381
|
+
currentPromise.then(callback).catch(callback);
|
|
1382
|
+
return currentPromise;
|
|
1383
|
+
};
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
const createNecessaryDirectoriesAsync = decorateForceSequential(function (filePath) {
|
|
1387
|
+
const directoryname = path.dirname(filePath);
|
|
1388
|
+
return fsPromises.mkdir(directoryname, { recursive: true });
|
|
1389
|
+
});
|
|
1390
|
+
|
|
1391
|
+
const invalidExtensionChar = (c) => {
|
|
1392
|
+
const code = c.charCodeAt(0);
|
|
1393
|
+
return !(
|
|
1394
|
+
code === 46 || // .
|
|
1395
|
+
(code >= 48 && code <= 57) ||
|
|
1396
|
+
(code >= 65 && code <= 90) ||
|
|
1397
|
+
(code >= 97 && code <= 122)
|
|
1398
|
+
);
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
class IncomingForm extends EventEmitter {
|
|
1402
|
+
constructor(options = {}) {
|
|
1403
|
+
super();
|
|
1404
|
+
|
|
1405
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
1406
|
+
if (!this.options.maxTotalFileSize) {
|
|
1407
|
+
this.options.maxTotalFileSize = this.options.maxFileSize;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
const dir = path.resolve(
|
|
1411
|
+
this.options.uploadDir || this.options.uploaddir || os.tmpdir(),
|
|
1412
|
+
);
|
|
1413
|
+
|
|
1414
|
+
this.uploaddir = dir;
|
|
1415
|
+
this.uploadDir = dir;
|
|
1416
|
+
|
|
1417
|
+
// initialize with null
|
|
1418
|
+
[
|
|
1419
|
+
'error',
|
|
1420
|
+
'headers',
|
|
1421
|
+
'type',
|
|
1422
|
+
'bytesExpected',
|
|
1423
|
+
'bytesReceived',
|
|
1424
|
+
'_parser',
|
|
1425
|
+
'req',
|
|
1426
|
+
].forEach((key) => {
|
|
1427
|
+
this[key] = null;
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
this._setUpRename();
|
|
1431
|
+
|
|
1432
|
+
this._flushing = 0;
|
|
1433
|
+
this._fieldsSize = 0;
|
|
1434
|
+
this._totalFileSize = 0;
|
|
1435
|
+
this._plugins = [];
|
|
1436
|
+
this.openedFiles = [];
|
|
1437
|
+
|
|
1438
|
+
this.options.enabledPlugins = []
|
|
1439
|
+
.concat(this.options.enabledPlugins)
|
|
1440
|
+
.filter(Boolean);
|
|
1441
|
+
|
|
1442
|
+
if (this.options.enabledPlugins.length === 0) {
|
|
1443
|
+
throw new FormidableError(
|
|
1444
|
+
'expect at least 1 enabled builtin plugin, see options.enabledPlugins',
|
|
1445
|
+
missingPlugin,
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
this.options.enabledPlugins.forEach((plugin) => {
|
|
1450
|
+
this.use(plugin);
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
this._setUpMaxFields();
|
|
1454
|
+
this._setUpMaxFiles();
|
|
1455
|
+
this.ended = undefined;
|
|
1456
|
+
this.type = undefined;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
use(plugin) {
|
|
1460
|
+
if (typeof plugin !== 'function') {
|
|
1461
|
+
throw new FormidableError(
|
|
1462
|
+
'.use: expect `plugin` to be a function',
|
|
1463
|
+
pluginFunction,
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
this._plugins.push(plugin.bind(this));
|
|
1467
|
+
return this;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
pause () {
|
|
1471
|
+
try {
|
|
1472
|
+
this.req.pause();
|
|
1473
|
+
} catch (err) {
|
|
1474
|
+
// the stream was destroyed
|
|
1475
|
+
if (!this.ended) {
|
|
1476
|
+
// before it was completed, crash & burn
|
|
1477
|
+
this._error(err);
|
|
1478
|
+
}
|
|
1479
|
+
return false;
|
|
1480
|
+
}
|
|
1481
|
+
return true;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
resume () {
|
|
1485
|
+
try {
|
|
1486
|
+
this.req.resume();
|
|
1487
|
+
} catch (err) {
|
|
1488
|
+
// the stream was destroyed
|
|
1489
|
+
if (!this.ended) {
|
|
1490
|
+
// before it was completed, crash & burn
|
|
1491
|
+
this._error(err);
|
|
1492
|
+
}
|
|
1493
|
+
return false;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
return true;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
// returns a promise if no callback is provided
|
|
1500
|
+
async parse(req, cb) {
|
|
1501
|
+
this.req = req;
|
|
1502
|
+
let promise;
|
|
1503
|
+
|
|
1504
|
+
// Setup callback first, so we don't miss anything from data events emitted immediately.
|
|
1505
|
+
if (!cb) {
|
|
1506
|
+
let resolveRef;
|
|
1507
|
+
let rejectRef;
|
|
1508
|
+
promise = new Promise((resolve, reject) => {
|
|
1509
|
+
resolveRef = resolve;
|
|
1510
|
+
rejectRef = reject;
|
|
1511
|
+
});
|
|
1512
|
+
cb = (err, fields, files) => {
|
|
1513
|
+
if (err) {
|
|
1514
|
+
rejectRef(err);
|
|
1515
|
+
} else {
|
|
1516
|
+
resolveRef([fields, files]);
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
const callback = __jsenv_default_import__$1(__jsenv_default_import__(cb));
|
|
1521
|
+
this.fields = {};
|
|
1522
|
+
const files = {};
|
|
1523
|
+
|
|
1524
|
+
this.on('field', (name, value) => {
|
|
1525
|
+
if (this.type === 'multipart' || this.type === 'urlencoded') {
|
|
1526
|
+
if (!hasOwnProp(this.fields, name)) {
|
|
1527
|
+
this.fields[name] = [value];
|
|
1528
|
+
} else {
|
|
1529
|
+
this.fields[name].push(value);
|
|
1530
|
+
}
|
|
1531
|
+
} else {
|
|
1532
|
+
this.fields[name] = value;
|
|
1533
|
+
}
|
|
1534
|
+
});
|
|
1535
|
+
this.on('file', (name, file) => {
|
|
1536
|
+
if (!hasOwnProp(files, name)) {
|
|
1537
|
+
files[name] = [file];
|
|
1538
|
+
} else {
|
|
1539
|
+
files[name].push(file);
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
this.on('error', (err) => {
|
|
1543
|
+
callback(err, this.fields, files);
|
|
1544
|
+
});
|
|
1545
|
+
this.on('end', () => {
|
|
1546
|
+
callback(null, this.fields, files);
|
|
1547
|
+
});
|
|
1548
|
+
|
|
1549
|
+
// Parse headers and setup the parser, ready to start listening for data.
|
|
1550
|
+
await this.writeHeaders(req.headers);
|
|
1551
|
+
|
|
1552
|
+
// Start listening for data.
|
|
1553
|
+
req
|
|
1554
|
+
.on('error', (err) => {
|
|
1555
|
+
this._error(err);
|
|
1556
|
+
})
|
|
1557
|
+
.on('aborted', () => {
|
|
1558
|
+
this.emit('aborted');
|
|
1559
|
+
this._error(new FormidableError('Request aborted', aborted));
|
|
1560
|
+
})
|
|
1561
|
+
.on('data', (buffer) => {
|
|
1562
|
+
try {
|
|
1563
|
+
this.write(buffer);
|
|
1564
|
+
} catch (err) {
|
|
1565
|
+
this._error(err);
|
|
1566
|
+
}
|
|
1567
|
+
})
|
|
1568
|
+
.on('end', () => {
|
|
1569
|
+
if (this.error) {
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
if (this._parser) {
|
|
1573
|
+
this._parser.end();
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
if (promise) {
|
|
1577
|
+
return promise;
|
|
1578
|
+
}
|
|
1579
|
+
return this;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
async writeHeaders(headers) {
|
|
1583
|
+
this.headers = headers;
|
|
1584
|
+
this._parseContentLength();
|
|
1585
|
+
await this._parseContentType();
|
|
1586
|
+
|
|
1587
|
+
if (!this._parser) {
|
|
1588
|
+
this._error(
|
|
1589
|
+
new FormidableError(
|
|
1590
|
+
'no parser found',
|
|
1591
|
+
noParser,
|
|
1592
|
+
415, // Unsupported Media Type
|
|
1593
|
+
),
|
|
1594
|
+
);
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
this._parser.once('error', (error) => {
|
|
1599
|
+
this._error(error);
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
write(buffer) {
|
|
1604
|
+
if (this.error) {
|
|
1605
|
+
return null;
|
|
1606
|
+
}
|
|
1607
|
+
if (!this._parser) {
|
|
1608
|
+
this._error(
|
|
1609
|
+
new FormidableError('uninitialized parser', uninitializedParser),
|
|
1610
|
+
);
|
|
1611
|
+
return null;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
this.bytesReceived += buffer.length;
|
|
1615
|
+
this.emit('progress', this.bytesReceived, this.bytesExpected);
|
|
1616
|
+
|
|
1617
|
+
this._parser.write(buffer);
|
|
1618
|
+
|
|
1619
|
+
return this.bytesReceived;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
onPart(part) {
|
|
1623
|
+
// this method can be overwritten by the user
|
|
1624
|
+
return this._handlePart(part);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
async _handlePart(part) {
|
|
1628
|
+
if (part.originalFilename && typeof part.originalFilename !== 'string') {
|
|
1629
|
+
this._error(
|
|
1630
|
+
new FormidableError(
|
|
1631
|
+
`the part.originalFilename should be string when it exists`,
|
|
1632
|
+
filenameNotString,
|
|
1633
|
+
),
|
|
1634
|
+
);
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// This MUST check exactly for undefined. You can not change it to !part.originalFilename.
|
|
1639
|
+
|
|
1640
|
+
// todo: uncomment when switch tests to Jest
|
|
1641
|
+
// console.log(part);
|
|
1642
|
+
|
|
1643
|
+
// ? NOTE(@tunnckocore): no it can be any falsey value, it most probably depends on what's returned
|
|
1644
|
+
// from somewhere else. Where recently I changed the return statements
|
|
1645
|
+
// and such thing because code style
|
|
1646
|
+
// ? NOTE(@tunnckocore): or even better, if there is no mimetype, then it's for sure a field
|
|
1647
|
+
// ? NOTE(@tunnckocore): originalFilename is an empty string when a field?
|
|
1648
|
+
if (!part.mimetype) {
|
|
1649
|
+
let value = '';
|
|
1650
|
+
const decoder = new StringDecoder(
|
|
1651
|
+
part.transferEncoding || this.options.encoding,
|
|
1652
|
+
);
|
|
1653
|
+
|
|
1654
|
+
part.on('data', (buffer) => {
|
|
1655
|
+
this._fieldsSize += buffer.length;
|
|
1656
|
+
if (this._fieldsSize > this.options.maxFieldsSize) {
|
|
1657
|
+
this._error(
|
|
1658
|
+
new FormidableError(
|
|
1659
|
+
`options.maxFieldsSize (${this.options.maxFieldsSize} bytes) exceeded, received ${this._fieldsSize} bytes of field data`,
|
|
1660
|
+
maxFieldsSizeExceeded,
|
|
1661
|
+
413, // Payload Too Large
|
|
1662
|
+
),
|
|
1663
|
+
);
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
value += decoder.write(buffer);
|
|
1667
|
+
});
|
|
1668
|
+
|
|
1669
|
+
part.on('end', () => {
|
|
1670
|
+
this.emit('field', part.name, value);
|
|
1671
|
+
});
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
if (!this.options.filter(part)) {
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
this._flushing += 1;
|
|
1680
|
+
|
|
1681
|
+
let fileSize = 0;
|
|
1682
|
+
const newFilename = this._getNewName(part);
|
|
1683
|
+
const filepath = this._joinDirectoryName(newFilename);
|
|
1684
|
+
const file = await this._newFile({
|
|
1685
|
+
newFilename,
|
|
1686
|
+
filepath,
|
|
1687
|
+
originalFilename: part.originalFilename,
|
|
1688
|
+
mimetype: part.mimetype,
|
|
1689
|
+
});
|
|
1690
|
+
file.on('error', (err) => {
|
|
1691
|
+
this._error(err);
|
|
1692
|
+
});
|
|
1693
|
+
this.emit('fileBegin', part.name, file);
|
|
1694
|
+
|
|
1695
|
+
file.open();
|
|
1696
|
+
this.openedFiles.push(file);
|
|
1697
|
+
|
|
1698
|
+
part.on('data', (buffer) => {
|
|
1699
|
+
this._totalFileSize += buffer.length;
|
|
1700
|
+
fileSize += buffer.length;
|
|
1701
|
+
|
|
1702
|
+
if (this._totalFileSize > this.options.maxTotalFileSize) {
|
|
1703
|
+
this._error(
|
|
1704
|
+
new FormidableError(
|
|
1705
|
+
`options.maxTotalFileSize (${this.options.maxTotalFileSize} bytes) exceeded, received ${this._totalFileSize} bytes of file data`,
|
|
1706
|
+
biggerThanTotalMaxFileSize,
|
|
1707
|
+
413,
|
|
1708
|
+
),
|
|
1709
|
+
);
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
if (buffer.length === 0) {
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
this.pause();
|
|
1716
|
+
file.write(buffer, () => {
|
|
1717
|
+
this.resume();
|
|
1718
|
+
});
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
part.on('end', () => {
|
|
1722
|
+
if (!this.options.allowEmptyFiles && fileSize === 0) {
|
|
1723
|
+
this._error(
|
|
1724
|
+
new FormidableError(
|
|
1725
|
+
`options.allowEmptyFiles is false, file size should be greater than 0`,
|
|
1726
|
+
noEmptyFiles,
|
|
1727
|
+
400,
|
|
1728
|
+
),
|
|
1729
|
+
);
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
if (fileSize < this.options.minFileSize) {
|
|
1733
|
+
this._error(
|
|
1734
|
+
new FormidableError(
|
|
1735
|
+
`options.minFileSize (${this.options.minFileSize} bytes) inferior, received ${fileSize} bytes of file data`,
|
|
1736
|
+
smallerThanMinFileSize,
|
|
1737
|
+
400,
|
|
1738
|
+
),
|
|
1739
|
+
);
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
if (fileSize > this.options.maxFileSize) {
|
|
1743
|
+
this._error(
|
|
1744
|
+
new FormidableError(
|
|
1745
|
+
`options.maxFileSize (${this.options.maxFileSize} bytes), received ${fileSize} bytes of file data`,
|
|
1746
|
+
biggerThanMaxFileSize,
|
|
1747
|
+
413,
|
|
1748
|
+
),
|
|
1749
|
+
);
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
file.end(() => {
|
|
1754
|
+
this._flushing -= 1;
|
|
1755
|
+
this.emit('file', part.name, file);
|
|
1756
|
+
this._maybeEnd();
|
|
1757
|
+
});
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// eslint-disable-next-line max-statements
|
|
1762
|
+
async _parseContentType() {
|
|
1763
|
+
if (this.bytesExpected === 0) {
|
|
1764
|
+
this._parser = new DummyParser(this, this.options);
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
if (!this.headers['content-type']) {
|
|
1769
|
+
this._error(
|
|
1770
|
+
new FormidableError(
|
|
1771
|
+
'bad content-type header, no content-type',
|
|
1772
|
+
missingContentType,
|
|
1773
|
+
400,
|
|
1774
|
+
),
|
|
1775
|
+
);
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
|
|
1780
|
+
new DummyParser(this, this.options);
|
|
1781
|
+
|
|
1782
|
+
const results = [];
|
|
1783
|
+
await Promise.all(this._plugins.map(async (plugin, idx) => {
|
|
1784
|
+
let pluginReturn = null;
|
|
1785
|
+
try {
|
|
1786
|
+
pluginReturn = await plugin(this, this.options) || this;
|
|
1787
|
+
} catch (err) {
|
|
1788
|
+
// directly throw from the `form.parse` method;
|
|
1789
|
+
// there is no other better way, except a handle through options
|
|
1790
|
+
const error = new FormidableError(
|
|
1791
|
+
`plugin on index ${idx} failed with: ${err.message}`,
|
|
1792
|
+
pluginFailed,
|
|
1793
|
+
500,
|
|
1794
|
+
);
|
|
1795
|
+
error.idx = idx;
|
|
1796
|
+
throw error;
|
|
1797
|
+
}
|
|
1798
|
+
Object.assign(this, pluginReturn);
|
|
1799
|
+
|
|
1800
|
+
// todo: use Set/Map and pass plugin name instead of the `idx` index
|
|
1801
|
+
this.emit('plugin', idx, pluginReturn);
|
|
1802
|
+
}));
|
|
1803
|
+
this.emit('pluginsResults', results);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
_error(err, eventName = 'error') {
|
|
1807
|
+
if (this.error || this.ended) {
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
this.req = null;
|
|
1812
|
+
this.error = err;
|
|
1813
|
+
this.emit(eventName, err);
|
|
1814
|
+
|
|
1815
|
+
this.openedFiles.forEach((file) => {
|
|
1816
|
+
file.destroy();
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
_parseContentLength() {
|
|
1821
|
+
this.bytesReceived = 0;
|
|
1822
|
+
if (this.headers['content-length']) {
|
|
1823
|
+
this.bytesExpected = parseInt(this.headers['content-length'], 10);
|
|
1824
|
+
} else if (this.headers['transfer-encoding'] === undefined) {
|
|
1825
|
+
this.bytesExpected = 0;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
if (this.bytesExpected !== null) {
|
|
1829
|
+
this.emit('progress', this.bytesReceived, this.bytesExpected);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
_newParser() {
|
|
1834
|
+
return new MultipartParser$1(this.options);
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
async _newFile({ filepath, originalFilename, mimetype, newFilename }) {
|
|
1838
|
+
if (this.options.fileWriteStreamHandler) {
|
|
1839
|
+
return new VolatileFile({
|
|
1840
|
+
newFilename,
|
|
1841
|
+
filepath,
|
|
1842
|
+
originalFilename,
|
|
1843
|
+
mimetype,
|
|
1844
|
+
createFileWriteStream: this.options.fileWriteStreamHandler,
|
|
1845
|
+
hashAlgorithm: this.options.hashAlgorithm,
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
if (this.options.createDirsFromUploads) {
|
|
1849
|
+
try {
|
|
1850
|
+
await createNecessaryDirectoriesAsync(filepath);
|
|
1851
|
+
} catch (errorCreatingDir) {
|
|
1852
|
+
this._error(new FormidableError(
|
|
1853
|
+
`cannot create directory`,
|
|
1854
|
+
cannotCreateDir,
|
|
1855
|
+
409,
|
|
1856
|
+
));
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
return new PersistentFile({
|
|
1860
|
+
newFilename,
|
|
1861
|
+
filepath,
|
|
1862
|
+
originalFilename,
|
|
1863
|
+
mimetype,
|
|
1864
|
+
hashAlgorithm: this.options.hashAlgorithm,
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
_getFileName(headerValue) {
|
|
1869
|
+
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
|
|
1870
|
+
const m = headerValue.match(
|
|
1871
|
+
/\bfilename=("(.*?)"|([^()<>{}[\]@,;:"?=\s/\t]+))($|;\s)/i,
|
|
1872
|
+
);
|
|
1873
|
+
if (!m) return null;
|
|
1874
|
+
|
|
1875
|
+
const match = m[2] || m[3] || '';
|
|
1876
|
+
let originalFilename = match.substr(match.lastIndexOf('\\') + 1);
|
|
1877
|
+
originalFilename = originalFilename.replace(/%22/g, '"');
|
|
1878
|
+
originalFilename = originalFilename.replace(/&#([\d]{4});/g, (_, code) =>
|
|
1879
|
+
String.fromCharCode(code),
|
|
1880
|
+
);
|
|
1881
|
+
|
|
1882
|
+
return originalFilename;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// able to get composed extension with multiple dots
|
|
1886
|
+
// "a.b.c" -> ".b.c"
|
|
1887
|
+
// as opposed to path.extname -> ".c"
|
|
1888
|
+
_getExtension(str) {
|
|
1889
|
+
if (!str) {
|
|
1890
|
+
return '';
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
const basename = path.basename(str);
|
|
1894
|
+
const firstDot = basename.indexOf('.');
|
|
1895
|
+
const lastDot = basename.lastIndexOf('.');
|
|
1896
|
+
let rawExtname = path.extname(basename);
|
|
1897
|
+
|
|
1898
|
+
if (firstDot !== lastDot) {
|
|
1899
|
+
rawExtname = basename.slice(firstDot);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
let filtered;
|
|
1903
|
+
const firstInvalidIndex = Array.from(rawExtname).findIndex(invalidExtensionChar);
|
|
1904
|
+
if (firstInvalidIndex === -1) {
|
|
1905
|
+
filtered = rawExtname;
|
|
1906
|
+
} else {
|
|
1907
|
+
filtered = rawExtname.substring(0, firstInvalidIndex);
|
|
1908
|
+
}
|
|
1909
|
+
if (filtered === '.') {
|
|
1910
|
+
return '';
|
|
1911
|
+
}
|
|
1912
|
+
return filtered;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
_joinDirectoryName(name) {
|
|
1916
|
+
const newPath = path.join(this.uploadDir, name);
|
|
1917
|
+
|
|
1918
|
+
// prevent directory traversal attacks
|
|
1919
|
+
if (!newPath.startsWith(this.uploadDir)) {
|
|
1920
|
+
return path.join(this.uploadDir, this.options.defaultInvalidName);
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
return newPath;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
_setUpRename() {
|
|
1927
|
+
const hasRename = typeof this.options.filename === 'function';
|
|
1928
|
+
if (hasRename) {
|
|
1929
|
+
this._getNewName = (part) => {
|
|
1930
|
+
let ext = '';
|
|
1931
|
+
let name = this.options.defaultInvalidName;
|
|
1932
|
+
if (part.originalFilename) {
|
|
1933
|
+
// can be null
|
|
1934
|
+
({ ext, name } = path.parse(part.originalFilename));
|
|
1935
|
+
if (this.options.keepExtensions !== true) {
|
|
1936
|
+
ext = '';
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
return this.options.filename.call(this, name, ext, part, this);
|
|
1940
|
+
};
|
|
1941
|
+
} else {
|
|
1942
|
+
this._getNewName = (part) => {
|
|
1943
|
+
const name = toHexoId();
|
|
1944
|
+
|
|
1945
|
+
if (part && this.options.keepExtensions) {
|
|
1946
|
+
const originalFilename =
|
|
1947
|
+
typeof part === 'string' ? part : part.originalFilename;
|
|
1948
|
+
return `${name}${this._getExtension(originalFilename)}`;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
return name;
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
_setUpMaxFields() {
|
|
1957
|
+
if (this.options.maxFields !== Infinity) {
|
|
1958
|
+
let fieldsCount = 0;
|
|
1959
|
+
this.on('field', () => {
|
|
1960
|
+
fieldsCount += 1;
|
|
1961
|
+
if (fieldsCount > this.options.maxFields) {
|
|
1962
|
+
this._error(
|
|
1963
|
+
new FormidableError(
|
|
1964
|
+
`options.maxFields (${this.options.maxFields}) exceeded`,
|
|
1965
|
+
maxFieldsExceeded,
|
|
1966
|
+
413,
|
|
1967
|
+
),
|
|
1968
|
+
);
|
|
1969
|
+
}
|
|
1970
|
+
});
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
_setUpMaxFiles() {
|
|
1975
|
+
if (this.options.maxFiles !== Infinity) {
|
|
1976
|
+
let fileCount = 0;
|
|
1977
|
+
this.on('fileBegin', () => {
|
|
1978
|
+
fileCount += 1;
|
|
1979
|
+
if (fileCount > this.options.maxFiles) {
|
|
1980
|
+
this._error(
|
|
1981
|
+
new FormidableError(
|
|
1982
|
+
`options.maxFiles (${this.options.maxFiles}) exceeded`,
|
|
1983
|
+
maxFilesExceeded,
|
|
1984
|
+
413,
|
|
1985
|
+
),
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
_maybeEnd() {
|
|
1993
|
+
if (!this.ended || this._flushing || this.error) {
|
|
1994
|
+
return;
|
|
1995
|
+
}
|
|
1996
|
+
this.req = null;
|
|
1997
|
+
this.emit('end');
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
export { DEFAULT_OPTIONS, DummyParser, IncomingForm, JSONParser, MultipartParser$1 as MultipartParser, OctetStreamParser, PersistentFile, QuerystringParser, VolatileFile, plugin$3 as plugin, plugin$2 as plugin$1, plugin$1 as plugin$2, plugin as plugin$3 };
|