minimalistic-server 0.0.65 → 0.0.67

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.
Files changed (4) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +2 -2
  3. package/index.mjs +2609 -2576
  4. package/package.json +32 -32
package/index.mjs CHANGED
@@ -1,2577 +1,2610 @@
1
- import * as http from 'node:http';
2
- import * as querystring from "node:querystring";
3
- import * as fs from "node:fs/promises";
4
- import * as tls from "node:tls";
5
- import * as net from "node:net";
6
- import * as nodeCrypto from "node:crypto";
7
-
8
- const servers = new Map();
9
- const keepAliveTimeout = 20000;
10
-
11
- let currentFsPromiseModule = fs;
12
-
13
- function setObjectProperty(object, name, value, enumerable = true, writable = true, configurable = true) {
14
- Object.defineProperty(
15
- object,
16
- name,
17
- {
18
- value,
19
- enumerable,
20
- writable,
21
- configurable,
22
- },
23
- )
24
- }
25
-
26
- function safePrint(data, isError = false) {
27
- try {
28
- if (isError) {
29
- console.error(data);
30
- console.trace('Stack trace');
31
- } else {
32
- console.log(data);
33
- }
34
- } catch (e) {
35
- console.error('Error printing data');
36
- console.trace('Stack trace');
37
- }
38
- }
39
-
40
- const escapeHtmlMap = {
41
- "&": "&",
42
- "<": "&lt;",
43
- ">": "&gt;",
44
- '"': "&quot;",
45
- "'": "&#39;",
46
- }
47
-
48
- const unescapeHtmlMap = {
49
- "&nbsp;": "\u00A0",
50
- "&lt;": "<",
51
- "&gt;": ">",
52
- "&amp;": "&",
53
- "&quot;": "\"",
54
- "&apos;": "'",
55
- "&cent;": "¢",
56
- "&pound;": "£",
57
- "&yen;": "¥",
58
- "&euro;": "€",
59
- "&copy;": "©",
60
- "&reg;": "®",
61
- "&trade;": "™",
62
-
63
- "&hellip;": "…",
64
- "&ndash;": "–",
65
- "&mdash;": "—",
66
- "&lsquo;": "‘",
67
- "&rsquo;": "’",
68
- "&ldquo;": "“",
69
- "&rdquo;": "”",
70
- "&laquo;": "«",
71
- "&raquo;": "»",
72
-
73
- "&times;": "×",
74
- "&divide;": "÷",
75
- "&plusmn;": "±",
76
- "&minus;": "−",
77
- "&sup2;": "²",
78
- "&sup3;": "³",
79
- "&frac12;": "½",
80
- "&frac14;": "¼",
81
- "&frac34;": "¾",
82
-
83
- "&deg;": "°",
84
- "&micro;": "µ",
85
- "&para;": "¶",
86
- "&sect;": "§",
87
- "&middot;": "·",
88
- "&bull;": "•",
89
-
90
- "&alpha;": "α",
91
- "&beta;": "β",
92
- "&gamma;": "γ",
93
- "&pi;": "π",
94
- "&sigma;": "σ",
95
- "&omega;": "ω",
96
- "&Alpha;": "Α",
97
- "&Beta;": "Β",
98
- "&Gamma;": "Γ",
99
- "&Pi;": "Π",
100
- "&Sigma;": "Σ",
101
- "&Omega;": "Ω"
102
- }
103
-
104
- const mimeTypes = {
105
- "123": "application/vnd.lotus-1-2-3",
106
- "3dml": "text/vnd.in3d.3dml",
107
- "3g2": "video/3gpp2",
108
- "3gp": "video/3gpp",
109
- "7z": "application/x-7z-compressed",
110
- "aab": "application/x-authorware-bin",
111
- "aac": "audio/aac",
112
- "aam": "application/x-authorware-map",
113
- "aas": "application/x-authorware-seg",
114
- "abw": "application/x-abiword",
115
- "ac": "application/pkix-attr-cert",
116
- "acc": "application/vnd.americandynamics.acc",
117
- "ace": "application/x-ace-compressed",
118
- "acu": "application/vnd.acucobol",
119
- "adp": "audio/adpcm",
120
- "aep": "application/vnd.audiograph",
121
- "afp": "application/vnd.ibm.modcap",
122
- "ahead": "application/vnd.ahead.space",
123
- "ai": "application/postscript",
124
- "aif": "audio/x-aiff",
125
- "aifc": "audio/x-aiff",
126
- "aiff": "audio/x-aiff",
127
- "air": "application/vnd.adobe.air-application-installer-package+zip",
128
- "ait": "application/vnd.dvb.ait",
129
- "ami": "application/vnd.amiga.ami",
130
- "apk": "application/vnd.android.package-archive",
131
- "apng": "image/apng",
132
- "application": "application/x-ms-application",
133
- "apr": "application/vnd.lotus-approach",
134
- "arc": "application/x-freearc",
135
- "asf": "video/x-ms-asf",
136
- "aso": "application/vnd.accpac.simply.aso",
137
- "atc": "application/vnd.acucorp",
138
- "atom": "application/atom+xml",
139
- "atomcat": "application/atomcat+xml",
140
- "atomsvc": "application/atomsvc+xml",
141
- "atx": "application/vnd.antix.game-component",
142
- "au": "audio/basic",
143
- "avi": "video/x-msvideo",
144
- "avif": "image/avif",
145
- "aw": "application/applixware",
146
- "azf": "application/vnd.airzip.filesecure.azf",
147
- "azs": "application/vnd.airzip.filesecure.azs",
148
- "azw": "application/vnd.amazon.ebook",
149
- "bcpio": "application/x-bcpio",
150
- "bdf": "application/x-font-bdf",
151
- "bdm": "application/vnd.syncml.dm+wbxml",
152
- "bed": "application/vnd.realvnc.bed",
153
- "bh2": "application/vnd.fujitsu.oasysprs",
154
- "bin": "application/octet-stream",
155
- "bmi": "application/vnd.bmi",
156
- "bmp": "image/bmp",
157
- "box": "application/vnd.previewsystems.box",
158
- "btif": "image/prs.btif",
159
- "bz": "application/x-bzip",
160
- "bz2": "application/x-bzip2",
161
- "c": "text/x-c",
162
- "c11amc": "application/vnd.cluetrust.cartomobile-config",
163
- "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg",
164
- "c4g": "application/vnd.clonk.c4group",
165
- "cab": "application/vnd.ms-cab-compressed",
166
- "car": "application/vnd.curl.car",
167
- "cat": "application/vnd.ms-pki.seccat",
168
- "ccxml": "application/ccxml+xml,",
169
- "cdbcmsg": "application/vnd.contact.cmsg",
170
- "cdkey": "application/vnd.mediastation.cdkey",
171
- "cdmia": "application/cdmi-capability",
172
- "cdmic": "application/cdmi-container",
173
- "cdmid": "application/cdmi-domain",
174
- "cdmio": "application/cdmi-object",
175
- "cdmiq": "application/cdmi-queue",
176
- "cdx": "chemical/x-cdx",
177
- "cdxml": "application/vnd.chemdraw+xml",
178
- "cdy": "application/vnd.cinderella",
179
- "cer": "application/pkix-cert",
180
- "cgm": "image/cgm",
181
- "chat": "application/x-chat",
182
- "chm": "application/vnd.ms-htmlhelp",
183
- "chrt": "application/vnd.kde.kchart",
184
- "cif": "chemical/x-cif",
185
- "cii": "application/vnd.anser-web-certificate-issue-initiation",
186
- "cil": "application/vnd.ms-artgalry",
187
- "cla": "application/vnd.claymore",
188
- "class": "application/java-vm",
189
- "clkk": "application/vnd.crick.clicker.keyboard",
190
- "clkp": "application/vnd.crick.clicker.palette",
191
- "clkt": "application/vnd.crick.clicker.template",
192
- "clkw": "application/vnd.crick.clicker.wordbank",
193
- "clkx": "application/vnd.crick.clicker",
194
- "clp": "application/x-msclip",
195
- "cmc": "application/vnd.cosmocaller",
196
- "cmdf": "chemical/x-cmdf",
197
- "cml": "chemical/x-cml",
198
- "cmp": "application/vnd.yellowriver-custom-menu",
199
- "cmx": "image/x-cmx",
200
- "cod": "application/vnd.rim.cod",
201
- "cpio": "application/x-cpio",
202
- "cpt": "application/mac-compactpro",
203
- "crd": "application/x-mscardfile",
204
- "crl": "application/pkix-crl",
205
- "cryptonote": "application/vnd.rig.cryptonote",
206
- "csh": "application/x-csh",
207
- "csml": "chemical/x-csml",
208
- "csp": "application/vnd.commonspace",
209
- "css": "text/css",
210
- "csv": "text/csv",
211
- "cu": "application/cu-seeme",
212
- "curl": "text/vnd.curl",
213
- "cww": "application/prs.cww",
214
- "dae": "model/vnd.collada+xml",
215
- "daf": "application/vnd.mobius.daf",
216
- "davmount": "application/davmount+xml",
217
- "dcurl": "text/vnd.curl.dcurl",
218
- "dd2": "application/vnd.oma.dd2+xml",
219
- "ddd": "application/vnd.fujixerox.ddd",
220
- "deb": "application/x-debian-package",
221
- "der": "application/x-x509-ca-cert",
222
- "dfac": "application/vnd.dreamfactory",
223
- "dir": "application/x-director",
224
- "dis": "application/vnd.mobius.dis",
225
- "djvu": "image/vnd.djvu",
226
- "dmg": "application/x-apple-diskimage",
227
- "dna": "application/vnd.dna",
228
- "doc": "application/msword",
229
- "docm": "application/vnd.ms-word.document.macroenabled.12",
230
- "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
231
- "dotm": "application/vnd.ms-word.template.macroenabled.12",
232
- "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
233
- "dp": "application/vnd.osgi.dp",
234
- "dpg": "application/vnd.dpgraph",
235
- "dra": "audio/vnd.dra",
236
- "dsc": "text/prs.lines.tag",
237
- "dssc": "application/dssc+der",
238
- "dtb": "application/x-dtbook+xml",
239
- "dtd": "application/xml-dtd",
240
- "dts": "audio/vnd.dts",
241
- "dtshd": "audio/vnd.dts.hd",
242
- "dvi": "application/x-dvi",
243
- "dwf": "model/vnd.dwf",
244
- "dwg": "image/vnd.dwg",
245
- "dxf": "image/vnd.dxf",
246
- "dxp": "application/vnd.spotfire.dxp",
247
- "ecelp4800": "audio/vnd.nuera.ecelp4800",
248
- "ecelp7470": "audio/vnd.nuera.ecelp7470",
249
- "ecelp9600": "audio/vnd.nuera.ecelp9600",
250
- "edm": "application/vnd.novadigm.edm",
251
- "edx": "application/vnd.novadigm.edx",
252
- "efif": "application/vnd.picsel",
253
- "ei6": "application/vnd.pg.osasli",
254
- "eml": "message/rfc822",
255
- "emma": "application/emma+xml",
256
- "eol": "audio/vnd.digital-winds",
257
- "eot": "application/vnd.ms-fontobject",
258
- "epub": "application/epub+zip",
259
- "es": "application/ecmascript",
260
- "es3": "application/vnd.eszigno3+xml",
261
- "esf": "application/vnd.epson.esf",
262
- "etx": "text/x-setext",
263
- "exe": "application/x-msdownload",
264
- "exi": "application/exi",
265
- "ext": "application/vnd.novadigm.ext",
266
- "ez2": "application/vnd.ezpix-album",
267
- "ez3": "application/vnd.ezpix-package",
268
- "f": "text/x-fortran",
269
- "f4v": "video/x-f4v",
270
- "fbs": "image/vnd.fastbidsheet",
271
- "fcs": "application/vnd.isac.fcs",
272
- "fdf": "application/vnd.fdf",
273
- "fe_launch": "application/vnd.denovo.fcselayout-link",
274
- "fg5": "application/vnd.fujitsu.oasysgp",
275
- "fh": "image/x-freehand",
276
- "fig": "application/x-xfig",
277
- "fli": "video/x-fli",
278
- "flo": "application/vnd.micrografx.flo",
279
- "flv": "video/x-flv",
280
- "flw": "application/vnd.kde.kivio",
281
- "flx": "text/vnd.fmi.flexstor",
282
- "fly": "text/vnd.fly",
283
- "fm": "application/vnd.framemaker",
284
- "fnc": "application/vnd.frogans.fnc",
285
- "fpx": "image/vnd.fpx",
286
- "fsc": "application/vnd.fsc.weblaunch",
287
- "fst": "image/vnd.fst",
288
- "ftc": "application/vnd.fluxtime.clip",
289
- "fti": "application/vnd.anser-web-funds-transfer-initiation",
290
- "fvt": "video/vnd.fvt",
291
- "fxp": "application/vnd.adobe.fxp",
292
- "fzs": "application/vnd.fuzzysheet",
293
- "g2w": "application/vnd.geoplan",
294
- "g3": "image/g3fax",
295
- "g3w": "application/vnd.geospace",
296
- "gac": "application/vnd.groove-account",
297
- "gdl": "model/vnd.gdl",
298
- "geo": "application/vnd.dynageo",
299
- "gex": "application/vnd.geometry-explorer",
300
- "ggb": "application/vnd.geogebra.file",
301
- "ggt": "application/vnd.geogebra.tool",
302
- "ghf": "application/vnd.groove-help",
303
- "gif": "image/gif",
304
- "gim": "application/vnd.groove-identity-message",
305
- "gmx": "application/vnd.gmx",
306
- "gnumeric": "application/x-gnumeric",
307
- "gph": "application/vnd.flographit",
308
- "gqf": "application/vnd.grafeq",
309
- "gram": "application/srgs",
310
- "grv": "application/vnd.groove-injector",
311
- "grxml": "application/srgs+xml",
312
- "gsf": "application/x-font-ghostscript",
313
- "gtar": "application/x-gtar",
314
- "gtm": "application/vnd.groove-tool-message",
315
- "gtw": "model/vnd.gtw",
316
- "gv": "text/vnd.graphviz",
317
- "gxt": "application/vnd.geonext",
318
- "h261": "video/h261",
319
- "h263": "video/h263",
320
- "h264": "video/h264",
321
- "hal": "application/vnd.hal+xml",
322
- "hbci": "application/vnd.hbci",
323
- "hdf": "application/x-hdf",
324
- "hlp": "application/winhlp",
325
- "hpgl": "application/vnd.hp-hpgl",
326
- "hpid": "application/vnd.hp-hpid",
327
- "hps": "application/vnd.hp-hps",
328
- "hqx": "application/mac-binhex40",
329
- "htke": "application/vnd.kenameaapp",
330
- "html": "text/html",
331
- "hvd": "application/vnd.yamaha.hv-dic",
332
- "hvp": "application/vnd.yamaha.hv-voice",
333
- "hvs": "application/vnd.yamaha.hv-script",
334
- "i2g": "application/vnd.intergeo",
335
- "icc": "application/vnd.iccprofile",
336
- "ice": "x-conference/x-cooltalk",
337
- "ico": "image/x-icon",
338
- "ics": "text/calendar",
339
- "ief": "image/ief",
340
- "ifm": "application/vnd.shana.informed.formdata",
341
- "igl": "application/vnd.igloader",
342
- "igm": "application/vnd.insors.igm",
343
- "igs": "model/iges",
344
- "igx": "application/vnd.micrografx.igx",
345
- "iif": "application/vnd.shana.informed.interchange",
346
- "imp": "application/vnd.accpac.simply.imp",
347
- "ims": "application/vnd.ms-ims",
348
- "ipfix": "application/ipfix",
349
- "ipk": "application/vnd.shana.informed.package",
350
- "irm": "application/vnd.ibm.rights-management",
351
- "irp": "application/vnd.irepository.package+xml",
352
- "itp": "application/vnd.shana.informed.formtemplate",
353
- "ivp": "application/vnd.immervision-ivp",
354
- "ivu": "application/vnd.immervision-ivu",
355
- "jad": "text/vnd.sun.j2me.app-descriptor",
356
- "jam": "application/vnd.jam",
357
- "jar": "application/java-archive",
358
- "java": "text/x-java-source,java",
359
- "jisp": "application/vnd.jisp",
360
- "jlt": "application/vnd.hp-jlyt",
361
- "jnlp": "application/x-java-jnlp-file",
362
- "joda": "application/vnd.joost.joda-archive",
363
- "jpeg": "image/jpeg",
364
- "jpg": "image/jpeg",
365
- "jpgv": "video/jpeg",
366
- "jpm": "video/jpm",
367
- "js": "text/javascript",
368
- "json": "application/json",
369
- "jsonld": "application/ld+json",
370
- "karbon": "application/vnd.kde.karbon",
371
- "kfo": "application/vnd.kde.kformula",
372
- "kia": "application/vnd.kidspiration",
373
- "kml": "application/vnd.google-earth.kml+xml",
374
- "kmz": "application/vnd.google-earth.kmz",
375
- "kne": "application/vnd.kinar",
376
- "kon": "application/vnd.kde.kontour",
377
- "kpr": "application/vnd.kde.kpresenter",
378
- "ksp": "application/vnd.kde.kspread",
379
- "ktx": "image/ktx",
380
- "ktz": "application/vnd.kahootz",
381
- "kwd": "application/vnd.kde.kword",
382
- "lasxml": "application/vnd.las.las+xml",
383
- "latex": "application/x-latex",
384
- "lbd": "application/vnd.llamagraphics.life-balance.desktop",
385
- "lbe": "application/vnd.llamagraphics.life-balance.exchange+xml",
386
- "les": "application/vnd.hhe.lesson-player",
387
- "link66": "application/vnd.route66.link66+xml",
388
- "lrm": "application/vnd.ms-lrm",
389
- "ltf": "application/vnd.frogans.ltf",
390
- "lvp": "audio/vnd.lucent.voice",
391
- "lwp": "application/vnd.lotus-wordpro",
392
- "m21": "application/mp21",
393
- "m3u": "audio/x-mpegurl",
394
- "m3u8": "application/vnd.apple.mpegurl",
395
- "m4a": "audio/mp4",
396
- "m4v": "video/x-m4v",
397
- "ma": "application/mathematica",
398
- "mads": "application/mads+xml",
399
- "mag": "application/vnd.ecowin.chart",
400
- "mathml": "application/mathml+xml",
401
- "mbk": "application/vnd.mobius.mbk",
402
- "mbox": "application/mbox",
403
- "mc1": "application/vnd.medcalcdata",
404
- "mcd": "application/vnd.mcd",
405
- "mcurl": "text/vnd.curl.mcurl",
406
- "mdb": "application/x-msaccess",
407
- "mdi": "image/vnd.ms-modi",
408
- "meta4": "application/metalink4+xml",
409
- "mets": "application/mets+xml",
410
- "mfm": "application/vnd.mfmp",
411
- "mgp": "application/vnd.osgeo.mapguide.package",
412
- "mgz": "application/vnd.proteus.magazine",
413
- "mid": "audio/midi",
414
- "midi": "audio/midi",
415
- "mif": "application/vnd.mif",
416
- "mj2": "video/mj2",
417
- "mjs": "text/javascript",
418
- "mkv": "video/x-matroska",
419
- "mlp": "application/vnd.dolby.mlp",
420
- "mmd": "application/vnd.chipnuts.karaoke-mmd",
421
- "mmf": "application/vnd.smaf",
422
- "mmr": "image/vnd.fujixerox.edmics-mmr",
423
- "mny": "application/x-msmoney",
424
- "mods": "application/mods+xml",
425
- "movie": "video/x-sgi-movie",
426
- "mp3": "audio/mpeg",
427
- "mp4": "video/mp4",
428
- "mp4a": "audio/mp4",
429
- "mpc": "application/vnd.mophun.certificate",
430
- "mpeg": "video/mpeg",
431
- "mpga": "audio/mpeg",
432
- "mpkg": "application/vnd.apple.installer+xml",
433
- "mpm": "application/vnd.blueice.multipass",
434
- "mpn": "application/vnd.mophun.application",
435
- "mpp": "application/vnd.ms-project",
436
- "mpy": "application/vnd.ibm.minipay",
437
- "mqy": "application/vnd.mobius.mqy",
438
- "mrc": "application/marc",
439
- "mrcx": "application/marcxml+xml",
440
- "mscml": "application/mediaservercontrol+xml",
441
- "mseq": "application/vnd.mseq",
442
- "msf": "application/vnd.epson.msf",
443
- "msh": "model/mesh",
444
- "msl": "application/vnd.mobius.msl",
445
- "msty": "application/vnd.muvee.style",
446
- "mts": "model/vnd.mts",
447
- "mus": "application/vnd.musician",
448
- "musicxml": "application/vnd.recordare.musicxml+xml",
449
- "mvb": "application/x-msmediaview",
450
- "mwf": "application/vnd.mfer",
451
- "mxf": "application/mxf",
452
- "mxl": "application/vnd.recordare.musicxml",
453
- "mxml": "application/xv+xml",
454
- "mxs": "application/vnd.triscape.mxs",
455
- "mxu": "video/vnd.mpegurl",
456
- "n-gage": "application/vnd.nokia.n-gage.symbian.install",
457
- "N/A": "application/andrew-inset",
458
- "n3": "text/n3",
459
- "nbp": "application/vnd.wolfram.player",
460
- "nc": "application/x-netcdf",
461
- "ncx": "application/x-dtbncx+xml",
462
- "ngdat": "application/vnd.nokia.n-gage.data",
463
- "nlu": "application/vnd.neurolanguage.nlu",
464
- "nml": "application/vnd.enliven",
465
- "nnd": "application/vnd.noblenet-directory",
466
- "nns": "application/vnd.noblenet-sealer",
467
- "nnw": "application/vnd.noblenet-web",
468
- "npx": "image/vnd.net-fpx",
469
- "nsf": "application/vnd.lotus-notes",
470
- "oa2": "application/vnd.fujitsu.oasys2",
471
- "oa3": "application/vnd.fujitsu.oasys3",
472
- "oas": "application/vnd.fujitsu.oasys",
473
- "obd": "application/x-msbinder",
474
- "oda": "application/oda",
475
- "odb": "application/vnd.oasis.opendocument.database",
476
- "odc": "application/vnd.oasis.opendocument.chart",
477
- "odf": "application/vnd.oasis.opendocument.formula",
478
- "odft": "application/vnd.oasis.opendocument.formula-template",
479
- "odg": "application/vnd.oasis.opendocument.graphics",
480
- "odi": "application/vnd.oasis.opendocument.image",
481
- "odm": "application/vnd.oasis.opendocument.text-master",
482
- "odp": "application/vnd.oasis.opendocument.presentation",
483
- "ods": "application/vnd.oasis.opendocument.spreadsheet",
484
- "odt": "application/vnd.oasis.opendocument.text",
485
- "oga": "audio/ogg",
486
- "ogv": "video/ogg",
487
- "ogx": "application/ogg",
488
- "onetoc": "application/onenote",
489
- "opf": "application/oebps-package+xml",
490
- "opus": "audio/ogg",
491
- "org": "application/vnd.lotus-organizer",
492
- "osf": "application/vnd.yamaha.openscoreformat",
493
- "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml",
494
- "otc": "application/vnd.oasis.opendocument.chart-template",
495
- "otf": "font/otf",
496
- "otg": "application/vnd.oasis.opendocument.graphics-template",
497
- "oth": "application/vnd.oasis.opendocument.text-web",
498
- "oti": "application/vnd.oasis.opendocument.image-template",
499
- "otp": "application/vnd.oasis.opendocument.presentation-template",
500
- "ots": "application/vnd.oasis.opendocument.spreadsheet-template",
501
- "ott": "application/vnd.oasis.opendocument.text-template",
502
- "oxt": "application/vnd.openofficeorg.extension",
503
- "p": "text/x-pascal",
504
- "p10": "application/pkcs10",
505
- "p12": "application/x-pkcs12",
506
- "p7b": "application/x-pkcs7-certificates",
507
- "p7m": "application/pkcs7-mime",
508
- "p7r": "application/x-pkcs7-certreqresp",
509
- "p7s": "application/pkcs7-signature",
510
- "p8": "application/pkcs8",
511
- "par": "text/plain-bas",
512
- "paw": "application/vnd.pawaafile",
513
- "pbd": "application/vnd.powerbuilder6",
514
- "pbm": "image/x-portable-bitmap",
515
- "pcf": "application/x-font-pcf",
516
- "pcl": "application/vnd.hp-pcl",
517
- "pclxl": "application/vnd.hp-pclxl",
518
- "pcurl": "application/vnd.curl.pcurl",
519
- "pcx": "image/x-pcx",
520
- "pdb": "application/vnd.palm",
521
- "pdf": "application/pdf",
522
- "pfa": "application/x-font-type1",
523
- "pfr": "application/font-tdpfr",
524
- "pgm": "image/x-portable-graymap",
525
- "pgn": "application/x-chess-pgn",
526
- "pgp": "application/pgp-signature",
527
- "pic": "image/x-pict",
528
- "pjpeg": "image/pjpeg",
529
- "pki": "application/pkixcmp",
530
- "pkipath": "application/pkix-pkipath",
531
- "plb": "application/vnd.3gpp.pic-bw-large",
532
- "plc": "application/vnd.mobius.plc",
533
- "plf": "application/vnd.pocketlearn",
534
- "pls": "application/pls+xml",
535
- "pml": "application/vnd.ctc-posml",
536
- "png": "image/png",
537
- "pnm": "image/x-portable-anymap",
538
- "portpkg": "application/vnd.macports.portpkg",
539
- "potm": "application/vnd.ms-powerpoint.template.macroenabled.12",
540
- "potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
541
- "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12",
542
- "ppd": "application/vnd.cups-ppd",
543
- "ppm": "image/x-portable-pixmap",
544
- "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12",
545
- "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
546
- "ppt": "application/vnd.ms-powerpoint",
547
- "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12",
548
- "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
549
- "prc": "application/x-mobipocket-ebook",
550
- "pre": "application/vnd.lotus-freelance",
551
- "prf": "application/pics-rules",
552
- "psb": "application/vnd.3gpp.pic-bw-small",
553
- "psd": "image/vnd.adobe.photoshop",
554
- "psf": "application/x-font-linux-psf",
555
- "pskcxml": "application/pskc+xml",
556
- "ptid": "application/vnd.pvi.ptid1",
557
- "pub": "application/x-mspublisher",
558
- "pvb": "application/vnd.3gpp.pic-bw-var",
559
- "pwn": "application/vnd.3m.post-it-notes",
560
- "pya": "audio/vnd.ms-playready.media.pya",
561
- "pyv": "video/vnd.ms-playready.media.pyv",
562
- "qam": "application/vnd.epson.quickanime",
563
- "qbo": "application/vnd.intu.qbo",
564
- "qfx": "application/vnd.intu.qfx",
565
- "qps": "application/vnd.publishare-delta-tree",
566
- "qt": "video/quicktime",
567
- "qxd": "application/vnd.quark.quarkxpress",
568
- "ra": "audio/vnd.rn-realaudio",
569
- "ram": "audio/vnd.rn-realaudio",
570
- "rar": "application/x-rar-compressed",
571
- "ras": "image/x-cmu-raster",
572
- "rcprofile": "application/vnd.ipunplugged.rcprofile",
573
- "rdf": "application/rdf+xml",
574
- "rdz": "application/vnd.data-vision.rdz",
575
- "rep": "application/vnd.businessobjects",
576
- "res": "application/x-dtbresource+xml",
577
- "rgb": "image/x-rgb",
578
- "rif": "application/reginfo+xml",
579
- "rip": "audio/vnd.rip",
580
- "rl": "application/resource-lists+xml",
581
- "rlc": "image/vnd.fujixerox.edmics-rlc",
582
- "rld": "application/resource-lists-diff+xml",
583
- "rm": "application/vnd.rn-realmedia",
584
- "rmi": "audio/mid",
585
- "rmp": "audio/x-pn-realaudio-plugin",
586
- "rms": "application/vnd.jcp.javame.midlet-rms",
587
- "rnc": "application/relax-ng-compact-syntax",
588
- "rp9": "application/vnd.cloanto.rp9",
589
- "rpss": "application/vnd.nokia.radio-presets",
590
- "rpst": "application/vnd.nokia.radio-preset",
591
- "rq": "application/sparql-query",
592
- "rs": "application/rls-services+xml",
593
- "rsd": "application/rsd+xml",
594
- "rss": "application/rss+xml",
595
- "rtf": "application/rtf",
596
- "rtx": "text/richtext",
597
- "s": "text/x-asm",
598
- "saf": "application/vnd.yamaha.smaf-audio",
599
- "sbml": "application/sbml+xml",
600
- "sc": "application/vnd.ibm.secure-container",
601
- "scd": "application/x-msschedule",
602
- "scm": "application/vnd.lotus-screencam",
603
- "scq": "application/scvp-cv-request",
604
- "scs": "application/scvp-cv-response",
605
- "scurl": "text/vnd.curl.scurl",
606
- "sda": "application/vnd.stardivision.draw",
607
- "sdc": "application/vnd.stardivision.calc",
608
- "sdd": "application/vnd.stardivision.impress",
609
- "sdkm": "application/vnd.solent.sdkm+xml",
610
- "sdp": "application/sdp",
611
- "sdw": "application/vnd.stardivision.writer",
612
- "see": "application/vnd.seemail",
613
- "seed": "application/vnd.fdsn.seed",
614
- "sema": "application/vnd.sema",
615
- "semd": "application/vnd.semd",
616
- "semf": "application/vnd.semf",
617
- "ser": "application/java-serialized-object",
618
- "setpay": "application/set-payment-initiation",
619
- "setreg": "application/set-registration-initiation",
620
- "sfd-hdstx": "application/vnd.hydrostatix.sof-data",
621
- "sfs": "application/vnd.spotfire.sfs",
622
- "sgl": "application/vnd.stardivision.writer-global",
623
- "sgml": "text/sgml",
624
- "sh": "application/x-sh",
625
- "shar": "application/x-shar",
626
- "shf": "application/shf+xml",
627
- "sis": "application/vnd.symbian.install",
628
- "sit": "application/x-stuffit",
629
- "sitx": "application/x-stuffitx",
630
- "skp": "application/vnd.koan",
631
- "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12",
632
- "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
633
- "slt": "application/vnd.epson.salt",
634
- "sm": "application/vnd.stepmania.stepchart",
635
- "smf": "application/vnd.stardivision.math",
636
- "smi": "application/smil+xml",
637
- "snd": "audio/basic",
638
- "snf": "application/x-font-snf",
639
- "spf": "application/vnd.yamaha.smaf-phrase",
640
- "spl": "application/x-futuresplash",
641
- "spot": "text/vnd.in3d.spot",
642
- "spp": "application/scvp-vp-response",
643
- "spq": "application/scvp-vp-request",
644
- "src": "application/x-wais-source",
645
- "sru": "application/sru+xml",
646
- "srx": "application/sparql-results+xml",
647
- "sse": "application/vnd.kodak-descriptor",
648
- "ssf": "application/vnd.epson.ssf",
649
- "ssml": "application/ssml+xml",
650
- "st": "application/vnd.sailingtracker.track",
651
- "stc": "application/vnd.sun.xml.calc.template",
652
- "std": "application/vnd.sun.xml.draw.template",
653
- "stf": "application/vnd.wt.stf",
654
- "sti": "application/vnd.sun.xml.impress.template",
655
- "stk": "application/hyperstudio",
656
- "stl": "application/vnd.ms-pki.stl",
657
- "str": "application/vnd.pg.format",
658
- "stw": "application/vnd.sun.xml.writer.template",
659
- "sub": "image/vnd.dvb.subtitle",
660
- "sus": "application/vnd.sus-calendar",
661
- "sv4cpio": "application/x-sv4cpio",
662
- "sv4crc": "application/x-sv4crc",
663
- "svc": "application/vnd.dvb.service",
664
- "svd": "application/vnd.svd",
665
- "svg": "image/svg+xml",
666
- "swf": "application/x-shockwave-flash",
667
- "swi": "application/vnd.aristanetworks.swi",
668
- "sxc": "application/vnd.sun.xml.calc",
669
- "sxd": "application/vnd.sun.xml.draw",
670
- "sxg": "application/vnd.sun.xml.writer.global",
671
- "sxi": "application/vnd.sun.xml.impress",
672
- "sxm": "application/vnd.sun.xml.math",
673
- "sxw": "application/vnd.sun.xml.writer",
674
- "t": "text/troff",
675
- "tao": "application/vnd.tao.intent-module-archive",
676
- "tar": "application/x-tar",
677
- "tcap": "application/vnd.3gpp2.tcap",
678
- "tcl": "application/x-tcl",
679
- "teacher": "application/vnd.smart.teacher",
680
- "tei": "application/tei+xml",
681
- "tex": "application/x-tex",
682
- "texinfo": "application/x-texinfo",
683
- "tfi": "application/thraud+xml",
684
- "tfm": "application/x-tex-tfm",
685
- "thmx": "application/vnd.ms-officetheme",
686
- "tif": "image/tiff",
687
- "tiff": "image/tiff",
688
- "tmo": "application/vnd.tmobile-livetv",
689
- "torrent": "application/x-bittorrent",
690
- "tpl": "application/vnd.groove-tool-template",
691
- "tpt": "application/vnd.trid.tpt",
692
- "tra": "application/vnd.trueapp",
693
- "trm": "application/x-msterminal",
694
- "ts": "video/mp2t",
695
- "tsd": "application/timestamped-data",
696
- "tsv": "text/tab-separated-values",
697
- "ttf": "font/ttf",
698
- "ttl": "text/turtle",
699
- "twd": "application/vnd.simtech-mindmapper",
700
- "txd": "application/vnd.genomatix.tuxedo",
701
- "txf": "application/vnd.mobius.txf",
702
- "txt": "text/plain",
703
- "ufd": "application/vnd.ufdl",
704
- "umj": "application/vnd.umajin",
705
- "unityweb": "application/vnd.unity",
706
- "uoml": "application/vnd.uoml+xml",
707
- "uri": "text/uri-list",
708
- "ustar": "application/x-ustar",
709
- "utz": "application/vnd.uiq.theme",
710
- "uu": "text/x-uuencode",
711
- "uva": "audio/vnd.dece.audio",
712
- "uvh": "video/vnd.dece.hd",
713
- "uvi": "image/vnd.dece.graphic",
714
- "uvm": "video/vnd.dece.mobile",
715
- "uvp": "video/vnd.dece.pd",
716
- "uvs": "video/vnd.dece.sd",
717
- "uvu": "video/vnd.uvvu.mp4",
718
- "uvv": "video/vnd.dece.video",
719
- "vcd": "application/x-cdlink",
720
- "vcf": "text/x-vcard",
721
- "vcg": "application/vnd.groove-vcard",
722
- "vcs": "text/x-vcalendar",
723
- "vcx": "application/vnd.vcx",
724
- "vis": "application/vnd.visionary",
725
- "viv": "video/vnd.vivo",
726
- "vsd": "application/vnd.visio",
727
- "vsdx": "application/vnd.visio2013",
728
- "vsf": "application/vnd.vsf",
729
- "vtu": "model/vnd.vtu",
730
- "vxml": "application/voicexml+xml",
731
- "wad": "application/x-doom",
732
- "wav": "audio/wav",
733
- "wax": "audio/x-ms-wax",
734
- "wbmp": "image/vnd.wap.wbmp",
735
- "wbs": "application/vnd.criticaltools.wbs+xml",
736
- "wbxml": "application/vnd.wap.wbxml",
737
- "weba": "audio/webm",
738
- "webm": "video/webm",
739
- "webp": "image/webp",
740
- "wg": "application/vnd.pmi.widget",
741
- "wgt": "application/widget",
742
- "wm": "video/x-ms-wm",
743
- "wma": "audio/x-ms-wma",
744
- "wmd": "application/x-ms-wmd",
745
- "wmf": "application/x-msmetafile",
746
- "wml": "text/vnd.wap.wml",
747
- "wmlc": "application/vnd.wap.wmlc",
748
- "wmls": "text/vnd.wap.wmlscript",
749
- "wmlsc": "application/vnd.wap.wmlscriptc",
750
- "wmv": "video/x-ms-wmv",
751
- "wmx": "video/x-ms-wmx",
752
- "wmz": "application/x-ms-wmz",
753
- "woff": "font/woff",
754
- "woff2": "font/woff2",
755
- "wpd": "application/vnd.wordperfect",
756
- "wpl": "application/vnd.ms-wpl",
757
- "wps": "application/vnd.ms-works",
758
- "wqd": "application/vnd.wqd",
759
- "wri": "application/x-mswrite",
760
- "wrl": "model/vrml",
761
- "wsdl": "application/wsdl+xml",
762
- "wspolicy": "application/wspolicy+xml",
763
- "wtb": "application/vnd.webturbo",
764
- "wvx": "video/x-ms-wvx",
765
- "x3d": "application/vnd.hzn-3d-crossword",
766
- "xap": "application/x-silverlight-app",
767
- "xar": "application/vnd.xara",
768
- "xbap": "application/x-ms-xbap",
769
- "xbd": "application/vnd.fujixerox.docuworks.binder",
770
- "xbm": "image/x-xbitmap",
771
- "xdf": "application/xcap-diff+xml",
772
- "xdm": "application/vnd.syncml.dm+xml",
773
- "xdp": "application/vnd.adobe.xdp+xml",
774
- "xdssc": "application/dssc+xml",
775
- "xdw": "application/vnd.fujixerox.docuworks",
776
- "xenc": "application/xenc+xml",
777
- "xer": "application/patch-ops-error+xml",
778
- "xfdf": "application/vnd.adobe.xfdf",
779
- "xfdl": "application/vnd.xfdl",
780
- "xhtml": "application/xhtml+xml",
781
- "xif": "image/vnd.xiff",
782
- "xlam": "application/vnd.ms-excel.addin.macroenabled.12",
783
- "xls": "application/vnd.ms-excel",
784
- "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12",
785
- "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12",
786
- "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
787
- "xltm": "application/vnd.ms-excel.template.macroenabled.12",
788
- "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
789
- "xml": "application/xml",
790
- "xo": "application/vnd.olpc-sugar",
791
- "xop": "application/xop+xml",
792
- "xpi": "application/x-xpinstall",
793
- "xpm": "image/x-xpixmap",
794
- "xpr": "application/vnd.is-xpr",
795
- "xps": "application/vnd.ms-xpsdocument",
796
- "xpw": "application/vnd.intercon.formnet",
797
- "xslt": "application/xslt+xml",
798
- "xsm": "application/vnd.syncml+xml",
799
- "xspf": "application/xspf+xml",
800
- "xul": "application/vnd.mozilla.xul+xml",
801
- "xwd": "image/x-xwindowdump",
802
- "xyz": "chemical/x-xyz",
803
- "yaml": "text/yaml",
804
- "yang": "application/yang",
805
- "yin": "application/yin+xml",
806
- "zaz": "application/vnd.zzazz.deck+xml",
807
- "zip": "application/zip",
808
- "zir": "application/vnd.zul",
809
- "zmm": "application/vnd.handheld-entertainment+xml",
810
- __proto__: null,
811
- };
812
-
813
- export function getServerFsPromiseModule() {
814
- return currentFsPromiseModule;
815
- }
816
-
817
- export function setServerFsPromiseModule(fsPromiseModule) {
818
- currentFsPromiseModule = fsPromiseModule;
819
- clearStaticCache();
820
- }
821
-
822
- export function getExtension(fileNameOrPath) {
823
- return fileNameOrPath?.toLowerCase().split('.').at(-1) ?? '';
824
- }
825
-
826
- export function getContentTypeByExtension(fileNameOrPath) {
827
- return mimeTypes[getExtension(fileNameOrPath)] ?? 'application/octet-stream';
828
- }
829
-
830
- export class UploadedFile {
831
- #content;
832
- #contentType;
833
- #fileName;
834
-
835
- constructor(content, contentType = 'application/octet-stream', fileName = '') {
836
- this.#content = content;
837
- this.#contentType = contentType;
838
- this.#fileName = fileName;
839
- }
840
-
841
- getContent() {
842
- return this.#content;
843
- }
844
-
845
- getContentType() {
846
- return this.#contentType;
847
- }
848
-
849
- getExtension() {
850
- return getExtension(this.#fileName);
851
- }
852
-
853
- getContentTypeByExtension() {
854
- return getContentTypeByExtension(this.#fileName);
855
- }
856
-
857
- getFileName() {
858
- return this.#fileName;
859
- }
860
-
861
- toJSON() {
862
- return {
863
- content: this.#content,
864
- contentType: this.#contentType,
865
- fileName: this.#fileName,
866
- };
867
- }
868
- }
869
-
870
- export class Request {
871
- #method;
872
- #headers = {};
873
- #path;
874
- #queryParams = {};
875
- #pathParams = {};
876
-
877
- #rawBodyPromise;
878
- #bodyPromise;
879
- #allParams;
880
- #cookies;
881
-
882
- #request;
883
- #customData = null;
884
- #webSocketData = null;
885
- #heads = null;
886
- #isWebSocketClosed = false;
887
-
888
- constructor(request, heads = null) {
889
- this.#request = request;
890
- this.#heads = heads;
891
-
892
- let url = new URL(`http://localhost${request.url}`);
893
-
894
- this.#method = request.method.toUpperCase();
895
-
896
- for (const k in request.headers) {
897
- setObjectProperty(this.#headers, k.toLowerCase(), request.headers[k]);
898
- }
899
-
900
- this.#path = url.pathname.split('/').filter(x => x).join('/');
901
-
902
- url.searchParams.forEach((v, k) => {
903
- setObjectProperty(this.#queryParams, k, v);
904
- });
905
- }
906
-
907
- isAlive() {
908
- return !(this.#request.socket.writableEnded || this.#request.socket.destroyed);
909
- }
910
-
911
- getMethod() {
912
- return this.#method;
913
- }
914
-
915
- getHeaders() {
916
- return this.#headers;
917
- }
918
-
919
- getIp() {
920
- return this.#request.socket.remoteAddress;
921
- }
922
-
923
- getPort() {
924
- return this.#request.socket.address().port;
925
- }
926
-
927
- getProtocol() {
928
- const socket = this.#request.socket;
929
-
930
- if (socket instanceof tls.TLSSocket && socket.encrypted) {
931
- return 'https';
932
- }
933
-
934
- return 'http';
935
- }
936
-
937
- getHost() {
938
- return this.#request.headers.host;
939
- }
940
-
941
- getPath() {
942
- return this.#path;
943
- }
944
-
945
- getQueryParams() {
946
- return this.#queryParams;
947
- }
948
-
949
- setPathParams(params) {
950
- this.#pathParams = params;
951
- }
952
-
953
- getPathParams() {
954
- return this.#pathParams;
955
- }
956
-
957
- getRawUrl() {
958
- return this.#request.url;
959
- }
960
-
961
- getRawBody() {
962
- if (this.#webSocketData) {
963
- return Buffer.from([]);
964
- }
965
-
966
- if (!this.#rawBodyPromise) {
967
- this.#rawBodyPromise = new Promise((resolve, reject) => {
968
- const body = [];
969
-
970
- this.#request.on('data', (chunk) => {
971
- try {
972
- body.push(chunk);
973
- } catch (error) {
974
- reject(error);
975
- }
976
- }).on('end', () => {
977
- try {
978
- resolve(Buffer.concat(body));
979
- } catch (error) {
980
- reject(error);
981
- }
982
- }).on('timeout', (error) => {
983
- reject(error);
984
- }).on('error', (error) => {
985
- reject(error);
986
- });
987
- });
988
- }
989
-
990
- return this.#rawBodyPromise;
991
- }
992
-
993
- getBody() {
994
- if (this.#webSocketData) {
995
- return {};
996
- }
997
-
998
- if (!this.#bodyPromise) {
999
- this.#bodyPromise = new Promise((resolve, reject) => {
1000
- const maxJsonSize = 32 * 1024 * 1024;
1001
- const maxUrlSize = 1024 * 1024;
1002
-
1003
- const contentType = this.#headers['content-type'] ?? '';
1004
-
1005
- if (this.#method === 'GET' && !contentType.includes('application/x-www-form-urlencoded')) {
1006
- resolve(this.#normalizeFormFields(this.#queryParams));
1007
- }
1008
-
1009
- this.getRawBody().then(async body => {
1010
- if (contentType.includes('application/x-www-form-urlencoded')) {
1011
- try {
1012
- if (body.length > maxUrlSize) {
1013
- throw new Error(`The URL request size (${body.length} bytes) exceeds ${maxUrlSize} bytes`);
1014
- }
1015
-
1016
- resolve(this.#parseUrlEncodedForm(body));
1017
- } catch (error) {
1018
- reject(error);
1019
- }
1020
- } else if (contentType.includes('multipart/form-data')) {
1021
- try {
1022
- resolve(await this.#parseMultipart(body, contentType));
1023
- } catch (error) {
1024
- reject(error);
1025
- }
1026
- } else if (contentType.includes('application/json')) {
1027
- try {
1028
- if (body.length > maxJsonSize) {
1029
- throw new Error(`The JSON request size (${body.length} bytes) exceeds ${maxJsonSize} bytes`);
1030
- }
1031
-
1032
- resolve(JSON.parse(body.toString()));
1033
- } catch (error) {
1034
- reject(error);
1035
- }
1036
- } else {
1037
- resolve({ body });
1038
- }
1039
- }).catch(error => {
1040
- reject(error);
1041
- });
1042
- });
1043
- }
1044
-
1045
- return this.#bodyPromise;
1046
- }
1047
-
1048
- #normalizeFormFields(fields) {
1049
- const result = {};
1050
-
1051
- for (const property in fields) {
1052
- const newpProp = property.replace(/\s+/gm, '').replaceAll('[]', '[-1]');
1053
- const pathToProperty = newpProp.split(/]\[|]|\[|\./gm).filter(x => x);
1054
-
1055
- let parent = result;
1056
-
1057
- for (let i = 0; i < pathToProperty.length; ++i) {
1058
- const curFragment = pathToProperty[i];
1059
- const nextFragment = i < pathToProperty.length - 1 ? pathToProperty[i + 1] : null;
1060
-
1061
- const nextParent = parent[curFragment] ?? (nextFragment ? (nextFragment.match(/^-?\d+$/gm) ? [] : {}) : fields[property]);
1062
-
1063
- if (typeof parent === 'object') {
1064
- if (parent instanceof Array && curFragment.match(/^-?\d+$/gm)) {
1065
- const index = Math.min(+curFragment, parent.length + 10000);
1066
-
1067
- if (index <= -1) {
1068
- if (nextParent instanceof Array) {
1069
- parent.push(...nextParent);
1070
- } else if (nextParent !== null) {
1071
- parent.push(nextParent);
1072
- }
1073
- } else {
1074
- while (parent.length <= index) {
1075
- parent.push(null);
1076
- }
1077
-
1078
- parent[index] = nextParent;
1079
- }
1080
- } else if (!(parent instanceof Array) && !curFragment.match(/^-?\d+$/gm)) {
1081
- setObjectProperty(parent, curFragment, nextParent);
1082
- } else {
1083
- break;
1084
- }
1085
- } else {
1086
- break;
1087
- }
1088
-
1089
- parent = nextParent;
1090
- }
1091
- }
1092
-
1093
- return result;
1094
- }
1095
-
1096
- async #parseMultipart(body, header) {
1097
- const boundaryString = header.match(/(?<=boundary=)\S+/gm)?.[0];
1098
-
1099
- const boundaryBuffer = Buffer.from('--' + boundaryString);
1100
- const partPositions = [];
1101
- let lastPosition = { start: -1, end: -1 };
1102
-
1103
- function isBoundaryEnd(boundary, index, b) {
1104
- let i = boundary.length - 1;
1105
-
1106
- if (index < i) {
1107
- return false;
1108
- }
1109
-
1110
- do {
1111
- if (boundary[i--] !== b[index--]) {
1112
- return false;
1113
- }
1114
- } while (i >= 0);
1115
-
1116
- return true;
1117
- }
1118
-
1119
- const maxSyncProcessedChunkSize = 40000000;
1120
-
1121
- for (let offset = 0; offset < body.length; offset += maxSyncProcessedChunkSize) {
1122
- const offsetEnd = Math.min(body.length, offset + maxSyncProcessedChunkSize);
1123
-
1124
- for (let i = offset; i < offsetEnd; ++i) {
1125
- if (isBoundaryEnd(boundaryBuffer, i, body)) {
1126
- lastPosition.end = i - boundaryBuffer.length - 1;
1127
-
1128
- lastPosition = {
1129
- start: i + 3,
1130
- end: -1,
1131
- };
1132
-
1133
- partPositions.push(lastPosition);
1134
- }
1135
- }
1136
-
1137
- if (offsetEnd < body.length) {
1138
- await new Promise((resolve) => setTimeout(resolve, 30));
1139
- }
1140
- }
1141
-
1142
- partPositions.pop();
1143
-
1144
- const newLineBuffer = Buffer.from('\r\n\r\n');
1145
-
1146
- const data = {};
1147
-
1148
- for (const position of partPositions) {
1149
- let end = position.end;
1150
-
1151
- for (let i = position.start; i < position.end; ++i) {
1152
- if (isBoundaryEnd(newLineBuffer, i, body)) {
1153
- end = i - 3;
1154
- break;
1155
- }
1156
- }
1157
-
1158
- const info = body.toString('utf-8', position.start, end);
1159
-
1160
- const name = decodeURIComponent(info.match(/(?<=name=")[^"]*/gm)?.[0] ?? '');
1161
- const fileName = decodeURIComponent(info.match(/(?<=filename=")[^"]*/gm)?.[0] ?? '');
1162
- const contentType = info.match(/(?<=^Content-Type:)[^\n]+/gm)?.[0]?.trim() ?? '';
1163
-
1164
- if (!name) {
1165
- continue;
1166
- }
1167
-
1168
- let value = null;
1169
-
1170
- if (contentType) {
1171
- if (fileName) {
1172
- value = new UploadedFile(
1173
- body.slice(end + 4, position.end),
1174
- contentType,
1175
- fileName
1176
- );
1177
- }
1178
- } else {
1179
- value = end === position.end ? '' : body.slice(end + 4, position.end).toString()
1180
- }
1181
-
1182
- if (name in data && !Array.isArray(data[name])) {
1183
- setObjectProperty(data, name, [data[name]]);
1184
- }
1185
-
1186
- if (Array.isArray(data[name])) {
1187
- if (value != null) {
1188
- data[name].push(value);
1189
- }
1190
-
1191
- } else {
1192
- setObjectProperty(data, name, value);
1193
- }
1194
- }
1195
-
1196
- return this.#normalizeFormFields(data);
1197
- }
1198
-
1199
- #parseUrlEncodedForm(body) {
1200
- const data = querystring.parse(body.toString());
1201
- return this.#normalizeFormFields(data);
1202
- }
1203
-
1204
- async getAllParams() {
1205
- if (!this.#allParams) {
1206
- const body = await this.getBody();
1207
-
1208
- this.#allParams = {
1209
- ...this.#queryParams,
1210
- ...this.#pathParams,
1211
- ...(typeof body === 'object' ? body : { body }),
1212
- }
1213
- }
1214
-
1215
- return this.#allParams;
1216
- }
1217
-
1218
- getCookies() {
1219
- if (!this.#cookies) {
1220
- this.#cookies = Object.fromEntries(
1221
- (this.#headers?.['cookie'] ?? '')
1222
- .split(/\s*;\s*/gm)
1223
- .map(s => s.split(/\s*=\s*/gm))
1224
- .map(x => [decodeURIComponent(x[0] ?? ''), decodeURIComponent(x[1] ?? '')])
1225
- );
1226
- }
1227
-
1228
- return this.#cookies;
1229
- }
1230
-
1231
- setCustomData(data) {
1232
- this.#customData = data;
1233
- }
1234
-
1235
- getCustomData() {
1236
- return this.#customData;
1237
- }
1238
-
1239
- toJSON() {
1240
- return {
1241
- method: this.#method,
1242
- headers: this.#headers,
1243
- path: this.#path,
1244
- queryParams: this.#queryParams,
1245
- };
1246
- }
1247
-
1248
- isHandledAsWebSocket() {
1249
- return !!this.#webSocketData;
1250
- }
1251
-
1252
- handleAsWebSocket(callback) {
1253
- if (typeof callback === 'function') {
1254
- try {
1255
- this.#initWebSocket();
1256
- } catch (error) {
1257
- this.#closeWebSocket(true);
1258
- throw error;
1259
- }
1260
- } else {
1261
- throw new Error('Callback not provided');
1262
- }
1263
-
1264
- const firstTime = this.#webSocketData.listeners.size === 0;
1265
-
1266
- this.#webSocketData.listeners.add(callback);
1267
-
1268
- if (firstTime) {
1269
- this.#handleWebSocketRequests();
1270
- }
1271
-
1272
- return {
1273
- write: this.#webSocketData.write,
1274
- close: this.#webSocketData.close,
1275
- };
1276
- }
1277
-
1278
- waitForWebSocketToClose() {
1279
- if (!this.#webSocketData || this.#isWebSocketClosed) {
1280
- return;
1281
- }
1282
-
1283
- return new Promise(resolve => {
1284
- const socket = this.#request.socket;
1285
-
1286
- socket.on("close", () => {
1287
- resolve();
1288
- });
1289
-
1290
- socket.on("end", () => {
1291
- resolve();
1292
- });
1293
-
1294
- socket.on("error", (error) => {
1295
- safePrint(error);
1296
- resolve();
1297
- });
1298
-
1299
- socket.on('timeout', (error) => {
1300
- safePrint(error);
1301
- resolve();
1302
- });
1303
- })
1304
- }
1305
-
1306
- async #readBytesFromWebSocket(size = 1) {
1307
- let result = Buffer.alloc(0);
1308
-
1309
- do {
1310
- if (this.#isWebSocketClosed) {
1311
- throw new Error('Socket is closed');
1312
- }
1313
-
1314
- this.#webSocketData.nextDataPromiseWithResolvers = null;
1315
- result = Buffer.concat([result, ...this.#webSocketData.collectedData]);
1316
- this.#webSocketData.collectedData = [];
1317
-
1318
- if (result.length === size) {
1319
- return result;
1320
- }
1321
-
1322
- if (result.length > size) {
1323
- this.#webSocketData.collectedData = [result.subarray(size)];
1324
- return result.subarray(0, size);
1325
- }
1326
-
1327
- this.#webSocketData.nextDataPromiseWithResolvers = Promise.withResolvers();
1328
- await this.#webSocketData.nextDataPromiseWithResolvers.promise;
1329
- } while (true);
1330
- }
1331
-
1332
- #initWebSocket() {
1333
- if (this.#isWebSocketClosed) {
1334
- throw new Error('Socket is already closed');
1335
- }
1336
-
1337
- if (this.#webSocketData) {
1338
- return;
1339
- }
1340
-
1341
- const webSocketHeader = this.#headers["sec-websocket-key"];
1342
-
1343
- if (!webSocketHeader) {
1344
- throw new JsonResponse({
1345
- message: 'Header "sec-websocket-key" is missing',
1346
- }, 400);
1347
- }
1348
-
1349
- const socket = this.#request.socket;
1350
-
1351
- this.#webSocketData = {
1352
- write: (data) => this.#writeIntoWebSocket(data),
1353
- close: () => {
1354
- this.#webSocketData.isManuallyClosed = true;
1355
- this.#closeWebSocket()
1356
- },
1357
- listeners: new Set(),
1358
- nextDataPromiseWithResolvers: null,
1359
- isManuallyClosed: false,
1360
- collectedData: this.#heads?.length ? [this.#heads] : [],
1361
- };
1362
-
1363
- const guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1364
- const sha1 = nodeCrypto.createHash("sha1");
1365
- sha1.update(webSocketHeader + guid);
1366
- const validHandShakeKey = sha1.digest("base64");
1367
-
1368
- const responseHeaders = [
1369
- "HTTP/1.1 101 Switching Protocols",
1370
- "Upgrade: websocket",
1371
- "Connection: Upgrade",
1372
- `Sec-WebSocket-Accept: ${validHandShakeKey}`,
1373
- "\r\n",
1374
- ].join("\r\n");
1375
-
1376
- socket.write(responseHeaders);
1377
-
1378
- socket.on("data", (data) => {
1379
- this.#webSocketData.collectedData.push(data);
1380
- this.#webSocketData.nextDataPromiseWithResolvers?.resolve();
1381
- this.#webSocketData.nextDataPromiseWithResolvers = null;
1382
- });
1383
-
1384
- socket.on("close", () => {
1385
- this.#closeWebSocket();
1386
- });
1387
-
1388
-
1389
- socket.on("end", () => {
1390
- this.#closeWebSocket();
1391
- });
1392
-
1393
- socket.on("error", (error) => {
1394
- this.#webSocketData.nextDataPromiseWithResolvers?.reject(error);
1395
- this.#webSocketData.nextDataPromiseWithResolvers = null;
1396
- this.#closeWebSocket(true);
1397
- });
1398
-
1399
- socket.on('timeout', (error) => {
1400
- this.#webSocketData.nextDataPromiseWithResolvers?.reject(error);
1401
- this.#webSocketData.nextDataPromiseWithResolvers = null;
1402
- this.#closeWebSocket(true);
1403
- });
1404
- }
1405
-
1406
- async #handleWebSocketRequests() {
1407
- const maxPayloadSize = 16 * 1024 * 1024;
1408
-
1409
- const sendDataToAllListeners = async (data = null, type = 'start') => {
1410
- const statuses = await Promise.allSettled(this.#webSocketData.listeners.values().map(listener => listener(data, type)));
1411
-
1412
- for (const status of statuses) {
1413
- if (status.status === 'rejected') {
1414
- safePrint(status.reason);
1415
- }
1416
- }
1417
- };
1418
-
1419
- try {
1420
- let collectedPayload = [];
1421
-
1422
- await new Promise((resolve) => setTimeout(resolve));
1423
- await sendDataToAllListeners();
1424
-
1425
- do {
1426
- if (this.#isWebSocketClosed) {
1427
- await sendDataToAllListeners(null, 'end');
1428
- return;
1429
- }
1430
-
1431
- const head = await this.#readBytesFromWebSocket(2);
1432
- const opCode = head[0] & 0b00001111;
1433
-
1434
- if (opCode === 0b00001000) {
1435
- await sendDataToAllListeners(null, 'end');
1436
- this.#closeWebSocket();
1437
- return;
1438
- }
1439
-
1440
- const fin = !!(head[0] & 0b10000000);
1441
- const hasMask = !!(head[1] & 0b10000000);
1442
- let payloadLength = head[1] & 0b01111111;
1443
-
1444
- if (payloadLength === 126) {
1445
- const payloadBytes = await this.#readBytesFromWebSocket(2);
1446
- payloadLength = 0;
1447
-
1448
- for (let i = 0; i < 2; ++i) {
1449
- payloadLength += payloadBytes[1 - i] << (8 * i);
1450
- }
1451
- } else if (payloadLength === 127) {
1452
- const payloadBytes = await this.#readBytesFromWebSocket(8);
1453
- payloadLength = 0;
1454
-
1455
- for (let i = 0; i < 8; ++i) {
1456
- payloadLength += payloadBytes[7 - i] << (8 * i);
1457
- }
1458
- }
1459
-
1460
- if (payloadLength > maxPayloadSize) {
1461
- throw new Error(`Payload size (${payloadLength} bytes) exceeds ${maxPayloadSize} bytes`);
1462
- }
1463
-
1464
- const mask = hasMask ? (await this.#readBytesFromWebSocket(4)) : Buffer.alloc(4);
1465
- const payload = await this.#readBytesFromWebSocket(payloadLength);
1466
-
1467
- for (let i = 0; i < payloadLength; ++i) {
1468
- payload[i] ^= mask[i & 0b11];
1469
- }
1470
-
1471
- collectedPayload.push(payload);
1472
-
1473
- if (fin) {
1474
- const finalBuffer = Buffer.concat(collectedPayload);
1475
- collectedPayload = [];
1476
-
1477
- switch (opCode) {
1478
- case 0b00000001:
1479
- case 0b00000010:
1480
- await sendDataToAllListeners(opCode === 0b00000001 ? finalBuffer.toString('utf8') : finalBuffer, 'message');
1481
- break;
1482
- case 0b00001001:
1483
- this.#writeIntoWebSocket(finalBuffer, true);
1484
- break;
1485
- default:
1486
- throw new Error(`Invalid op code ${opCode}`);
1487
- }
1488
- }
1489
- } while (true);
1490
- } catch (error) {
1491
- safePrint(error, true);
1492
- this.#closeWebSocket(true);
1493
-
1494
- if (this.#webSocketData.isManuallyClosed) {
1495
- await sendDataToAllListeners(null, 'end');
1496
- } else {
1497
- await sendDataToAllListeners(error, 'error');
1498
- }
1499
- }
1500
- }
1501
-
1502
- #writeIntoWebSocket(data, isPong = false) {
1503
- if (this.#isWebSocketClosed || !this.#webSocketData) {
1504
- return;
1505
- }
1506
-
1507
- if (ArrayBuffer.isView(data) && !(data instanceof Buffer)) {
1508
- data = data.buffer;
1509
- }
1510
-
1511
- if (typeof data !== 'string' && !(data instanceof Array || data instanceof Buffer || data instanceof ArrayBuffer)) {
1512
- data = `${data}`;
1513
- }
1514
-
1515
- const isString = typeof data === 'string';
1516
- const buffer = data instanceof Buffer ? data : (isString ? Buffer.from(data, 'utf-8') : Buffer.from(data));
1517
- const length = buffer.length;
1518
-
1519
- let payloadLength = [length];
1520
-
1521
- if (length > 65535) {
1522
- payloadLength[0] = 127;
1523
-
1524
- for (let i = 7; i >= 0; --i) {
1525
- payloadLength.push((length >> (i * 8)) & 0b11111111);
1526
- }
1527
- } else if (length > 125) {
1528
- payloadLength[0] = 126;
1529
-
1530
- for (let i = 1; i >= 0; --i) {
1531
- payloadLength.push((length >> (i * 8)) & 0b11111111);
1532
- }
1533
- }
1534
-
1535
- const opCodeWithFin = Buffer.from([isPong ? 0b10001010 : (isString ? 0b10000001 : 0b10000010)]);
1536
- payloadLength = Buffer.from(payloadLength);
1537
-
1538
- this.#request.socket.write(opCodeWithFin);
1539
- this.#request.socket.write(payloadLength);
1540
- this.#request.socket.write(buffer);
1541
- }
1542
-
1543
- #closeWebSocket(error = false) {
1544
- if (this.#isWebSocketClosed || !this.#webSocketData) {
1545
- return;
1546
- }
1547
-
1548
- this.#isWebSocketClosed = true;
1549
- const socket = this.#request.socket;
1550
- this.#webSocketData.nextDataPromiseWithResolvers?.reject(new Error('Socket is closed'));
1551
- this.#webSocketData.nextDataPromiseWithResolvers = null;
1552
- socket.end(Buffer.from(error ? [] : [0b10001000, 0b00000000]));
1553
- }
1554
- }
1555
-
1556
- export class Response {
1557
- #cookies;
1558
- #addedCustomHeaders = {};
1559
-
1560
- getCode() {
1561
- return 200;
1562
- }
1563
-
1564
- getHeaders() {
1565
- return this.getMergedWithOtherHeaders({ 'Content-Type': 'text/plain' });
1566
- }
1567
-
1568
- getBody() {
1569
- '';
1570
- }
1571
-
1572
- setCookies(cookies) {
1573
- this.#cookies = { ...cookies };
1574
- }
1575
-
1576
- addCookies(cookies) {
1577
- this.#cookies = this.#cookies ? { ...this.#cookies, ...cookies } : cookies;
1578
- }
1579
-
1580
- getCookies() {
1581
- return this.#cookies ?? {};
1582
- }
1583
-
1584
- addCustomHeaders(customHeaders) {
1585
- this.#addedCustomHeaders = { ...this.#addedCustomHeaders, ...customHeaders };
1586
- }
1587
-
1588
- getMergedWithOtherHeaders(headers) {
1589
- if (this.#cookies) {
1590
- let cookieStrings = [];
1591
- const maxAge = 60 * 60 * 24 * 365 * 5;
1592
-
1593
- for (const k in this.#cookies) {
1594
- let cookieValue = this.#cookies[k];
1595
-
1596
- if (cookieValue === null || cookieValue === undefined) {
1597
- cookieValue = { value: '', maxAge: 0 };
1598
- } if (!(cookieValue instanceof Object)) {
1599
- cookieValue = { value: cookieValue };
1600
- }
1601
-
1602
- cookieValue = { maxage: maxAge, path: '/', ...cookieValue };
1603
- let cookieString = `${encodeURIComponent(k)}=${encodeURIComponent(cookieValue.value ?? '')}`;
1604
-
1605
- for (const prop in cookieValue) {
1606
- const name = prop.split('-').join('').toLowerCase();
1607
-
1608
- const attributeName = {
1609
- 'domain': 'Domain',
1610
- 'expires': 'Expires',
1611
- 'httponly': 'HttpOnly',
1612
- 'maxage': 'Max-Age',
1613
- 'partitioned': 'Partitioned',
1614
- 'path': 'Path',
1615
- 'samesite': 'Samesite',
1616
- 'secure': 'Secure',
1617
- }[name];
1618
-
1619
- let attributeValue = cookieValue[prop];
1620
-
1621
- if (attributeName) {
1622
- if (!['httponly', 'partitioned', 'secure'].includes(name)) {
1623
- if (attributeValue instanceof Date) {
1624
- attributeValue = attributeValue.toUTCString();
1625
- } else if (name !== 'path') {
1626
- attributeValue = encodeURIComponent(`${attributeValue}`);
1627
- }
1628
-
1629
- cookieString += `; ${attributeName}=${attributeValue}`
1630
- } else if (attributeValue) {
1631
- cookieString += `; ${attributeName}`
1632
- }
1633
- }
1634
- }
1635
- cookieStrings.push(cookieString);
1636
- }
1637
-
1638
- if (cookieStrings.length) {
1639
- headers = {
1640
- ...headers,
1641
- 'Set-Cookie': cookieStrings,
1642
- }
1643
- }
1644
- }
1645
-
1646
- headers = {
1647
- ...headers,
1648
- ...this.#addedCustomHeaders,
1649
- }
1650
-
1651
- for (const key of Object.keys(headers)) {
1652
- if (headers[key] === null || headers[key] === undefined) {
1653
- delete headers[key];
1654
- }
1655
- }
1656
-
1657
- return headers;
1658
- }
1659
- }
1660
-
1661
- export class CustomResponse extends Response {
1662
- #code;
1663
- #data;
1664
- #headers;
1665
-
1666
- constructor(data, code = 200, headers = { 'Content-Type': 'application/octet-stream' }, cookies = null) {
1667
- super();
1668
- this.#data = data;
1669
- this.#code = code;
1670
- this.#headers = headers;
1671
- this.setCookies(cookies);
1672
- }
1673
-
1674
- getCode() {
1675
- return this.#code;
1676
- }
1677
-
1678
- getHeaders() {
1679
- return this.getMergedWithOtherHeaders(this.#headers);
1680
- }
1681
-
1682
- getBody() {
1683
- return this.#data;
1684
- }
1685
-
1686
- getData() {
1687
- return this.#data;
1688
- }
1689
-
1690
- setData(data) {
1691
- this.#data = data;
1692
- }
1693
- }
1694
-
1695
- export class FileResponse extends Response {
1696
- #code;
1697
- #filePath;
1698
- #contentType;
1699
-
1700
- #dataPromise = null;
1701
-
1702
- #maxChunkSize = 4 * 1024 * 1024;
1703
- #maxFragmentSize = 16 * 1024 * 1024 * 1024;
1704
- #fragmentRequestMap = new WeakMap();
1705
- #makeNotFoundResponse = null;
1706
- #urlPathForDirectory = null;
1707
-
1708
- #blocked = false;
1709
- #proxiedResponse = null;
1710
- #enableFragments = false;
1711
-
1712
- constructor(filePath, code = 200, contentType = null, cookies = null) {
1713
- super();
1714
- this.#filePath = filePath;
1715
- this.#code = code;
1716
- this.#contentType = contentType;
1717
- this.setCookies(cookies);
1718
- }
1719
-
1720
- async getCode(headers = null) {
1721
- if (this.#proxiedResponse) {
1722
- return await this.#proxiedResponse.getCode(headers);
1723
- }
1724
-
1725
- const requestedFragment = await this.#getFragmentRequest(headers);
1726
- const data = await this.#retreiveData();
1727
-
1728
- if (requestedFragment) {
1729
- return 206;
1730
- }
1731
-
1732
- return data.code;
1733
- }
1734
-
1735
- async getHeaders(headers = null) {
1736
- if (this.#proxiedResponse) {
1737
- const resultingHeaders = await this.#proxiedResponse.getHeaders(headers);
1738
- return this.getMergedWithOtherHeaders(resultingHeaders);
1739
- }
1740
-
1741
- const requestedFragment = await this.#getFragmentRequest(headers);
1742
- const data = await this.#retreiveData();
1743
-
1744
- if (requestedFragment) {
1745
- return {
1746
- ...data.headers,
1747
- 'Accept-Ranges': 'bytes',
1748
- 'Content-Range': `bytes ${requestedFragment.offset}-${requestedFragment.offset + requestedFragment.size - 1}/${data.size}`,
1749
- 'Content-Length': `${requestedFragment.size}`,
1750
- 'Cache-Control': 'no-store, no-cache, must-revalidate',
1751
- };
1752
- }
1753
-
1754
- if (typeof data.body === 'function') {
1755
- return {
1756
- ...data.headers,
1757
- 'Cache-Control': 'no-store, no-cache, must-revalidate',
1758
- }
1759
- }
1760
-
1761
- return data.headers;
1762
- }
1763
-
1764
- async getBody(headers = null) {
1765
- if (this.#proxiedResponse) {
1766
- return await this.#proxiedResponse.getBody(headers);
1767
- }
1768
-
1769
- const requestedFragment = await this.#getFragmentRequest(headers);
1770
- const data = await this.#retreiveData();
1771
-
1772
- if (requestedFragment) {
1773
- return () => data.body(requestedFragment);
1774
- }
1775
-
1776
- return data.body;
1777
- }
1778
-
1779
- getProxy() {
1780
- const proxy = new FileResponse(this.#filePath, this.#code, this.#contentType, this.getCookies());
1781
- proxy.#proxiedResponse = this;
1782
- return proxy;
1783
- }
1784
-
1785
- getFilePath() {
1786
- return this.#filePath;
1787
- }
1788
-
1789
- setFilePath(filePath) {
1790
- this.#filePath = filePath;
1791
- this.#dataPromise = this.#proxiedResponse = null;
1792
- }
1793
-
1794
- setNotFoundErrorCustomResponseHandler(handler) {
1795
- this.#makeNotFoundResponse = handler;
1796
- this.#dataPromise = this.#proxiedResponse = null;
1797
- }
1798
-
1799
- enableFraments() {
1800
- this.#enableFragments = true;
1801
- this.#dataPromise = this.#proxiedResponse = null;
1802
- }
1803
-
1804
- setUrlPathForDirectory(urlPathForDirectory) {
1805
- this.#urlPathForDirectory = urlPathForDirectory;
1806
- this.#dataPromise = this.#proxiedResponse = null;
1807
- }
1808
-
1809
- #isStreamableFileFormat(isDirectory = false) {
1810
- if (isDirectory) {
1811
- return false;
1812
- }
1813
-
1814
- const extension = this.#filePath?.toLowerCase().split('.').at(-1) ?? '';
1815
- return !!mimeTypes[extension]?.match(/^(audio|video)\//);
1816
- }
1817
-
1818
- #getFragmentRequest(headers) {
1819
- let promise = this.#fragmentRequestMap.get(headers);
1820
-
1821
- if (promise === undefined && headers?.['range'] && this.#enableFragments) {
1822
- const getFragmentRequest = async () => {
1823
- if (headers['range'].includes(',')) {
1824
- return null;
1825
- }
1826
-
1827
- const numbers = headers['range']
1828
- .trim()
1829
- .replace('bytes=', '')
1830
- .split(/\b\s*-\s*/gm)
1831
- .map(x => {
1832
- if (!x) {
1833
- return null;
1834
- }
1835
-
1836
- x = +x;
1837
-
1838
- if (!Number.isSafeInteger(x)) {
1839
- return null;
1840
- }
1841
-
1842
- return x;
1843
- });
1844
-
1845
- const data = await this.#retreiveData();
1846
-
1847
- if (data.code < 200 || data.code > 299 || typeof data.body !== 'function') {
1848
- return null;
1849
- }
1850
-
1851
- let offset = numbers[0] ?? 0;
1852
-
1853
- if (offset < 0) {
1854
- offset = data.size + offset;
1855
- }
1856
-
1857
- if (offset >= data.size) {
1858
- offset = data.size - 1;
1859
- }
1860
-
1861
- let size = (numbers[1] ?? (this.#maxFragmentSize - 1 + offset)) - offset + 1;
1862
-
1863
- if (size < 0) {
1864
- size = 0;
1865
- }
1866
-
1867
- size = Math.min(size, data.size - offset);
1868
-
1869
- return {
1870
- offset,
1871
- size,
1872
- };
1873
- }
1874
-
1875
- promise = getFragmentRequest();
1876
- this.#fragmentRequestMap.set(headers, promise);
1877
- }
1878
-
1879
- return promise ?? null;
1880
- }
1881
-
1882
- async * #getDirectoryStream() {
1883
- const data = await this.#retreiveData();
1884
-
1885
- if (typeof data.body !== 'function') {
1886
- yield data.body;
1887
- return;
1888
- }
1889
-
1890
- const urlPath = this.#urlPathForDirectory;
1891
- const parentUrlPath = urlPath.split('/').filter((x, i, arr) => x && i !== arr.length - 1).join('/');
1892
- const filePath = this.#filePath;
1893
-
1894
- yield `<!DOCTYPE html>
1895
- <html lang="en">
1896
- <head>
1897
- <title>${escapeHtml(urlPath)}</title>
1898
- </head>
1899
- <body>
1900
- ${urlPath ? `<a href="/${parentUrlPath}">Up</a><hr>` : ''}
1901
- `;
1902
- const entries = await currentFsPromiseModule.readdir(filePath, { withFileTypes: true });
1903
- let counter = 0;
1904
-
1905
- for (const entry of entries) {
1906
- if (this.#blocked) {
1907
- break;
1908
- }
1909
-
1910
- if (counter === 100) {
1911
- await new Promise(resolve => setTimeout(resolve, 50));
1912
- counter = 0;
1913
- }
1914
-
1915
- ++counter;
1916
-
1917
- yield `<a${entry.isFile() ? ' target="_blank"' : ''} href="/${urlPath}/${encodeURIComponent(entry.name)}">${escapeHtml(entry.name)}</a><br>`;
1918
- }
1919
-
1920
-
1921
- yield `</body>`
1922
-
1923
- }
1924
-
1925
- async * #getBodyStream(fragmentRequest) {
1926
- const data = await this.#retreiveData();
1927
-
1928
- if (typeof data.body !== 'function') {
1929
- yield data.body;
1930
- return;
1931
- }
1932
-
1933
- let filehandle;
1934
-
1935
- try {
1936
- const requestedPosition = fragmentRequest?.offset ?? 0;
1937
- const requestedSize = fragmentRequest?.size ?? data.size;
1938
- const maxChunkSize = fragmentRequest ? 256 * 1024 : this.#maxChunkSize;
1939
-
1940
- filehandle = await currentFsPromiseModule.open(this.#filePath, 'r');
1941
-
1942
- for (let offset = 0; offset < requestedSize && !this.#blocked; offset += maxChunkSize) {
1943
- const size = Math.min(maxChunkSize, requestedSize - offset);
1944
- const chunk = await filehandle.read(Buffer.alloc(size), 0, size, offset + requestedPosition);
1945
- yield chunk.buffer;
1946
- }
1947
- } finally {
1948
- filehandle?.close();
1949
- }
1950
- }
1951
-
1952
- #retreiveData() {
1953
- if (!this.#dataPromise) {
1954
- this.#dataPromise = new Promise(async resolve => {
1955
- let filehandle;
1956
-
1957
- try {
1958
- if (this.#blocked) {
1959
- throw new Error(`${this.#filePath} is blocked`);
1960
- }
1961
-
1962
- filehandle = await currentFsPromiseModule.open(this.#filePath, 'r');
1963
- const stat = await filehandle.stat();
1964
- const size = stat.size;
1965
- const readAsDirectory = stat.isDirectory() && this.#urlPathForDirectory !== null;
1966
- const body = size > this.#maxChunkSize || this.#isStreamableFileFormat(readAsDirectory) ? (fragmentRequest => this.#getBodyStream(fragmentRequest)) : (readAsDirectory ? (() => this.#getDirectoryStream()) : await filehandle.readFile());
1967
-
1968
- resolve({
1969
- code: this.#code,
1970
- headers: this.getMergedWithOtherHeaders({
1971
- 'Content-Type': this.#contentType ??
1972
- (readAsDirectory ? 'text/html; charset=utf-8' : null) ??
1973
- mimeTypes[this.#filePath.split('.').at(-1).toLowerCase()] ??
1974
- 'application/octet-stream',
1975
- 'Content-Length': readAsDirectory ? null : size,
1976
- }
1977
- ),
1978
- body,
1979
- size,
1980
- });
1981
- } catch (e) {
1982
- safePrint(e, true);
1983
-
1984
- const notFountData = {
1985
- code: 404,
1986
- headers: this.getMergedWithOtherHeaders({ 'Content-Type': 'application/json' }),
1987
- body: JSON.stringify({
1988
- message: `File not found`
1989
- })
1990
- };
1991
-
1992
- try {
1993
- const response = await this.#makeNotFoundResponse?.(this.#filePath);
1994
-
1995
- if (response instanceof Response) {
1996
- const code = await response.getCode();
1997
- const headers = await response.getHeaders();
1998
- const body = await response.getBody();
1999
-
2000
- notFountData.code = code;
2001
- notFountData.headers = headers;
2002
- notFountData.body = body;
2003
- }
2004
- } catch (error) {
2005
- safePrint(error, true);
2006
- }
2007
-
2008
- resolve(notFountData);
2009
- } finally {
2010
- await filehandle?.close();
2011
- }
2012
- });
2013
- }
2014
-
2015
- return this.#dataPromise;
2016
- }
2017
-
2018
- block() {
2019
- this.#blocked = true;
2020
- this.#dataPromise = this.#proxiedResponse = null;
2021
- }
2022
-
2023
- isBlocked() {
2024
- return this.#blocked;
2025
- }
2026
- }
2027
-
2028
- export class JsonResponse extends Response {
2029
- #object;
2030
- #code;
2031
- #cachedBuffer = null;
2032
-
2033
- constructor(object = {}, code = 200, cookies = null) {
2034
- super();
2035
- this.setCookies(cookies);
2036
- this.#object = object;
2037
- this.#code = code;
2038
- }
2039
-
2040
- getCode() {
2041
- return this.#code;
2042
- }
2043
-
2044
- getHeaders() {
2045
- return this.getMergedWithOtherHeaders({
2046
- 'Content-Type': 'application/json',
2047
- 'Content-Length': `${this.getBody().length}`,
2048
- });
2049
- }
2050
-
2051
- getBody() {
2052
- if (!this.#cachedBuffer) {
2053
- this.#cachedBuffer = Buffer.from(JSON.stringify(this.#object), 'utf8');
2054
- }
2055
-
2056
- return this.#cachedBuffer;
2057
- }
2058
-
2059
- getObject() {
2060
- return this.#object;
2061
- }
2062
-
2063
- setObject(object) {
2064
- this.#cachedBuffer = null;
2065
- this.#object = object;
2066
- }
2067
- }
2068
-
2069
- export class HTMLResponse extends Response {
2070
- #string;
2071
- #code;
2072
- #cachedBuffer = null;
2073
-
2074
- constructor(string = '', code = 200, cookies = null) {
2075
- super();
2076
- this.setCookies(cookies);
2077
- this.#string = string;
2078
- this.#code = code;
2079
- }
2080
-
2081
- getCode() {
2082
- return this.#code;
2083
- }
2084
-
2085
- getHeaders() {
2086
- return this.getMergedWithOtherHeaders({
2087
- 'Content-Type': 'text/html; charset=utf-8',
2088
- 'Content-Length': `${this.getBody().length}`,
2089
- });
2090
- }
2091
-
2092
- getBody() {
2093
- if (!this.#cachedBuffer) {
2094
- this.#cachedBuffer = Buffer.from(this.#string, 'utf8');
2095
- }
2096
-
2097
- return this.#cachedBuffer;
2098
- }
2099
-
2100
- getString() {
2101
- return this.#string;
2102
- }
2103
-
2104
- setString(string) {
2105
- this.#cachedBuffer = null;
2106
- this.#string = string;
2107
- }
2108
- }
2109
-
2110
- export class RedirectResponse extends Response {
2111
- #url;
2112
- #code;
2113
-
2114
- constructor(url, code = 301, cookies = null) {
2115
- super();
2116
- this.setCookies(cookies);
2117
- this.#url = url;
2118
- this.#code = code;
2119
- }
2120
-
2121
- getCode() {
2122
- return this.#code;
2123
- }
2124
-
2125
- getHeaders() {
2126
- return this.getMergedWithOtherHeaders({ 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store, no-cache, must-revalidate', 'Location': this.#url, });
2127
- }
2128
-
2129
- getBody() {
2130
- return '';
2131
- }
2132
-
2133
- getUrl() {
2134
- return this.#url;
2135
- }
2136
-
2137
- setUrl(url) {
2138
- this.#url = url;
2139
- }
2140
- }
2141
-
2142
- export function serve(routes, port = 80, staticFileDirectoryOrDirectories = null, handleNotFoundError = null, handleServerError = null) {
2143
- port = +port;
2144
- routes = normalizeRoutes(routes, handleServerError);
2145
- staticFileDirectoryOrDirectories = normalizeStaticFileDirectories(staticFileDirectoryOrDirectories);
2146
- safePrint(routes);
2147
- safePrint(staticFileDirectoryOrDirectories);
2148
-
2149
- try {
2150
- unserve(port);
2151
-
2152
-
2153
- const callback = async (req, res, heads = null) => {
2154
- try {
2155
- const [code, headers, body, isHandledAsWebSocket] = await handleRequest(req, routes, staticFileDirectoryOrDirectories, handleNotFoundError, heads);
2156
-
2157
- if (isHandledAsWebSocket || !(res instanceof http.ServerResponse)) {
2158
- return;
2159
- }
2160
-
2161
- res.writeHead(code, headers);
2162
-
2163
- if (typeof body === 'function') {
2164
- for await (const chunk of body()) {
2165
- if (res.writableEnded || res.destroyed) {
2166
- break;
2167
- }
2168
-
2169
- res.write(chunk);
2170
- let waitTime = 0;
2171
-
2172
- while (res.writableNeedDrain && !res.writableEnded && !res.destroyed && waitTime < keepAliveTimeout) {
2173
- await new Promise(resolve => setTimeout(resolve, 50));
2174
- waitTime += 50;
2175
- }
2176
- }
2177
- } else {
2178
- res.end(body);
2179
- }
2180
- } catch (error) {
2181
- safePrint(error, true);
2182
- } finally {
2183
- if (res instanceof net.Socket) {
2184
- if (!res.writableEnded) {
2185
- res.end();
2186
- }
2187
-
2188
- await new Promise(resolve => setTimeout(resolve, 2000));
2189
-
2190
- if (!res.destroyed) {
2191
- res.destroy();
2192
- }
2193
- } else if (res instanceof http.ServerResponse && !res.writableEnded && !res.destroyed) {
2194
- res.end();
2195
- }
2196
- }
2197
- };
2198
-
2199
- const server = http.createServer(callback);
2200
- server.on('upgrade', callback);
2201
-
2202
- servers.set(port, server);
2203
-
2204
- server.keepAliveTimeout = keepAliveTimeout;
2205
- server.headersTimeout = keepAliveTimeout;
2206
-
2207
- server.on('error', (error) => {
2208
- unserve(port);
2209
- safePrint(error, true);
2210
- });
2211
-
2212
- server.listen(port, () => {
2213
- safePrint(`Server started on port ${port}`);
2214
- });
2215
- } catch (e) {
2216
- safePrint(e, true);
2217
- }
2218
-
2219
-
2220
- }
2221
-
2222
- export function escapeHtml(str) {
2223
- return str?.toString().replace(/[&<>"']/gm, (c) => escapeHtmlMap[c]) ?? '';
2224
- }
2225
-
2226
- export function unescapeHtml(htmlStr) {
2227
- return htmlStr?.toString()
2228
- .replace(/&(?:#(\d+|x[\dA-Fa-f]+)|([^;]+));/gm, (entity, digits, entityName) => unescapeHtmlMap[entity] ?? entityName ?? String.fromCharCode("0" + digits));
2229
- }
2230
-
2231
- export function unserve(port = 80) {
2232
- port = +port;
2233
-
2234
- servers.get(port)?.close(() => {
2235
- safePrint(`Server closed on port ${port}`);
2236
- });
2237
-
2238
- servers.delete(port);
2239
- }
2240
-
2241
- function wrapInMiddlewares(callback, preMiddlewares = [], postMiddlewares = [], handleServerError = null) {
2242
- let resultCallback = async (request, handleOptions = false) => {
2243
- let response;
2244
-
2245
- try {
2246
- for (const pre of preMiddlewares) {
2247
- const req = await pre(request);
2248
-
2249
- if (req instanceof Request) {
2250
- request = req;
2251
- }
2252
- }
2253
-
2254
- response = wrapInResponseClass(handleOptions ? new CustomResponse(Buffer.from(''), 200, { 'Content-Type': null }) : await callback(request));
2255
- } catch (error) {
2256
- if (error instanceof Response) {
2257
- response = error;
2258
- } else {
2259
- throw error;
2260
- }
2261
- }
2262
-
2263
- for (const post of postMiddlewares) {
2264
- const res = await post(request, response);
2265
-
2266
- if (res !== undefined) {
2267
- response = wrapInResponseClass(res);
2268
- }
2269
- }
2270
-
2271
- return response;
2272
- };
2273
-
2274
- if (handleServerError) {
2275
- const tempCallback = resultCallback;
2276
-
2277
- resultCallback = async (request, handleOptions = false) => {
2278
- try {
2279
- return await tempCallback(request, handleOptions);
2280
- } catch (error) {
2281
- if (error instanceof Response) {
2282
- throw error;
2283
- }
2284
-
2285
- safePrint(error);
2286
- return wrapInResponseClass(await handleServerError(request, error));
2287
- }
2288
- };
2289
- }
2290
-
2291
- return resultCallback;
2292
- }
2293
-
2294
- function normalizeStaticFileDirectories(staticFileDirectoryOrDirectories) {
2295
- if (staticFileDirectoryOrDirectories !== null && staticFileDirectoryOrDirectories !== undefined) {
2296
- if (!Array.isArray(staticFileDirectoryOrDirectories)) {
2297
- staticFileDirectoryOrDirectories = [staticFileDirectoryOrDirectories];
2298
- }
2299
-
2300
- staticFileDirectoryOrDirectories = staticFileDirectoryOrDirectories.filter(x => x !== null && typeof x === 'object' || typeof x === 'string').map(x => {
2301
- let serverFilePath = '', urlPath = '', showWholeDirectory = false, maxAgeInSeconds = -1, preMiddlewares = [], postMiddlewares = [];
2302
- const defaultMaxAge = 5 * 24 * 60 * 60;
2303
-
2304
- if (typeof x === 'string') {
2305
- serverFilePath = urlPath = x;
2306
- showWholeDirectory = false;
2307
- maxAgeInSeconds = defaultMaxAge;
2308
- preMiddlewares = [];
2309
- postMiddlewares = [];
2310
- } else {
2311
- ({ serverFilePath, urlPath, showWholeDirectory, maxAgeInSeconds, preMiddlewares, postMiddlewares } = x);
2312
- }
2313
-
2314
- urlPath = `${urlPath}`.split('/').filter(x => x).join('/');
2315
- serverFilePath = `${serverFilePath}`.split('/').filter(x => x).join('/');
2316
- showWholeDirectory = !!showWholeDirectory;
2317
- maxAgeInSeconds = Math.floor(+(maxAgeInSeconds ?? defaultMaxAge));
2318
- preMiddlewares = Array.isArray(preMiddlewares) ? preMiddlewares.filter(x => typeof x === 'function') : [];
2319
- postMiddlewares = Array.isArray(postMiddlewares) ? postMiddlewares.filter(x => typeof x === 'function') : [];
2320
-
2321
- return {
2322
- urlPath,
2323
- serverFilePath,
2324
- showWholeDirectory,
2325
- maxAgeInSeconds,
2326
- preMiddlewares,
2327
- postMiddlewares,
2328
- };
2329
- });
2330
- } else {
2331
- staticFileDirectoryOrDirectories = [];
2332
- }
2333
-
2334
- return staticFileDirectoryOrDirectories;
2335
- }
2336
-
2337
- function normalizeRoutes(routes, handleServerError) {
2338
- const flatten = {};
2339
-
2340
- function flattenRecursively(root, path = '', preMiddlewares = [], postMiddlewares = []) {
2341
- if (!root) {
2342
- return;
2343
- }
2344
-
2345
- if (root.preMiddlewares instanceof Array) {
2346
- preMiddlewares = [...preMiddlewares, ...root.preMiddlewares];
2347
- }
2348
-
2349
- if (root.postMiddlewares instanceof Array) {
2350
- postMiddlewares = [...root.postMiddlewares, ...postMiddlewares];
2351
- }
2352
-
2353
- if (root && typeof root === 'object' && !Array.isArray(root)) {
2354
- for (const prop in root) {
2355
- const newPath = (path + '/' + prop).split('/').filter(x => x).join('/');
2356
- flattenRecursively(root[prop], newPath, preMiddlewares, postMiddlewares);
2357
- }
2358
- } else if (typeof root === 'function') {
2359
- setObjectProperty(flatten, path, wrapInMiddlewares(root, preMiddlewares, postMiddlewares, handleServerError));
2360
- }
2361
- }
2362
-
2363
- flattenRecursively(routes);
2364
-
2365
- const result = Object.create(null);
2366
-
2367
- for (const route in flatten) {
2368
- const split = route.split('/').filter(x => x);
2369
- let method = split.at(-1)?.toUpperCase();
2370
-
2371
- if (['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'].includes(method)) {
2372
- split.pop();
2373
- } else {
2374
- method = 'GET';
2375
- }
2376
-
2377
- let parent = result;
2378
-
2379
- for (const fragment of split) {
2380
- if (!parent[fragment]) {
2381
- setObjectProperty(parent, fragment, Object.create(null));
2382
- }
2383
-
2384
- parent = parent[fragment];
2385
- }
2386
-
2387
- setObjectProperty(parent, `/${method}/`, flatten[route]);
2388
- }
2389
-
2390
- return result;
2391
- }
2392
-
2393
- function wrapInResponseClass(response) {
2394
- if (!(response instanceof Response)) {
2395
- if (typeof response === 'object') {
2396
- response = new JsonResponse(response, 200);
2397
- } else {
2398
- response = new HTMLResponse(`${response ?? ''}`, 200);
2399
- }
2400
- }
2401
-
2402
- return response;
2403
- }
2404
-
2405
- const staticCache = new Map();
2406
-
2407
- export function clearStaticCache(path = null) {
2408
- invalidateStaticCache(path);
2409
- }
2410
-
2411
- export function blockStaticCache(path = null) {
2412
- invalidateStaticCache(path, false);
2413
- }
2414
-
2415
- function invalidateStaticCache(path = null, clear = true) {
2416
- if (path === null) {
2417
- for (const v of staticCache.values()) {
2418
- v.block();
2419
- }
2420
-
2421
- if (clear) {
2422
- staticCache.clear();
2423
- }
2424
- } else {
2425
- path = `${path}`.split('/').filter(x => x).join('/');
2426
-
2427
- if (!clear && !staticCache.has(path)) {
2428
- staticCache.set(path, new FileResponse(path));
2429
- }
2430
-
2431
- staticCache.get(path)?.block();
2432
-
2433
- if (clear) {
2434
- staticCache.delete(path);
2435
- }
2436
- }
2437
- }
2438
-
2439
- async function handleRequest(req, routes, staticFileDirectories, handleNotFoundError, heads) {
2440
- let response = new JsonResponse({
2441
- message: 'Invalid data'
2442
- }, 400);
2443
-
2444
- let requestHeaders;
2445
- let responseBodyIsIncluded = true;
2446
-
2447
- try {
2448
- const request = new Request(req, heads);
2449
-
2450
- const method = request.getMethod();
2451
- const path = request.getPath();
2452
- const pathParams = {};
2453
- requestHeaders = request.getHeaders();
2454
- responseBodyIsIncluded = method !== 'HEAD';
2455
-
2456
- let routeHandler = routes;
2457
- let handleOptions = false;
2458
-
2459
- const staticFileOrDirectory = method === 'GET' || !responseBodyIsIncluded ? staticFileDirectories.find(x => x.urlPath === path || path.startsWith(x.urlPath + '/')) : null;
2460
-
2461
- if (staticFileOrDirectory) {
2462
- routeHandler = () => {
2463
- const filePath = decodeURI(path).replace(staticFileOrDirectory.urlPath, staticFileOrDirectory.serverFilePath);
2464
-
2465
- let resp = staticCache.get(filePath);
2466
-
2467
- if (!resp) {
2468
- resp = new FileResponse(filePath);
2469
- resp.enableFraments();
2470
-
2471
- if (staticFileOrDirectory.maxAgeInSeconds > 0) {
2472
- resp.addCustomHeaders({
2473
- 'Cache-Control': `public, max-age=${staticFileOrDirectory.maxAgeInSeconds}`,
2474
- });
2475
- }
2476
-
2477
- if (handleNotFoundError) {
2478
- resp.setNotFoundErrorCustomResponseHandler(() => handleNotFoundError(request));
2479
- }
2480
-
2481
- if (staticFileOrDirectory.showWholeDirectory) {
2482
- resp.setUrlPathForDirectory(path);
2483
- }
2484
-
2485
- staticCache.set(filePath, resp);
2486
-
2487
- if (staticCache.size > 500) {
2488
- for (const [k, v] of staticCache.entries()) {
2489
- if (!v.isBlocked()) {
2490
- staticCache.delete(k);
2491
- break;
2492
- }
2493
- }
2494
- }
2495
- }
2496
-
2497
- return resp.getProxy();
2498
- };
2499
-
2500
- routeHandler = wrapInMiddlewares(routeHandler, staticFileOrDirectory.preMiddlewares, staticFileOrDirectory.postMiddlewares);
2501
- } else {
2502
- for (const fragment of path.split('/')) {
2503
- if (!fragment) {
2504
- break;
2505
- }
2506
-
2507
- let newRouteHandler = routeHandler[fragment];
2508
-
2509
- if (!newRouteHandler) {
2510
- for (let k in routeHandler) {
2511
- if (k.match(/^{\w+}$/gm)) {
2512
- setObjectProperty(pathParams, k.replace(/[{}]/gm, ''), decodeURIComponent(fragment));
2513
- newRouteHandler = routeHandler[k];
2514
- break;
2515
- }
2516
- }
2517
- }
2518
-
2519
- routeHandler = newRouteHandler;
2520
-
2521
- if (!routeHandler) {
2522
- break;
2523
- }
2524
- }
2525
-
2526
-
2527
- if (method === 'OPTIONS' && typeof routeHandler?.[`/${method}/`] !== 'function') {
2528
- handleOptions = true;
2529
- routeHandler = routeHandler?.[`/${requestHeaders?.['access-control-request-method']}/`.toUpperCase()];
2530
- } else if (!responseBodyIsIncluded && typeof routeHandler?.[`/${method}/`] !== 'function') {
2531
- routeHandler = routeHandler?.[`/GET/`];
2532
- } else {
2533
- routeHandler = routeHandler?.[`/${method}/`];
2534
- }
2535
- }
2536
-
2537
- if (typeof routeHandler === 'function') {
2538
- request.setPathParams(pathParams);
2539
- response = await routeHandler(request, handleOptions);
2540
-
2541
- if (request.isHandledAsWebSocket()) {
2542
- await request.waitForWebSocketToClose();
2543
- return [null, null, null, true];
2544
- }
2545
- } else {
2546
- response = await handleNotFoundError?.(request);
2547
-
2548
- if (!(response instanceof Response)) {
2549
- response = new JsonResponse({
2550
- message: `Route ${method} "${path}" not found`
2551
- }, 404);
2552
- }
2553
- }
2554
- } catch (error) {
2555
- if (error instanceof Response) {
2556
- response = error;
2557
- } else {
2558
- safePrint(error, true);
2559
-
2560
- response = new JsonResponse({
2561
- message: 'Something went wrong'
2562
- }, 500);
2563
- }
2564
- }
2565
-
2566
- try {
2567
- return await Promise.all([response.getCode(requestHeaders), response.getHeaders(requestHeaders), responseBodyIsIncluded ? response.getBody(requestHeaders) : '', false]);
2568
- } catch (error) {
2569
- safePrint(error, true);
2570
-
2571
- response = new JsonResponse({
2572
- message: 'Something went wrong'
2573
- }, 500);
2574
-
2575
- return [response.getCode(), response.getHeaders(), responseBodyIsIncluded ? response.getBody() : '', false];
2576
- }
1
+ import * as http from 'node:http';
2
+ import * as querystring from "node:querystring";
3
+ import * as fs from "node:fs/promises";
4
+ import * as tls from "node:tls";
5
+ import * as net from "node:net";
6
+ import * as nodeCrypto from "node:crypto";
7
+
8
+ const servers = new Map();
9
+ const keepAliveTimeout = 20000;
10
+
11
+ let currentFsPromiseModule = fs;
12
+
13
+ function setObjectProperty(object, name, value, enumerable = true, writable = true, configurable = true) {
14
+ Object.defineProperty(
15
+ object,
16
+ name,
17
+ {
18
+ value,
19
+ enumerable,
20
+ writable,
21
+ configurable,
22
+ },
23
+ )
24
+ }
25
+
26
+ function safePrint(data, isError = false) {
27
+ try {
28
+ if (isError) {
29
+ console.error(data);
30
+ console.trace('Stack trace');
31
+ } else {
32
+ console.log(data);
33
+ }
34
+ } catch (e) {
35
+ console.error('Error printing data');
36
+ console.trace('Stack trace');
37
+ }
38
+ }
39
+
40
+ const escapeHtmlMap = {
41
+ "&": "&amp;",
42
+ "<": "&lt;",
43
+ ">": "&gt;",
44
+ '"': "&quot;",
45
+ "'": "&#39;",
46
+ }
47
+
48
+ const unescapeHtmlMap = {
49
+ "&nbsp;": "\u00A0",
50
+ "&lt;": "<",
51
+ "&gt;": ">",
52
+ "&amp;": "&",
53
+ "&quot;": "\"",
54
+ "&apos;": "'",
55
+ "&cent;": "¢",
56
+ "&pound;": "£",
57
+ "&yen;": "¥",
58
+ "&euro;": "€",
59
+ "&copy;": "©",
60
+ "&reg;": "®",
61
+ "&trade;": "™",
62
+
63
+ "&hellip;": "…",
64
+ "&ndash;": "–",
65
+ "&mdash;": "—",
66
+ "&lsquo;": "‘",
67
+ "&rsquo;": "’",
68
+ "&ldquo;": "“",
69
+ "&rdquo;": "”",
70
+ "&laquo;": "«",
71
+ "&raquo;": "»",
72
+
73
+ "&times;": "×",
74
+ "&divide;": "÷",
75
+ "&plusmn;": "±",
76
+ "&minus;": "−",
77
+ "&sup2;": "²",
78
+ "&sup3;": "³",
79
+ "&frac12;": "½",
80
+ "&frac14;": "¼",
81
+ "&frac34;": "¾",
82
+
83
+ "&deg;": "°",
84
+ "&micro;": "µ",
85
+ "&para;": "¶",
86
+ "&sect;": "§",
87
+ "&middot;": "·",
88
+ "&bull;": "•",
89
+
90
+ "&alpha;": "α",
91
+ "&beta;": "β",
92
+ "&gamma;": "γ",
93
+ "&pi;": "π",
94
+ "&sigma;": "σ",
95
+ "&omega;": "ω",
96
+ "&Alpha;": "Α",
97
+ "&Beta;": "Β",
98
+ "&Gamma;": "Γ",
99
+ "&Pi;": "Π",
100
+ "&Sigma;": "Σ",
101
+ "&Omega;": "Ω"
102
+ }
103
+
104
+ const mimeTypes = {
105
+ "123": "application/vnd.lotus-1-2-3",
106
+ "3dml": "text/vnd.in3d.3dml",
107
+ "3g2": "video/3gpp2",
108
+ "3gp": "video/3gpp",
109
+ "7z": "application/x-7z-compressed",
110
+ "aab": "application/x-authorware-bin",
111
+ "aac": "audio/aac",
112
+ "aam": "application/x-authorware-map",
113
+ "aas": "application/x-authorware-seg",
114
+ "abw": "application/x-abiword",
115
+ "ac": "application/pkix-attr-cert",
116
+ "acc": "application/vnd.americandynamics.acc",
117
+ "ace": "application/x-ace-compressed",
118
+ "acu": "application/vnd.acucobol",
119
+ "adp": "audio/adpcm",
120
+ "aep": "application/vnd.audiograph",
121
+ "afp": "application/vnd.ibm.modcap",
122
+ "ahead": "application/vnd.ahead.space",
123
+ "ai": "application/postscript",
124
+ "aif": "audio/x-aiff",
125
+ "aifc": "audio/x-aiff",
126
+ "aiff": "audio/x-aiff",
127
+ "air": "application/vnd.adobe.air-application-installer-package+zip",
128
+ "ait": "application/vnd.dvb.ait",
129
+ "ami": "application/vnd.amiga.ami",
130
+ "apk": "application/vnd.android.package-archive",
131
+ "apng": "image/apng",
132
+ "application": "application/x-ms-application",
133
+ "apr": "application/vnd.lotus-approach",
134
+ "arc": "application/x-freearc",
135
+ "asf": "video/x-ms-asf",
136
+ "aso": "application/vnd.accpac.simply.aso",
137
+ "atc": "application/vnd.acucorp",
138
+ "atom": "application/atom+xml",
139
+ "atomcat": "application/atomcat+xml",
140
+ "atomsvc": "application/atomsvc+xml",
141
+ "atx": "application/vnd.antix.game-component",
142
+ "au": "audio/basic",
143
+ "avi": "video/x-msvideo",
144
+ "avif": "image/avif",
145
+ "aw": "application/applixware",
146
+ "azf": "application/vnd.airzip.filesecure.azf",
147
+ "azs": "application/vnd.airzip.filesecure.azs",
148
+ "azw": "application/vnd.amazon.ebook",
149
+ "bcpio": "application/x-bcpio",
150
+ "bdf": "application/x-font-bdf",
151
+ "bdm": "application/vnd.syncml.dm+wbxml",
152
+ "bed": "application/vnd.realvnc.bed",
153
+ "bh2": "application/vnd.fujitsu.oasysprs",
154
+ "bin": "application/octet-stream",
155
+ "bmi": "application/vnd.bmi",
156
+ "bmp": "image/bmp",
157
+ "box": "application/vnd.previewsystems.box",
158
+ "btif": "image/prs.btif",
159
+ "bz": "application/x-bzip",
160
+ "bz2": "application/x-bzip2",
161
+ "c": "text/x-c",
162
+ "c11amc": "application/vnd.cluetrust.cartomobile-config",
163
+ "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg",
164
+ "c4g": "application/vnd.clonk.c4group",
165
+ "cab": "application/vnd.ms-cab-compressed",
166
+ "car": "application/vnd.curl.car",
167
+ "cat": "application/vnd.ms-pki.seccat",
168
+ "ccxml": "application/ccxml+xml,",
169
+ "cdbcmsg": "application/vnd.contact.cmsg",
170
+ "cdkey": "application/vnd.mediastation.cdkey",
171
+ "cdmia": "application/cdmi-capability",
172
+ "cdmic": "application/cdmi-container",
173
+ "cdmid": "application/cdmi-domain",
174
+ "cdmio": "application/cdmi-object",
175
+ "cdmiq": "application/cdmi-queue",
176
+ "cdx": "chemical/x-cdx",
177
+ "cdxml": "application/vnd.chemdraw+xml",
178
+ "cdy": "application/vnd.cinderella",
179
+ "cer": "application/pkix-cert",
180
+ "cgm": "image/cgm",
181
+ "chat": "application/x-chat",
182
+ "chm": "application/vnd.ms-htmlhelp",
183
+ "chrt": "application/vnd.kde.kchart",
184
+ "cif": "chemical/x-cif",
185
+ "cii": "application/vnd.anser-web-certificate-issue-initiation",
186
+ "cil": "application/vnd.ms-artgalry",
187
+ "cla": "application/vnd.claymore",
188
+ "class": "application/java-vm",
189
+ "clkk": "application/vnd.crick.clicker.keyboard",
190
+ "clkp": "application/vnd.crick.clicker.palette",
191
+ "clkt": "application/vnd.crick.clicker.template",
192
+ "clkw": "application/vnd.crick.clicker.wordbank",
193
+ "clkx": "application/vnd.crick.clicker",
194
+ "clp": "application/x-msclip",
195
+ "cmc": "application/vnd.cosmocaller",
196
+ "cmdf": "chemical/x-cmdf",
197
+ "cml": "chemical/x-cml",
198
+ "cmp": "application/vnd.yellowriver-custom-menu",
199
+ "cmx": "image/x-cmx",
200
+ "cod": "application/vnd.rim.cod",
201
+ "cpio": "application/x-cpio",
202
+ "cpt": "application/mac-compactpro",
203
+ "crd": "application/x-mscardfile",
204
+ "crl": "application/pkix-crl",
205
+ "cryptonote": "application/vnd.rig.cryptonote",
206
+ "csh": "application/x-csh",
207
+ "csml": "chemical/x-csml",
208
+ "csp": "application/vnd.commonspace",
209
+ "css": "text/css",
210
+ "csv": "text/csv",
211
+ "cu": "application/cu-seeme",
212
+ "curl": "text/vnd.curl",
213
+ "cww": "application/prs.cww",
214
+ "dae": "model/vnd.collada+xml",
215
+ "daf": "application/vnd.mobius.daf",
216
+ "davmount": "application/davmount+xml",
217
+ "dcurl": "text/vnd.curl.dcurl",
218
+ "dd2": "application/vnd.oma.dd2+xml",
219
+ "ddd": "application/vnd.fujixerox.ddd",
220
+ "deb": "application/x-debian-package",
221
+ "der": "application/x-x509-ca-cert",
222
+ "dfac": "application/vnd.dreamfactory",
223
+ "dir": "application/x-director",
224
+ "dis": "application/vnd.mobius.dis",
225
+ "djvu": "image/vnd.djvu",
226
+ "dmg": "application/x-apple-diskimage",
227
+ "dna": "application/vnd.dna",
228
+ "doc": "application/msword",
229
+ "docm": "application/vnd.ms-word.document.macroenabled.12",
230
+ "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
231
+ "dotm": "application/vnd.ms-word.template.macroenabled.12",
232
+ "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
233
+ "dp": "application/vnd.osgi.dp",
234
+ "dpg": "application/vnd.dpgraph",
235
+ "dra": "audio/vnd.dra",
236
+ "dsc": "text/prs.lines.tag",
237
+ "dssc": "application/dssc+der",
238
+ "dtb": "application/x-dtbook+xml",
239
+ "dtd": "application/xml-dtd",
240
+ "dts": "audio/vnd.dts",
241
+ "dtshd": "audio/vnd.dts.hd",
242
+ "dvi": "application/x-dvi",
243
+ "dwf": "model/vnd.dwf",
244
+ "dwg": "image/vnd.dwg",
245
+ "dxf": "image/vnd.dxf",
246
+ "dxp": "application/vnd.spotfire.dxp",
247
+ "ecelp4800": "audio/vnd.nuera.ecelp4800",
248
+ "ecelp7470": "audio/vnd.nuera.ecelp7470",
249
+ "ecelp9600": "audio/vnd.nuera.ecelp9600",
250
+ "edm": "application/vnd.novadigm.edm",
251
+ "edx": "application/vnd.novadigm.edx",
252
+ "efif": "application/vnd.picsel",
253
+ "ei6": "application/vnd.pg.osasli",
254
+ "eml": "message/rfc822",
255
+ "emma": "application/emma+xml",
256
+ "eol": "audio/vnd.digital-winds",
257
+ "eot": "application/vnd.ms-fontobject",
258
+ "epub": "application/epub+zip",
259
+ "es": "application/ecmascript",
260
+ "es3": "application/vnd.eszigno3+xml",
261
+ "esf": "application/vnd.epson.esf",
262
+ "etx": "text/x-setext",
263
+ "exe": "application/x-msdownload",
264
+ "exi": "application/exi",
265
+ "ext": "application/vnd.novadigm.ext",
266
+ "ez2": "application/vnd.ezpix-album",
267
+ "ez3": "application/vnd.ezpix-package",
268
+ "f": "text/x-fortran",
269
+ "f4v": "video/x-f4v",
270
+ "fbs": "image/vnd.fastbidsheet",
271
+ "fcs": "application/vnd.isac.fcs",
272
+ "fdf": "application/vnd.fdf",
273
+ "fe_launch": "application/vnd.denovo.fcselayout-link",
274
+ "fg5": "application/vnd.fujitsu.oasysgp",
275
+ "fh": "image/x-freehand",
276
+ "fig": "application/x-xfig",
277
+ "fli": "video/x-fli",
278
+ "flo": "application/vnd.micrografx.flo",
279
+ "flv": "video/x-flv",
280
+ "flw": "application/vnd.kde.kivio",
281
+ "flx": "text/vnd.fmi.flexstor",
282
+ "fly": "text/vnd.fly",
283
+ "fm": "application/vnd.framemaker",
284
+ "fnc": "application/vnd.frogans.fnc",
285
+ "fpx": "image/vnd.fpx",
286
+ "fsc": "application/vnd.fsc.weblaunch",
287
+ "fst": "image/vnd.fst",
288
+ "ftc": "application/vnd.fluxtime.clip",
289
+ "fti": "application/vnd.anser-web-funds-transfer-initiation",
290
+ "fvt": "video/vnd.fvt",
291
+ "fxp": "application/vnd.adobe.fxp",
292
+ "fzs": "application/vnd.fuzzysheet",
293
+ "g2w": "application/vnd.geoplan",
294
+ "g3": "image/g3fax",
295
+ "g3w": "application/vnd.geospace",
296
+ "gac": "application/vnd.groove-account",
297
+ "gdl": "model/vnd.gdl",
298
+ "geo": "application/vnd.dynageo",
299
+ "gex": "application/vnd.geometry-explorer",
300
+ "ggb": "application/vnd.geogebra.file",
301
+ "ggt": "application/vnd.geogebra.tool",
302
+ "ghf": "application/vnd.groove-help",
303
+ "gif": "image/gif",
304
+ "gim": "application/vnd.groove-identity-message",
305
+ "gmx": "application/vnd.gmx",
306
+ "gnumeric": "application/x-gnumeric",
307
+ "gph": "application/vnd.flographit",
308
+ "gqf": "application/vnd.grafeq",
309
+ "gram": "application/srgs",
310
+ "grv": "application/vnd.groove-injector",
311
+ "grxml": "application/srgs+xml",
312
+ "gsf": "application/x-font-ghostscript",
313
+ "gtar": "application/x-gtar",
314
+ "gtm": "application/vnd.groove-tool-message",
315
+ "gtw": "model/vnd.gtw",
316
+ "gv": "text/vnd.graphviz",
317
+ "gxt": "application/vnd.geonext",
318
+ "h261": "video/h261",
319
+ "h263": "video/h263",
320
+ "h264": "video/h264",
321
+ "hal": "application/vnd.hal+xml",
322
+ "hbci": "application/vnd.hbci",
323
+ "hdf": "application/x-hdf",
324
+ "hlp": "application/winhlp",
325
+ "hpgl": "application/vnd.hp-hpgl",
326
+ "hpid": "application/vnd.hp-hpid",
327
+ "hps": "application/vnd.hp-hps",
328
+ "hqx": "application/mac-binhex40",
329
+ "htke": "application/vnd.kenameaapp",
330
+ "html": "text/html",
331
+ "hvd": "application/vnd.yamaha.hv-dic",
332
+ "hvp": "application/vnd.yamaha.hv-voice",
333
+ "hvs": "application/vnd.yamaha.hv-script",
334
+ "i2g": "application/vnd.intergeo",
335
+ "icc": "application/vnd.iccprofile",
336
+ "ice": "x-conference/x-cooltalk",
337
+ "ico": "image/x-icon",
338
+ "ics": "text/calendar",
339
+ "ief": "image/ief",
340
+ "ifm": "application/vnd.shana.informed.formdata",
341
+ "igl": "application/vnd.igloader",
342
+ "igm": "application/vnd.insors.igm",
343
+ "igs": "model/iges",
344
+ "igx": "application/vnd.micrografx.igx",
345
+ "iif": "application/vnd.shana.informed.interchange",
346
+ "imp": "application/vnd.accpac.simply.imp",
347
+ "ims": "application/vnd.ms-ims",
348
+ "ipfix": "application/ipfix",
349
+ "ipk": "application/vnd.shana.informed.package",
350
+ "irm": "application/vnd.ibm.rights-management",
351
+ "irp": "application/vnd.irepository.package+xml",
352
+ "itp": "application/vnd.shana.informed.formtemplate",
353
+ "ivp": "application/vnd.immervision-ivp",
354
+ "ivu": "application/vnd.immervision-ivu",
355
+ "jad": "text/vnd.sun.j2me.app-descriptor",
356
+ "jam": "application/vnd.jam",
357
+ "jar": "application/java-archive",
358
+ "java": "text/x-java-source,java",
359
+ "jisp": "application/vnd.jisp",
360
+ "jlt": "application/vnd.hp-jlyt",
361
+ "jnlp": "application/x-java-jnlp-file",
362
+ "joda": "application/vnd.joost.joda-archive",
363
+ "jpeg": "image/jpeg",
364
+ "jpg": "image/jpeg",
365
+ "jpgv": "video/jpeg",
366
+ "jpm": "video/jpm",
367
+ "js": "text/javascript",
368
+ "json": "application/json",
369
+ "jsonld": "application/ld+json",
370
+ "karbon": "application/vnd.kde.karbon",
371
+ "kfo": "application/vnd.kde.kformula",
372
+ "kia": "application/vnd.kidspiration",
373
+ "kml": "application/vnd.google-earth.kml+xml",
374
+ "kmz": "application/vnd.google-earth.kmz",
375
+ "kne": "application/vnd.kinar",
376
+ "kon": "application/vnd.kde.kontour",
377
+ "kpr": "application/vnd.kde.kpresenter",
378
+ "ksp": "application/vnd.kde.kspread",
379
+ "ktx": "image/ktx",
380
+ "ktz": "application/vnd.kahootz",
381
+ "kwd": "application/vnd.kde.kword",
382
+ "lasxml": "application/vnd.las.las+xml",
383
+ "latex": "application/x-latex",
384
+ "lbd": "application/vnd.llamagraphics.life-balance.desktop",
385
+ "lbe": "application/vnd.llamagraphics.life-balance.exchange+xml",
386
+ "les": "application/vnd.hhe.lesson-player",
387
+ "link66": "application/vnd.route66.link66+xml",
388
+ "lrm": "application/vnd.ms-lrm",
389
+ "ltf": "application/vnd.frogans.ltf",
390
+ "lvp": "audio/vnd.lucent.voice",
391
+ "lwp": "application/vnd.lotus-wordpro",
392
+ "m21": "application/mp21",
393
+ "m3u": "audio/x-mpegurl",
394
+ "m3u8": "application/vnd.apple.mpegurl",
395
+ "m4a": "audio/mp4",
396
+ "m4v": "video/x-m4v",
397
+ "ma": "application/mathematica",
398
+ "mads": "application/mads+xml",
399
+ "mag": "application/vnd.ecowin.chart",
400
+ "mathml": "application/mathml+xml",
401
+ "mbk": "application/vnd.mobius.mbk",
402
+ "mbox": "application/mbox",
403
+ "mc1": "application/vnd.medcalcdata",
404
+ "mcd": "application/vnd.mcd",
405
+ "mcurl": "text/vnd.curl.mcurl",
406
+ "mdb": "application/x-msaccess",
407
+ "mdi": "image/vnd.ms-modi",
408
+ "meta4": "application/metalink4+xml",
409
+ "mets": "application/mets+xml",
410
+ "mfm": "application/vnd.mfmp",
411
+ "mgp": "application/vnd.osgeo.mapguide.package",
412
+ "mgz": "application/vnd.proteus.magazine",
413
+ "mid": "audio/midi",
414
+ "midi": "audio/midi",
415
+ "mif": "application/vnd.mif",
416
+ "mj2": "video/mj2",
417
+ "mjs": "text/javascript",
418
+ "mkv": "video/x-matroska",
419
+ "mlp": "application/vnd.dolby.mlp",
420
+ "mmd": "application/vnd.chipnuts.karaoke-mmd",
421
+ "mmf": "application/vnd.smaf",
422
+ "mmr": "image/vnd.fujixerox.edmics-mmr",
423
+ "mny": "application/x-msmoney",
424
+ "mods": "application/mods+xml",
425
+ "movie": "video/x-sgi-movie",
426
+ "mp3": "audio/mpeg",
427
+ "mp4": "video/mp4",
428
+ "mp4a": "audio/mp4",
429
+ "mpc": "application/vnd.mophun.certificate",
430
+ "mpeg": "video/mpeg",
431
+ "mpga": "audio/mpeg",
432
+ "mpkg": "application/vnd.apple.installer+xml",
433
+ "mpm": "application/vnd.blueice.multipass",
434
+ "mpn": "application/vnd.mophun.application",
435
+ "mpp": "application/vnd.ms-project",
436
+ "mpy": "application/vnd.ibm.minipay",
437
+ "mqy": "application/vnd.mobius.mqy",
438
+ "mrc": "application/marc",
439
+ "mrcx": "application/marcxml+xml",
440
+ "mscml": "application/mediaservercontrol+xml",
441
+ "mseq": "application/vnd.mseq",
442
+ "msf": "application/vnd.epson.msf",
443
+ "msh": "model/mesh",
444
+ "msl": "application/vnd.mobius.msl",
445
+ "msty": "application/vnd.muvee.style",
446
+ "mts": "model/vnd.mts",
447
+ "mus": "application/vnd.musician",
448
+ "musicxml": "application/vnd.recordare.musicxml+xml",
449
+ "mvb": "application/x-msmediaview",
450
+ "mwf": "application/vnd.mfer",
451
+ "mxf": "application/mxf",
452
+ "mxl": "application/vnd.recordare.musicxml",
453
+ "mxml": "application/xv+xml",
454
+ "mxs": "application/vnd.triscape.mxs",
455
+ "mxu": "video/vnd.mpegurl",
456
+ "n-gage": "application/vnd.nokia.n-gage.symbian.install",
457
+ "N/A": "application/andrew-inset",
458
+ "n3": "text/n3",
459
+ "nbp": "application/vnd.wolfram.player",
460
+ "nc": "application/x-netcdf",
461
+ "ncx": "application/x-dtbncx+xml",
462
+ "ngdat": "application/vnd.nokia.n-gage.data",
463
+ "nlu": "application/vnd.neurolanguage.nlu",
464
+ "nml": "application/vnd.enliven",
465
+ "nnd": "application/vnd.noblenet-directory",
466
+ "nns": "application/vnd.noblenet-sealer",
467
+ "nnw": "application/vnd.noblenet-web",
468
+ "npx": "image/vnd.net-fpx",
469
+ "nsf": "application/vnd.lotus-notes",
470
+ "oa2": "application/vnd.fujitsu.oasys2",
471
+ "oa3": "application/vnd.fujitsu.oasys3",
472
+ "oas": "application/vnd.fujitsu.oasys",
473
+ "obd": "application/x-msbinder",
474
+ "oda": "application/oda",
475
+ "odb": "application/vnd.oasis.opendocument.database",
476
+ "odc": "application/vnd.oasis.opendocument.chart",
477
+ "odf": "application/vnd.oasis.opendocument.formula",
478
+ "odft": "application/vnd.oasis.opendocument.formula-template",
479
+ "odg": "application/vnd.oasis.opendocument.graphics",
480
+ "odi": "application/vnd.oasis.opendocument.image",
481
+ "odm": "application/vnd.oasis.opendocument.text-master",
482
+ "odp": "application/vnd.oasis.opendocument.presentation",
483
+ "ods": "application/vnd.oasis.opendocument.spreadsheet",
484
+ "odt": "application/vnd.oasis.opendocument.text",
485
+ "oga": "audio/ogg",
486
+ "ogv": "video/ogg",
487
+ "ogx": "application/ogg",
488
+ "onetoc": "application/onenote",
489
+ "opf": "application/oebps-package+xml",
490
+ "opus": "audio/ogg",
491
+ "org": "application/vnd.lotus-organizer",
492
+ "osf": "application/vnd.yamaha.openscoreformat",
493
+ "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml",
494
+ "otc": "application/vnd.oasis.opendocument.chart-template",
495
+ "otf": "font/otf",
496
+ "otg": "application/vnd.oasis.opendocument.graphics-template",
497
+ "oth": "application/vnd.oasis.opendocument.text-web",
498
+ "oti": "application/vnd.oasis.opendocument.image-template",
499
+ "otp": "application/vnd.oasis.opendocument.presentation-template",
500
+ "ots": "application/vnd.oasis.opendocument.spreadsheet-template",
501
+ "ott": "application/vnd.oasis.opendocument.text-template",
502
+ "oxt": "application/vnd.openofficeorg.extension",
503
+ "p": "text/x-pascal",
504
+ "p10": "application/pkcs10",
505
+ "p12": "application/x-pkcs12",
506
+ "p7b": "application/x-pkcs7-certificates",
507
+ "p7m": "application/pkcs7-mime",
508
+ "p7r": "application/x-pkcs7-certreqresp",
509
+ "p7s": "application/pkcs7-signature",
510
+ "p8": "application/pkcs8",
511
+ "par": "text/plain-bas",
512
+ "paw": "application/vnd.pawaafile",
513
+ "pbd": "application/vnd.powerbuilder6",
514
+ "pbm": "image/x-portable-bitmap",
515
+ "pcf": "application/x-font-pcf",
516
+ "pcl": "application/vnd.hp-pcl",
517
+ "pclxl": "application/vnd.hp-pclxl",
518
+ "pcurl": "application/vnd.curl.pcurl",
519
+ "pcx": "image/x-pcx",
520
+ "pdb": "application/vnd.palm",
521
+ "pdf": "application/pdf",
522
+ "pfa": "application/x-font-type1",
523
+ "pfr": "application/font-tdpfr",
524
+ "pgm": "image/x-portable-graymap",
525
+ "pgn": "application/x-chess-pgn",
526
+ "pgp": "application/pgp-signature",
527
+ "pic": "image/x-pict",
528
+ "pjpeg": "image/pjpeg",
529
+ "pki": "application/pkixcmp",
530
+ "pkipath": "application/pkix-pkipath",
531
+ "plb": "application/vnd.3gpp.pic-bw-large",
532
+ "plc": "application/vnd.mobius.plc",
533
+ "plf": "application/vnd.pocketlearn",
534
+ "pls": "application/pls+xml",
535
+ "pml": "application/vnd.ctc-posml",
536
+ "png": "image/png",
537
+ "pnm": "image/x-portable-anymap",
538
+ "portpkg": "application/vnd.macports.portpkg",
539
+ "potm": "application/vnd.ms-powerpoint.template.macroenabled.12",
540
+ "potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
541
+ "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12",
542
+ "ppd": "application/vnd.cups-ppd",
543
+ "ppm": "image/x-portable-pixmap",
544
+ "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12",
545
+ "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
546
+ "ppt": "application/vnd.ms-powerpoint",
547
+ "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12",
548
+ "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
549
+ "prc": "application/x-mobipocket-ebook",
550
+ "pre": "application/vnd.lotus-freelance",
551
+ "prf": "application/pics-rules",
552
+ "psb": "application/vnd.3gpp.pic-bw-small",
553
+ "psd": "image/vnd.adobe.photoshop",
554
+ "psf": "application/x-font-linux-psf",
555
+ "pskcxml": "application/pskc+xml",
556
+ "ptid": "application/vnd.pvi.ptid1",
557
+ "pub": "application/x-mspublisher",
558
+ "pvb": "application/vnd.3gpp.pic-bw-var",
559
+ "pwn": "application/vnd.3m.post-it-notes",
560
+ "pya": "audio/vnd.ms-playready.media.pya",
561
+ "pyv": "video/vnd.ms-playready.media.pyv",
562
+ "qam": "application/vnd.epson.quickanime",
563
+ "qbo": "application/vnd.intu.qbo",
564
+ "qfx": "application/vnd.intu.qfx",
565
+ "qps": "application/vnd.publishare-delta-tree",
566
+ "qt": "video/quicktime",
567
+ "qxd": "application/vnd.quark.quarkxpress",
568
+ "ra": "audio/vnd.rn-realaudio",
569
+ "ram": "audio/vnd.rn-realaudio",
570
+ "rar": "application/x-rar-compressed",
571
+ "ras": "image/x-cmu-raster",
572
+ "rcprofile": "application/vnd.ipunplugged.rcprofile",
573
+ "rdf": "application/rdf+xml",
574
+ "rdz": "application/vnd.data-vision.rdz",
575
+ "rep": "application/vnd.businessobjects",
576
+ "res": "application/x-dtbresource+xml",
577
+ "rgb": "image/x-rgb",
578
+ "rif": "application/reginfo+xml",
579
+ "rip": "audio/vnd.rip",
580
+ "rl": "application/resource-lists+xml",
581
+ "rlc": "image/vnd.fujixerox.edmics-rlc",
582
+ "rld": "application/resource-lists-diff+xml",
583
+ "rm": "application/vnd.rn-realmedia",
584
+ "rmi": "audio/mid",
585
+ "rmp": "audio/x-pn-realaudio-plugin",
586
+ "rms": "application/vnd.jcp.javame.midlet-rms",
587
+ "rnc": "application/relax-ng-compact-syntax",
588
+ "rp9": "application/vnd.cloanto.rp9",
589
+ "rpss": "application/vnd.nokia.radio-presets",
590
+ "rpst": "application/vnd.nokia.radio-preset",
591
+ "rq": "application/sparql-query",
592
+ "rs": "application/rls-services+xml",
593
+ "rsd": "application/rsd+xml",
594
+ "rss": "application/rss+xml",
595
+ "rtf": "application/rtf",
596
+ "rtx": "text/richtext",
597
+ "s": "text/x-asm",
598
+ "saf": "application/vnd.yamaha.smaf-audio",
599
+ "sbml": "application/sbml+xml",
600
+ "sc": "application/vnd.ibm.secure-container",
601
+ "scd": "application/x-msschedule",
602
+ "scm": "application/vnd.lotus-screencam",
603
+ "scq": "application/scvp-cv-request",
604
+ "scs": "application/scvp-cv-response",
605
+ "scurl": "text/vnd.curl.scurl",
606
+ "sda": "application/vnd.stardivision.draw",
607
+ "sdc": "application/vnd.stardivision.calc",
608
+ "sdd": "application/vnd.stardivision.impress",
609
+ "sdkm": "application/vnd.solent.sdkm+xml",
610
+ "sdp": "application/sdp",
611
+ "sdw": "application/vnd.stardivision.writer",
612
+ "see": "application/vnd.seemail",
613
+ "seed": "application/vnd.fdsn.seed",
614
+ "sema": "application/vnd.sema",
615
+ "semd": "application/vnd.semd",
616
+ "semf": "application/vnd.semf",
617
+ "ser": "application/java-serialized-object",
618
+ "setpay": "application/set-payment-initiation",
619
+ "setreg": "application/set-registration-initiation",
620
+ "sfd-hdstx": "application/vnd.hydrostatix.sof-data",
621
+ "sfs": "application/vnd.spotfire.sfs",
622
+ "sgl": "application/vnd.stardivision.writer-global",
623
+ "sgml": "text/sgml",
624
+ "sh": "application/x-sh",
625
+ "shar": "application/x-shar",
626
+ "shf": "application/shf+xml",
627
+ "sis": "application/vnd.symbian.install",
628
+ "sit": "application/x-stuffit",
629
+ "sitx": "application/x-stuffitx",
630
+ "skp": "application/vnd.koan",
631
+ "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12",
632
+ "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
633
+ "slt": "application/vnd.epson.salt",
634
+ "sm": "application/vnd.stepmania.stepchart",
635
+ "smf": "application/vnd.stardivision.math",
636
+ "smi": "application/smil+xml",
637
+ "snd": "audio/basic",
638
+ "snf": "application/x-font-snf",
639
+ "spf": "application/vnd.yamaha.smaf-phrase",
640
+ "spl": "application/x-futuresplash",
641
+ "spot": "text/vnd.in3d.spot",
642
+ "spp": "application/scvp-vp-response",
643
+ "spq": "application/scvp-vp-request",
644
+ "src": "application/x-wais-source",
645
+ "sru": "application/sru+xml",
646
+ "srx": "application/sparql-results+xml",
647
+ "sse": "application/vnd.kodak-descriptor",
648
+ "ssf": "application/vnd.epson.ssf",
649
+ "ssml": "application/ssml+xml",
650
+ "st": "application/vnd.sailingtracker.track",
651
+ "stc": "application/vnd.sun.xml.calc.template",
652
+ "std": "application/vnd.sun.xml.draw.template",
653
+ "stf": "application/vnd.wt.stf",
654
+ "sti": "application/vnd.sun.xml.impress.template",
655
+ "stk": "application/hyperstudio",
656
+ "stl": "application/vnd.ms-pki.stl",
657
+ "str": "application/vnd.pg.format",
658
+ "stw": "application/vnd.sun.xml.writer.template",
659
+ "sub": "image/vnd.dvb.subtitle",
660
+ "sus": "application/vnd.sus-calendar",
661
+ "sv4cpio": "application/x-sv4cpio",
662
+ "sv4crc": "application/x-sv4crc",
663
+ "svc": "application/vnd.dvb.service",
664
+ "svd": "application/vnd.svd",
665
+ "svg": "image/svg+xml",
666
+ "swf": "application/x-shockwave-flash",
667
+ "swi": "application/vnd.aristanetworks.swi",
668
+ "sxc": "application/vnd.sun.xml.calc",
669
+ "sxd": "application/vnd.sun.xml.draw",
670
+ "sxg": "application/vnd.sun.xml.writer.global",
671
+ "sxi": "application/vnd.sun.xml.impress",
672
+ "sxm": "application/vnd.sun.xml.math",
673
+ "sxw": "application/vnd.sun.xml.writer",
674
+ "t": "text/troff",
675
+ "tao": "application/vnd.tao.intent-module-archive",
676
+ "tar": "application/x-tar",
677
+ "tcap": "application/vnd.3gpp2.tcap",
678
+ "tcl": "application/x-tcl",
679
+ "teacher": "application/vnd.smart.teacher",
680
+ "tei": "application/tei+xml",
681
+ "tex": "application/x-tex",
682
+ "texinfo": "application/x-texinfo",
683
+ "tfi": "application/thraud+xml",
684
+ "tfm": "application/x-tex-tfm",
685
+ "thmx": "application/vnd.ms-officetheme",
686
+ "tif": "image/tiff",
687
+ "tiff": "image/tiff",
688
+ "tmo": "application/vnd.tmobile-livetv",
689
+ "torrent": "application/x-bittorrent",
690
+ "tpl": "application/vnd.groove-tool-template",
691
+ "tpt": "application/vnd.trid.tpt",
692
+ "tra": "application/vnd.trueapp",
693
+ "trm": "application/x-msterminal",
694
+ "ts": "video/mp2t",
695
+ "tsd": "application/timestamped-data",
696
+ "tsv": "text/tab-separated-values",
697
+ "ttf": "font/ttf",
698
+ "ttl": "text/turtle",
699
+ "twd": "application/vnd.simtech-mindmapper",
700
+ "txd": "application/vnd.genomatix.tuxedo",
701
+ "txf": "application/vnd.mobius.txf",
702
+ "txt": "text/plain",
703
+ "ufd": "application/vnd.ufdl",
704
+ "umj": "application/vnd.umajin",
705
+ "unityweb": "application/vnd.unity",
706
+ "uoml": "application/vnd.uoml+xml",
707
+ "uri": "text/uri-list",
708
+ "ustar": "application/x-ustar",
709
+ "utz": "application/vnd.uiq.theme",
710
+ "uu": "text/x-uuencode",
711
+ "uva": "audio/vnd.dece.audio",
712
+ "uvh": "video/vnd.dece.hd",
713
+ "uvi": "image/vnd.dece.graphic",
714
+ "uvm": "video/vnd.dece.mobile",
715
+ "uvp": "video/vnd.dece.pd",
716
+ "uvs": "video/vnd.dece.sd",
717
+ "uvu": "video/vnd.uvvu.mp4",
718
+ "uvv": "video/vnd.dece.video",
719
+ "vcd": "application/x-cdlink",
720
+ "vcf": "text/x-vcard",
721
+ "vcg": "application/vnd.groove-vcard",
722
+ "vcs": "text/x-vcalendar",
723
+ "vcx": "application/vnd.vcx",
724
+ "vis": "application/vnd.visionary",
725
+ "viv": "video/vnd.vivo",
726
+ "vsd": "application/vnd.visio",
727
+ "vsdx": "application/vnd.visio2013",
728
+ "vsf": "application/vnd.vsf",
729
+ "vtu": "model/vnd.vtu",
730
+ "vxml": "application/voicexml+xml",
731
+ "wad": "application/x-doom",
732
+ "wav": "audio/wav",
733
+ "wax": "audio/x-ms-wax",
734
+ "wbmp": "image/vnd.wap.wbmp",
735
+ "wbs": "application/vnd.criticaltools.wbs+xml",
736
+ "wbxml": "application/vnd.wap.wbxml",
737
+ "weba": "audio/webm",
738
+ "webm": "video/webm",
739
+ "webp": "image/webp",
740
+ "wg": "application/vnd.pmi.widget",
741
+ "wgt": "application/widget",
742
+ "wm": "video/x-ms-wm",
743
+ "wma": "audio/x-ms-wma",
744
+ "wmd": "application/x-ms-wmd",
745
+ "wmf": "application/x-msmetafile",
746
+ "wml": "text/vnd.wap.wml",
747
+ "wmlc": "application/vnd.wap.wmlc",
748
+ "wmls": "text/vnd.wap.wmlscript",
749
+ "wmlsc": "application/vnd.wap.wmlscriptc",
750
+ "wmv": "video/x-ms-wmv",
751
+ "wmx": "video/x-ms-wmx",
752
+ "wmz": "application/x-ms-wmz",
753
+ "woff": "font/woff",
754
+ "woff2": "font/woff2",
755
+ "wpd": "application/vnd.wordperfect",
756
+ "wpl": "application/vnd.ms-wpl",
757
+ "wps": "application/vnd.ms-works",
758
+ "wqd": "application/vnd.wqd",
759
+ "wri": "application/x-mswrite",
760
+ "wrl": "model/vrml",
761
+ "wsdl": "application/wsdl+xml",
762
+ "wspolicy": "application/wspolicy+xml",
763
+ "wtb": "application/vnd.webturbo",
764
+ "wvx": "video/x-ms-wvx",
765
+ "x3d": "application/vnd.hzn-3d-crossword",
766
+ "xap": "application/x-silverlight-app",
767
+ "xar": "application/vnd.xara",
768
+ "xbap": "application/x-ms-xbap",
769
+ "xbd": "application/vnd.fujixerox.docuworks.binder",
770
+ "xbm": "image/x-xbitmap",
771
+ "xdf": "application/xcap-diff+xml",
772
+ "xdm": "application/vnd.syncml.dm+xml",
773
+ "xdp": "application/vnd.adobe.xdp+xml",
774
+ "xdssc": "application/dssc+xml",
775
+ "xdw": "application/vnd.fujixerox.docuworks",
776
+ "xenc": "application/xenc+xml",
777
+ "xer": "application/patch-ops-error+xml",
778
+ "xfdf": "application/vnd.adobe.xfdf",
779
+ "xfdl": "application/vnd.xfdl",
780
+ "xhtml": "application/xhtml+xml",
781
+ "xif": "image/vnd.xiff",
782
+ "xlam": "application/vnd.ms-excel.addin.macroenabled.12",
783
+ "xls": "application/vnd.ms-excel",
784
+ "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12",
785
+ "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12",
786
+ "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
787
+ "xltm": "application/vnd.ms-excel.template.macroenabled.12",
788
+ "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
789
+ "xml": "application/xml",
790
+ "xo": "application/vnd.olpc-sugar",
791
+ "xop": "application/xop+xml",
792
+ "xpi": "application/x-xpinstall",
793
+ "xpm": "image/x-xpixmap",
794
+ "xpr": "application/vnd.is-xpr",
795
+ "xps": "application/vnd.ms-xpsdocument",
796
+ "xpw": "application/vnd.intercon.formnet",
797
+ "xslt": "application/xslt+xml",
798
+ "xsm": "application/vnd.syncml+xml",
799
+ "xspf": "application/xspf+xml",
800
+ "xul": "application/vnd.mozilla.xul+xml",
801
+ "xwd": "image/x-xwindowdump",
802
+ "xyz": "chemical/x-xyz",
803
+ "yaml": "text/yaml",
804
+ "yang": "application/yang",
805
+ "yin": "application/yin+xml",
806
+ "zaz": "application/vnd.zzazz.deck+xml",
807
+ "zip": "application/zip",
808
+ "zir": "application/vnd.zul",
809
+ "zmm": "application/vnd.handheld-entertainment+xml",
810
+ __proto__: null,
811
+ };
812
+
813
+ export function getServerFsPromiseModule() {
814
+ return currentFsPromiseModule;
815
+ }
816
+
817
+ export function setServerFsPromiseModule(fsPromiseModule) {
818
+ currentFsPromiseModule = fsPromiseModule;
819
+ clearStaticCache();
820
+ }
821
+
822
+ export function getExtension(fileNameOrPath) {
823
+ return fileNameOrPath?.toLowerCase().split('.').at(-1) ?? '';
824
+ }
825
+
826
+ export function getContentTypeByExtension(fileNameOrPath) {
827
+ return mimeTypes[getExtension(fileNameOrPath)] ?? 'application/octet-stream';
828
+ }
829
+
830
+ export class UploadedFile {
831
+ #content;
832
+ #contentType;
833
+ #fileName;
834
+
835
+ constructor(content, contentType = 'application/octet-stream', fileName = '') {
836
+ this.#content = content;
837
+ this.#contentType = contentType;
838
+ this.#fileName = fileName;
839
+ }
840
+
841
+ getContent() {
842
+ return this.#content;
843
+ }
844
+
845
+ getContentType() {
846
+ return this.#contentType;
847
+ }
848
+
849
+ getExtension() {
850
+ return getExtension(this.#fileName);
851
+ }
852
+
853
+ getContentTypeByExtension() {
854
+ return getContentTypeByExtension(this.#fileName);
855
+ }
856
+
857
+ getFileName() {
858
+ return this.#fileName;
859
+ }
860
+
861
+ toJSON() {
862
+ return {
863
+ content: this.#content,
864
+ contentType: this.#contentType,
865
+ fileName: this.#fileName,
866
+ };
867
+ }
868
+ }
869
+
870
+ export class Request {
871
+ #method;
872
+ #headers = {};
873
+ #path = '';
874
+ #queryParams = {};
875
+ #pathParams = {};
876
+
877
+ #rawBodyPromise;
878
+ #bodyPromise;
879
+ #allParams;
880
+ #cookies;
881
+
882
+ #request;
883
+ #customData = null;
884
+ #webSocketData = null;
885
+ #heads = null;
886
+ #isWebSocketClosed = false;
887
+
888
+ constructor(request, heads = null) {
889
+ this.#request = request;
890
+ this.#heads = heads;
891
+
892
+ this.#method = request.method.toUpperCase();
893
+
894
+ for (const k in request.headers) {
895
+ setObjectProperty(this.#headers, k, request.headers[k]);
896
+ }
897
+
898
+ try {
899
+ const url = new URL(`http://localhost${request.url}`);
900
+
901
+ this.#path = url.pathname.split('/').filter(x => x).join('/');
902
+
903
+ url.searchParams.forEach((v, k) => {
904
+ setObjectProperty(this.#queryParams, k, v);
905
+ });
906
+ } catch (error) {
907
+ safePrint(error, true);
908
+ }
909
+ }
910
+
911
+ isAlive() {
912
+ return !(this.#request.socket.writableEnded || this.#request.socket.destroyed);
913
+ }
914
+
915
+ getMethod() {
916
+ return this.#method;
917
+ }
918
+
919
+ getHeaders() {
920
+ return this.#headers;
921
+ }
922
+
923
+ getIp() {
924
+ return this.#request.socket.remoteAddress;
925
+ }
926
+
927
+ getPort() {
928
+ return this.#request.socket.address().port;
929
+ }
930
+
931
+ getProtocol() {
932
+ const socket = this.#request.socket;
933
+
934
+ if (socket instanceof tls.TLSSocket && socket.encrypted) {
935
+ return 'https';
936
+ }
937
+
938
+ return 'http';
939
+ }
940
+
941
+ getHost() {
942
+ return this.#request.headers.host;
943
+ }
944
+
945
+ getPath() {
946
+ return this.#path;
947
+ }
948
+
949
+ getQueryParams() {
950
+ return this.#queryParams;
951
+ }
952
+
953
+ setPathParams(params) {
954
+ this.#pathParams = params;
955
+ }
956
+
957
+ getPathParams() {
958
+ return this.#pathParams;
959
+ }
960
+
961
+ getRawUrl() {
962
+ return this.#request.url;
963
+ }
964
+
965
+ getRawBody() {
966
+ if (this.#webSocketData) {
967
+ return Buffer.from([]);
968
+ }
969
+
970
+ if (!this.#rawBodyPromise) {
971
+ this.#rawBodyPromise = new Promise((resolve, reject) => {
972
+ const body = [];
973
+
974
+ this.#request.on('data', (chunk) => {
975
+ try {
976
+ body.push(chunk);
977
+ } catch (error) {
978
+ reject(error);
979
+ }
980
+ }).on('end', () => {
981
+ try {
982
+ resolve(Buffer.concat(body));
983
+ } catch (error) {
984
+ reject(error);
985
+ }
986
+ }).on('timeout', (error) => {
987
+ reject(error);
988
+ }).on('error', (error) => {
989
+ reject(error);
990
+ });
991
+ });
992
+ }
993
+
994
+ return this.#rawBodyPromise;
995
+ }
996
+
997
+ getBody() {
998
+ if (this.#webSocketData) {
999
+ return {};
1000
+ }
1001
+
1002
+ if (!this.#bodyPromise) {
1003
+ this.#bodyPromise = new Promise((resolve, reject) => {
1004
+ const maxJsonSize = 32 * 1024 * 1024;
1005
+ const maxUrlSize = 1024 * 1024;
1006
+
1007
+ const contentType = this.#headers['content-type'] ?? '';
1008
+
1009
+ if (this.#method === 'GET' && !contentType.includes('application/x-www-form-urlencoded')) {
1010
+ resolve(this.#normalizeFormFields(this.#queryParams));
1011
+ }
1012
+
1013
+ this.getRawBody().then(async body => {
1014
+ if (contentType.includes('application/x-www-form-urlencoded')) {
1015
+ try {
1016
+ if (body.length > maxUrlSize) {
1017
+ throw new Error(`The URL request size (${body.length} bytes) exceeds ${maxUrlSize} bytes`);
1018
+ }
1019
+
1020
+ resolve(this.#parseUrlEncodedForm(body));
1021
+ } catch (error) {
1022
+ reject(error);
1023
+ }
1024
+ } else if (contentType.includes('multipart/form-data')) {
1025
+ try {
1026
+ resolve(await this.#parseMultipart(body, contentType));
1027
+ } catch (error) {
1028
+ reject(error);
1029
+ }
1030
+ } else if (contentType.includes('application/json')) {
1031
+ try {
1032
+ if (body.length > maxJsonSize) {
1033
+ throw new Error(`The JSON request size (${body.length} bytes) exceeds ${maxJsonSize} bytes`);
1034
+ }
1035
+
1036
+ resolve(JSON.parse(body.toString()));
1037
+ } catch (error) {
1038
+ reject(error);
1039
+ }
1040
+ } else {
1041
+ resolve({ body });
1042
+ }
1043
+ }).catch(error => {
1044
+ reject(error);
1045
+ });
1046
+ });
1047
+ }
1048
+
1049
+ return this.#bodyPromise;
1050
+ }
1051
+
1052
+ #normalizeFormFields(fields) {
1053
+ const result = {};
1054
+
1055
+ for (const property in fields) {
1056
+ const newpProp = property.replace(/\s+/gm, '').replaceAll('[]', '[-1]');
1057
+ const pathToProperty = newpProp.split(/]\[|]|\[|\./gm).filter(x => x);
1058
+
1059
+ let parent = result;
1060
+
1061
+ for (let i = 0; i < pathToProperty.length; ++i) {
1062
+ const curFragment = pathToProperty[i];
1063
+ const nextFragment = i < pathToProperty.length - 1 ? pathToProperty[i + 1] : null;
1064
+
1065
+ const nextParent = parent[curFragment] ?? (nextFragment ? (nextFragment.match(/^-?\d+$/gm) ? [] : {}) : fields[property]);
1066
+
1067
+ if (typeof parent === 'object') {
1068
+ if (parent instanceof Array && curFragment.match(/^-?\d+$/gm)) {
1069
+ const index = Math.min(+curFragment, parent.length + 10000);
1070
+
1071
+ if (index <= -1) {
1072
+ if (nextParent instanceof Array) {
1073
+ parent.push(...nextParent);
1074
+ } else if (nextParent !== null) {
1075
+ parent.push(nextParent);
1076
+ }
1077
+ } else {
1078
+ while (parent.length <= index) {
1079
+ parent.push(null);
1080
+ }
1081
+
1082
+ parent[index] = nextParent;
1083
+ }
1084
+ } else if (!(parent instanceof Array) && !curFragment.match(/^-?\d+$/gm)) {
1085
+ setObjectProperty(parent, curFragment, nextParent);
1086
+ } else {
1087
+ break;
1088
+ }
1089
+ } else {
1090
+ break;
1091
+ }
1092
+
1093
+ parent = nextParent;
1094
+ }
1095
+ }
1096
+
1097
+ return result;
1098
+ }
1099
+
1100
+ async #parseMultipart(body, header) {
1101
+ const boundaryString = header.match(/(?<=boundary=)\S+/gm)?.[0];
1102
+
1103
+ const boundaryBuffer = Buffer.from('--' + boundaryString);
1104
+ const partPositions = [];
1105
+ let lastPosition = { start: -1, end: -1 };
1106
+
1107
+ function isBoundaryEnd(boundary, index, b) {
1108
+ let i = boundary.length - 1;
1109
+
1110
+ if (index < i) {
1111
+ return false;
1112
+ }
1113
+
1114
+ do {
1115
+ if (boundary[i--] !== b[index--]) {
1116
+ return false;
1117
+ }
1118
+ } while (i >= 0);
1119
+
1120
+ return true;
1121
+ }
1122
+
1123
+ const maxSyncProcessedChunkSize = 40000000;
1124
+
1125
+ for (let offset = 0; offset < body.length; offset += maxSyncProcessedChunkSize) {
1126
+ const offsetEnd = Math.min(body.length, offset + maxSyncProcessedChunkSize);
1127
+
1128
+ for (let i = offset; i < offsetEnd; ++i) {
1129
+ if (isBoundaryEnd(boundaryBuffer, i, body)) {
1130
+ lastPosition.end = i - boundaryBuffer.length - 1;
1131
+
1132
+ lastPosition = {
1133
+ start: i + 3,
1134
+ end: -1,
1135
+ };
1136
+
1137
+ partPositions.push(lastPosition);
1138
+ }
1139
+ }
1140
+
1141
+ if (offsetEnd < body.length) {
1142
+ await new Promise((resolve) => setTimeout(resolve, 30));
1143
+ }
1144
+ }
1145
+
1146
+ partPositions.pop();
1147
+
1148
+ const newLineBuffer = Buffer.from('\r\n\r\n');
1149
+
1150
+ const data = {};
1151
+
1152
+ for (const position of partPositions) {
1153
+ let end = position.end;
1154
+
1155
+ for (let i = position.start; i < position.end; ++i) {
1156
+ if (isBoundaryEnd(newLineBuffer, i, body)) {
1157
+ end = i - 3;
1158
+ break;
1159
+ }
1160
+ }
1161
+
1162
+ const info = body.toString('utf-8', position.start, end);
1163
+
1164
+ const name = decodeURIComponent(info.match(/(?<=name=")[^"]*/gm)?.[0] ?? '');
1165
+ const fileName = decodeURIComponent(info.match(/(?<=filename=")[^"]*/gm)?.[0] ?? '');
1166
+ const contentType = info.match(/(?<=^Content-Type:)[^\n]+/gm)?.[0]?.trim() ?? '';
1167
+
1168
+ if (!name) {
1169
+ continue;
1170
+ }
1171
+
1172
+ let value = null;
1173
+
1174
+ if (contentType) {
1175
+ if (fileName) {
1176
+ value = new UploadedFile(
1177
+ body.slice(end + 4, position.end),
1178
+ contentType,
1179
+ fileName
1180
+ );
1181
+ }
1182
+ } else {
1183
+ value = end === position.end ? '' : body.slice(end + 4, position.end).toString()
1184
+ }
1185
+
1186
+ if (name in data && !Array.isArray(data[name])) {
1187
+ setObjectProperty(data, name, [data[name]]);
1188
+ }
1189
+
1190
+ if (Array.isArray(data[name])) {
1191
+ if (value != null) {
1192
+ data[name].push(value);
1193
+ }
1194
+
1195
+ } else {
1196
+ setObjectProperty(data, name, value);
1197
+ }
1198
+ }
1199
+
1200
+ return this.#normalizeFormFields(data);
1201
+ }
1202
+
1203
+ #parseUrlEncodedForm(body) {
1204
+ const data = querystring.parse(body.toString());
1205
+ return this.#normalizeFormFields(data);
1206
+ }
1207
+
1208
+ async getAllParams() {
1209
+ if (!this.#allParams) {
1210
+ const body = await this.getBody();
1211
+
1212
+ this.#allParams = {
1213
+ ...this.#queryParams,
1214
+ ...this.#pathParams,
1215
+ ...(typeof body === 'object' ? body : { body }),
1216
+ }
1217
+ }
1218
+
1219
+ return this.#allParams;
1220
+ }
1221
+
1222
+ getCookies() {
1223
+ if (!this.#cookies) {
1224
+ this.#cookies = Object.fromEntries(
1225
+ (this.#headers?.['cookie'] ?? '')
1226
+ .split(/\s*;\s*/gm)
1227
+ .map(s => s.split(/\s*=\s*/gm))
1228
+ .map(x => [decodeURIComponent(x[0] ?? ''), decodeURIComponent(x[1] ?? '')])
1229
+ );
1230
+ }
1231
+
1232
+ return this.#cookies;
1233
+ }
1234
+
1235
+ setCustomData(data) {
1236
+ this.#customData = data;
1237
+ }
1238
+
1239
+ getCustomData() {
1240
+ return this.#customData;
1241
+ }
1242
+
1243
+ toJSON() {
1244
+ return {
1245
+ method: this.#method,
1246
+ headers: this.#headers,
1247
+ path: this.#path,
1248
+ queryParams: this.#queryParams,
1249
+ };
1250
+ }
1251
+
1252
+ isHandledAsWebSocket() {
1253
+ return !!this.#webSocketData;
1254
+ }
1255
+
1256
+ handleAsWebSocket(callback) {
1257
+ if (typeof callback === 'function') {
1258
+ try {
1259
+ this.#initWebSocket();
1260
+ } catch (error) {
1261
+ this.#closeWebSocket(true);
1262
+ throw error;
1263
+ }
1264
+ } else {
1265
+ throw new Error('Callback not provided');
1266
+ }
1267
+
1268
+ const firstTime = this.#webSocketData.listeners.size === 0;
1269
+
1270
+ this.#webSocketData.listeners.add(callback);
1271
+
1272
+ if (firstTime) {
1273
+ this.#handleWebSocketRequests();
1274
+ }
1275
+
1276
+ return {
1277
+ write: this.#webSocketData.write,
1278
+ close: this.#webSocketData.close,
1279
+ };
1280
+ }
1281
+
1282
+ waitForWebSocketToClose() {
1283
+ if (!this.#webSocketData || this.#isWebSocketClosed) {
1284
+ return;
1285
+ }
1286
+
1287
+ return new Promise(resolve => {
1288
+ const socket = this.#request.socket;
1289
+
1290
+ socket.on("close", () => {
1291
+ resolve();
1292
+ });
1293
+
1294
+ socket.on("end", () => {
1295
+ resolve();
1296
+ });
1297
+
1298
+ socket.on("error", (error) => {
1299
+ safePrint(error, true);
1300
+ resolve();
1301
+ });
1302
+
1303
+ socket.on('timeout', (error) => {
1304
+ safePrint(error, true);
1305
+ resolve();
1306
+ });
1307
+ })
1308
+ }
1309
+
1310
+ async #readBytesFromWebSocket(size = 1) {
1311
+ let result = Buffer.alloc(0);
1312
+
1313
+ do {
1314
+ if (this.#isWebSocketClosed) {
1315
+ throw new Error('Socket is closed');
1316
+ }
1317
+
1318
+ this.#webSocketData.nextDataPromiseWithResolvers = null;
1319
+ result = Buffer.concat([result, ...this.#webSocketData.collectedData]);
1320
+ this.#webSocketData.collectedData = [];
1321
+
1322
+ if (result.length === size) {
1323
+ return result;
1324
+ }
1325
+
1326
+ if (result.length > size) {
1327
+ this.#webSocketData.collectedData = [result.subarray(size)];
1328
+ return result.subarray(0, size);
1329
+ }
1330
+
1331
+ this.#webSocketData.nextDataPromiseWithResolvers = Promise.withResolvers();
1332
+ await this.#webSocketData.nextDataPromiseWithResolvers.promise;
1333
+ } while (true);
1334
+ }
1335
+
1336
+ #initWebSocket() {
1337
+ if (this.#isWebSocketClosed) {
1338
+ throw new Error('Socket is already closed');
1339
+ }
1340
+
1341
+ if (this.#webSocketData) {
1342
+ return;
1343
+ }
1344
+
1345
+ const webSocketHeader = this.#headers["sec-websocket-key"];
1346
+
1347
+ if (!webSocketHeader) {
1348
+ throw new JsonResponse({
1349
+ message: 'Header "sec-websocket-key" is missing',
1350
+ }, 400);
1351
+ }
1352
+
1353
+ const socket = this.#request.socket;
1354
+
1355
+ this.#webSocketData = {
1356
+ write: (data) => this.#writeIntoWebSocket(data),
1357
+ close: () => {
1358
+ this.#webSocketData.isManuallyClosed = true;
1359
+ this.#closeWebSocket()
1360
+ },
1361
+ listeners: new Set(),
1362
+ nextDataPromiseWithResolvers: null,
1363
+ isManuallyClosed: false,
1364
+ collectedData: this.#heads?.length ? [this.#heads] : [],
1365
+ };
1366
+
1367
+ const guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1368
+ const sha1 = nodeCrypto.createHash("sha1");
1369
+ sha1.update(webSocketHeader + guid);
1370
+ const validHandShakeKey = sha1.digest("base64");
1371
+
1372
+ const responseHeaders = [
1373
+ "HTTP/1.1 101 Switching Protocols",
1374
+ "Upgrade: websocket",
1375
+ "Connection: Upgrade",
1376
+ `Sec-WebSocket-Accept: ${validHandShakeKey}`,
1377
+ "\r\n",
1378
+ ].join("\r\n");
1379
+
1380
+ socket.write(responseHeaders);
1381
+
1382
+ socket.on("data", (data) => {
1383
+ this.#webSocketData.collectedData.push(data);
1384
+ this.#webSocketData.nextDataPromiseWithResolvers?.resolve();
1385
+ this.#webSocketData.nextDataPromiseWithResolvers = null;
1386
+ });
1387
+
1388
+ socket.on("close", () => {
1389
+ this.#closeWebSocket();
1390
+ });
1391
+
1392
+
1393
+ socket.on("end", () => {
1394
+ this.#closeWebSocket();
1395
+ });
1396
+
1397
+ socket.on("error", (error) => {
1398
+ this.#webSocketData.nextDataPromiseWithResolvers?.reject(error);
1399
+ this.#webSocketData.nextDataPromiseWithResolvers = null;
1400
+ this.#closeWebSocket(true);
1401
+ });
1402
+
1403
+ socket.on('timeout', (error) => {
1404
+ this.#webSocketData.nextDataPromiseWithResolvers?.reject(error);
1405
+ this.#webSocketData.nextDataPromiseWithResolvers = null;
1406
+ this.#closeWebSocket(true);
1407
+ });
1408
+ }
1409
+
1410
+ async #handleWebSocketRequests() {
1411
+ const maxPayloadSize = 16 * 1024 * 1024;
1412
+
1413
+ const sendDataToAllListeners = async (data = null, type = 'start') => {
1414
+ const statuses = await Promise.allSettled(this.#webSocketData.listeners.values().map(listener => listener(data, type)));
1415
+
1416
+ for (const status of statuses) {
1417
+ if (status.status === 'rejected') {
1418
+ safePrint(status.reason);
1419
+ }
1420
+ }
1421
+ };
1422
+
1423
+ try {
1424
+ let collectedPayload = [];
1425
+
1426
+ await new Promise((resolve) => setTimeout(resolve));
1427
+ await sendDataToAllListeners();
1428
+
1429
+ do {
1430
+ if (this.#isWebSocketClosed) {
1431
+ await sendDataToAllListeners(null, 'end');
1432
+ return;
1433
+ }
1434
+
1435
+ const head = await this.#readBytesFromWebSocket(2);
1436
+ const opCode = head[0] & 0b00001111;
1437
+
1438
+ if (opCode === 0b00001000) {
1439
+ await sendDataToAllListeners(null, 'end');
1440
+ this.#closeWebSocket();
1441
+ return;
1442
+ }
1443
+
1444
+ const fin = !!(head[0] & 0b10000000);
1445
+ const hasMask = !!(head[1] & 0b10000000);
1446
+ let payloadLength = head[1] & 0b01111111;
1447
+
1448
+ if (payloadLength === 126) {
1449
+ const payloadBytes = await this.#readBytesFromWebSocket(2);
1450
+ payloadLength = 0;
1451
+
1452
+ for (let i = 0; i < 2; ++i) {
1453
+ payloadLength += payloadBytes[1 - i] << (8 * i);
1454
+ }
1455
+ } else if (payloadLength === 127) {
1456
+ const payloadBytes = await this.#readBytesFromWebSocket(8);
1457
+ payloadLength = 0;
1458
+
1459
+ for (let i = 0; i < 8; ++i) {
1460
+ payloadLength += payloadBytes[7 - i] << (8 * i);
1461
+ }
1462
+ }
1463
+
1464
+ if (payloadLength > maxPayloadSize) {
1465
+ throw new Error(`Payload size (${payloadLength} bytes) exceeds ${maxPayloadSize} bytes`);
1466
+ }
1467
+
1468
+ const mask = hasMask ? (await this.#readBytesFromWebSocket(4)) : Buffer.alloc(4);
1469
+ const payload = await this.#readBytesFromWebSocket(payloadLength);
1470
+
1471
+ for (let i = 0; i < payloadLength; ++i) {
1472
+ payload[i] ^= mask[i & 0b11];
1473
+ }
1474
+
1475
+ collectedPayload.push(payload);
1476
+
1477
+ if (fin) {
1478
+ const finalBuffer = Buffer.concat(collectedPayload);
1479
+ collectedPayload = [];
1480
+
1481
+ switch (opCode) {
1482
+ case 0b00000001:
1483
+ case 0b00000010:
1484
+ await sendDataToAllListeners(opCode === 0b00000001 ? finalBuffer.toString('utf8') : finalBuffer, 'message');
1485
+ break;
1486
+ case 0b00001001:
1487
+ this.#writeIntoWebSocket(finalBuffer, true);
1488
+ break;
1489
+ default:
1490
+ throw new Error(`Invalid op code ${opCode}`);
1491
+ }
1492
+ }
1493
+ } while (true);
1494
+ } catch (error) {
1495
+ safePrint(error, true);
1496
+ this.#closeWebSocket(true);
1497
+
1498
+ if (this.#webSocketData.isManuallyClosed) {
1499
+ await sendDataToAllListeners(null, 'end');
1500
+ } else {
1501
+ await sendDataToAllListeners(error, 'error');
1502
+ }
1503
+ }
1504
+ }
1505
+
1506
+ #writeIntoWebSocket(data, isPong = false) {
1507
+ if (this.#isWebSocketClosed || !this.#webSocketData) {
1508
+ return;
1509
+ }
1510
+
1511
+ if (ArrayBuffer.isView(data) && !(data instanceof Buffer)) {
1512
+ data = data.buffer;
1513
+ }
1514
+
1515
+ if (typeof data !== 'string' && !(data instanceof Array || data instanceof Buffer || data instanceof ArrayBuffer)) {
1516
+ data = `${data}`;
1517
+ }
1518
+
1519
+ const isString = typeof data === 'string';
1520
+ const buffer = data instanceof Buffer ? data : (isString ? Buffer.from(data, 'utf-8') : Buffer.from(data));
1521
+ const length = buffer.length;
1522
+
1523
+ let payloadLength = [length];
1524
+
1525
+ if (length > 65535) {
1526
+ payloadLength[0] = 127;
1527
+
1528
+ for (let i = 7; i >= 0; --i) {
1529
+ payloadLength.push((length >> (i * 8)) & 0b11111111);
1530
+ }
1531
+ } else if (length > 125) {
1532
+ payloadLength[0] = 126;
1533
+
1534
+ for (let i = 1; i >= 0; --i) {
1535
+ payloadLength.push((length >> (i * 8)) & 0b11111111);
1536
+ }
1537
+ }
1538
+
1539
+ const opCodeWithFin = Buffer.from([isPong ? 0b10001010 : (isString ? 0b10000001 : 0b10000010)]);
1540
+ payloadLength = Buffer.from(payloadLength);
1541
+
1542
+ this.#request.socket.write(opCodeWithFin);
1543
+ this.#request.socket.write(payloadLength);
1544
+ this.#request.socket.write(buffer);
1545
+ }
1546
+
1547
+ #closeWebSocket(error = false) {
1548
+ if (this.#isWebSocketClosed || !this.#webSocketData) {
1549
+ return;
1550
+ }
1551
+
1552
+ this.#isWebSocketClosed = true;
1553
+ const socket = this.#request.socket;
1554
+ this.#webSocketData.nextDataPromiseWithResolvers?.reject(new Error('Socket is closed'));
1555
+ this.#webSocketData.nextDataPromiseWithResolvers = null;
1556
+ socket.end(Buffer.from(error ? [] : [0b10001000, 0b00000000]));
1557
+ }
1558
+ }
1559
+
1560
+ export class Response {
1561
+ #cookies;
1562
+ #addedCustomHeaders = {};
1563
+
1564
+ getCode() {
1565
+ return 200;
1566
+ }
1567
+
1568
+ getHeaders() {
1569
+ return this.getMergedWithOtherHeaders({ 'Content-Type': 'text/plain' });
1570
+ }
1571
+
1572
+ getBody() {
1573
+ '';
1574
+ }
1575
+
1576
+ setCookies(cookies) {
1577
+ this.#cookies = { ...cookies };
1578
+ }
1579
+
1580
+ addCookies(cookies) {
1581
+ this.#cookies = this.#cookies ? { ...this.#cookies, ...cookies } : cookies;
1582
+ }
1583
+
1584
+ getCookies() {
1585
+ return this.#cookies ?? {};
1586
+ }
1587
+
1588
+ addCustomHeaders(customHeaders) {
1589
+ this.#addedCustomHeaders = { ...this.#addedCustomHeaders, ...customHeaders };
1590
+ }
1591
+
1592
+ getMergedWithOtherHeaders(headers) {
1593
+ if (this.#cookies) {
1594
+ let cookieStrings = [];
1595
+ const maxAge = 60 * 60 * 24 * 365 * 5;
1596
+
1597
+ for (const k in this.#cookies) {
1598
+ let cookieValue = this.#cookies[k];
1599
+
1600
+ if (cookieValue === null || cookieValue === undefined) {
1601
+ cookieValue = { value: '', maxAge: 0 };
1602
+ } if (!(cookieValue instanceof Object)) {
1603
+ cookieValue = { value: cookieValue };
1604
+ }
1605
+
1606
+ cookieValue = { maxage: maxAge, path: '/', ...cookieValue };
1607
+ let cookieString = `${encodeURIComponent(k)}=${encodeURIComponent(cookieValue.value ?? '')}`;
1608
+
1609
+ for (const prop in cookieValue) {
1610
+ const name = prop.split('-').join('').toLowerCase();
1611
+
1612
+ const attributeName = {
1613
+ 'domain': 'Domain',
1614
+ 'expires': 'Expires',
1615
+ 'httponly': 'HttpOnly',
1616
+ 'maxage': 'Max-Age',
1617
+ 'partitioned': 'Partitioned',
1618
+ 'path': 'Path',
1619
+ 'samesite': 'Samesite',
1620
+ 'secure': 'Secure',
1621
+ }[name];
1622
+
1623
+ let attributeValue = cookieValue[prop];
1624
+
1625
+ if (attributeName) {
1626
+ if (!['httponly', 'partitioned', 'secure'].includes(name)) {
1627
+ if (attributeValue instanceof Date) {
1628
+ attributeValue = attributeValue.toUTCString();
1629
+ } else if (name !== 'path') {
1630
+ attributeValue = encodeURIComponent(`${attributeValue}`);
1631
+ }
1632
+
1633
+ cookieString += `; ${attributeName}=${attributeValue}`
1634
+ } else if (attributeValue) {
1635
+ cookieString += `; ${attributeName}`
1636
+ }
1637
+ }
1638
+ }
1639
+ cookieStrings.push(cookieString);
1640
+ }
1641
+
1642
+ if (cookieStrings.length) {
1643
+ headers = {
1644
+ ...headers,
1645
+ 'Set-Cookie': cookieStrings,
1646
+ }
1647
+ }
1648
+ }
1649
+
1650
+ headers = {
1651
+ ...headers,
1652
+ ...this.#addedCustomHeaders,
1653
+ }
1654
+
1655
+ for (const key of Object.keys(headers)) {
1656
+ if (headers[key] === null || headers[key] === undefined) {
1657
+ delete headers[key];
1658
+ }
1659
+ }
1660
+
1661
+ return headers;
1662
+ }
1663
+ }
1664
+
1665
+ export class CustomResponse extends Response {
1666
+ #code;
1667
+ #data;
1668
+ #headers;
1669
+
1670
+ constructor(data, code = 200, headers = { 'Content-Type': 'application/octet-stream' }, cookies = null) {
1671
+ super();
1672
+ this.#data = data;
1673
+ this.#code = code;
1674
+ this.#headers = headers;
1675
+ this.setCookies(cookies);
1676
+ }
1677
+
1678
+ getCode() {
1679
+ return this.#code;
1680
+ }
1681
+
1682
+ getHeaders() {
1683
+ return this.getMergedWithOtherHeaders(this.#headers);
1684
+ }
1685
+
1686
+ getBody() {
1687
+ return this.#data;
1688
+ }
1689
+
1690
+ getData() {
1691
+ return this.#data;
1692
+ }
1693
+
1694
+ setData(data) {
1695
+ this.#data = data;
1696
+ }
1697
+ }
1698
+
1699
+ export class FileResponse extends Response {
1700
+ #code;
1701
+ #filePath;
1702
+ #contentType;
1703
+
1704
+ #dataPromise = null;
1705
+
1706
+ #maxChunkSize = 4 * 1024 * 1024;
1707
+ #maxFragmentSize = 16 * 1024 * 1024 * 1024;
1708
+ #fragmentRequestMap = new WeakMap();
1709
+ #makeNotFoundResponse = null;
1710
+ #urlPathForDirectory = null;
1711
+
1712
+ #blocked = false;
1713
+ #proxiedResponse = null;
1714
+ #enableFragments = false;
1715
+
1716
+ constructor(filePath, code = 200, contentType = null, cookies = null) {
1717
+ super();
1718
+ this.#filePath = filePath;
1719
+ this.#code = code;
1720
+ this.#contentType = contentType;
1721
+ this.setCookies(cookies);
1722
+ }
1723
+
1724
+ async getCode(headers = null) {
1725
+ if (this.#proxiedResponse) {
1726
+ return await this.#proxiedResponse.getCode(headers);
1727
+ }
1728
+
1729
+ const requestedFragment = await this.#getFragmentRequest(headers);
1730
+ const data = await this.#retreiveData();
1731
+
1732
+ if (requestedFragment) {
1733
+ return 206;
1734
+ }
1735
+
1736
+ return data.code;
1737
+ }
1738
+
1739
+ async getHeaders(headers = null) {
1740
+ if (this.#proxiedResponse) {
1741
+ const resultingHeaders = await this.#proxiedResponse.getHeaders(headers);
1742
+ return this.getMergedWithOtherHeaders(resultingHeaders);
1743
+ }
1744
+
1745
+ const requestedFragment = await this.#getFragmentRequest(headers);
1746
+ const data = await this.#retreiveData();
1747
+
1748
+ if (requestedFragment) {
1749
+ return {
1750
+ ...data.headers,
1751
+ 'Accept-Ranges': 'bytes',
1752
+ 'Content-Range': `bytes ${requestedFragment.offset}-${requestedFragment.offset + requestedFragment.size - 1}/${data.size}`,
1753
+ 'Content-Length': `${requestedFragment.size}`,
1754
+ 'Cache-Control': 'no-store, no-cache, must-revalidate',
1755
+ };
1756
+ }
1757
+
1758
+ if (typeof data.body === 'function') {
1759
+ return {
1760
+ ...data.headers,
1761
+ 'Cache-Control': 'no-store, no-cache, must-revalidate',
1762
+ }
1763
+ }
1764
+
1765
+ return data.headers;
1766
+ }
1767
+
1768
+ async getBody(headers = null) {
1769
+ if (this.#proxiedResponse) {
1770
+ return await this.#proxiedResponse.getBody(headers);
1771
+ }
1772
+
1773
+ const requestedFragment = await this.#getFragmentRequest(headers);
1774
+ const data = await this.#retreiveData();
1775
+
1776
+ if (requestedFragment) {
1777
+ return () => data.body(requestedFragment);
1778
+ }
1779
+
1780
+ return data.body;
1781
+ }
1782
+
1783
+ getProxy() {
1784
+ const proxy = new FileResponse(this.#filePath, this.#code, this.#contentType, this.getCookies());
1785
+ proxy.#proxiedResponse = this;
1786
+ return proxy;
1787
+ }
1788
+
1789
+ getFilePath() {
1790
+ return this.#filePath;
1791
+ }
1792
+
1793
+ setFilePath(filePath) {
1794
+ this.#filePath = filePath;
1795
+ this.#dataPromise = this.#proxiedResponse = null;
1796
+ }
1797
+
1798
+ setNotFoundErrorCustomResponseHandler(handler) {
1799
+ this.#makeNotFoundResponse = handler;
1800
+ this.#dataPromise = this.#proxiedResponse = null;
1801
+ }
1802
+
1803
+ enableFraments() {
1804
+ this.#enableFragments = true;
1805
+ this.#dataPromise = this.#proxiedResponse = null;
1806
+ }
1807
+
1808
+ setUrlPathForDirectory(urlPathForDirectory) {
1809
+ this.#urlPathForDirectory = urlPathForDirectory;
1810
+ this.#dataPromise = this.#proxiedResponse = null;
1811
+ }
1812
+
1813
+ #isStreamableFileFormat(isDirectory = false) {
1814
+ if (isDirectory) {
1815
+ return false;
1816
+ }
1817
+
1818
+ const extension = this.#filePath?.toLowerCase().split('.').at(-1) ?? '';
1819
+ return !!mimeTypes[extension]?.match(/^(audio|video)\//);
1820
+ }
1821
+
1822
+ #getFragmentRequest(headers) {
1823
+ let promise = this.#fragmentRequestMap.get(headers);
1824
+
1825
+ if (promise === undefined && headers?.['range'] && this.#enableFragments) {
1826
+ const getFragmentRequest = async () => {
1827
+ if (headers['range'].includes(',')) {
1828
+ return null;
1829
+ }
1830
+
1831
+ const numbers = headers['range']
1832
+ .trim()
1833
+ .replace('bytes=', '')
1834
+ .split(/\b\s*-\s*/gm)
1835
+ .map(x => {
1836
+ if (!x) {
1837
+ return null;
1838
+ }
1839
+
1840
+ x = +x;
1841
+
1842
+ if (!Number.isSafeInteger(x)) {
1843
+ return null;
1844
+ }
1845
+
1846
+ return x;
1847
+ });
1848
+
1849
+ const data = await this.#retreiveData();
1850
+
1851
+ if (data.code < 200 || data.code > 299 || typeof data.body !== 'function') {
1852
+ return null;
1853
+ }
1854
+
1855
+ let offset = numbers[0] ?? 0;
1856
+
1857
+ if (offset < 0) {
1858
+ offset = data.size + offset;
1859
+ }
1860
+
1861
+ if (offset >= data.size) {
1862
+ offset = data.size - 1;
1863
+ }
1864
+
1865
+ let size = (numbers[1] ?? (this.#maxFragmentSize - 1 + offset)) - offset + 1;
1866
+
1867
+ if (size < 0) {
1868
+ size = 0;
1869
+ }
1870
+
1871
+ size = Math.min(size, data.size - offset);
1872
+
1873
+ return {
1874
+ offset,
1875
+ size,
1876
+ };
1877
+ }
1878
+
1879
+ promise = getFragmentRequest();
1880
+ this.#fragmentRequestMap.set(headers, promise);
1881
+ }
1882
+
1883
+ return promise ?? null;
1884
+ }
1885
+
1886
+ async * #getDirectoryStream() {
1887
+ const data = await this.#retreiveData();
1888
+
1889
+ if (typeof data.body !== 'function') {
1890
+ yield data.body;
1891
+ return;
1892
+ }
1893
+
1894
+ const urlPath = this.#urlPathForDirectory;
1895
+ const parentUrlPath = urlPath.split('/').filter((x, i, arr) => x && i !== arr.length - 1).join('/');
1896
+ const filePath = this.#filePath;
1897
+
1898
+ yield `<!DOCTYPE html>
1899
+ <html lang="en">
1900
+ <head>
1901
+ <title>${escapeHtml(urlPath)}</title>
1902
+ </head>
1903
+ <body>
1904
+ ${urlPath ? `<a href="/${parentUrlPath}">Up</a><hr>` : ''}
1905
+ `;
1906
+ const entries = await currentFsPromiseModule.readdir(filePath, { withFileTypes: true });
1907
+ let counter = 0;
1908
+
1909
+ for (const entry of entries) {
1910
+ if (this.#blocked) {
1911
+ break;
1912
+ }
1913
+
1914
+ if (counter === 100) {
1915
+ await new Promise(resolve => setTimeout(resolve, 50));
1916
+ counter = 0;
1917
+ }
1918
+
1919
+ ++counter;
1920
+
1921
+ yield `<a${entry.isFile() ? ' target="_blank"' : ''} href="/${urlPath}/${encodeURIComponent(entry.name)}">${escapeHtml(entry.name)}</a><br>`;
1922
+ }
1923
+
1924
+
1925
+ yield `</body>`
1926
+
1927
+ }
1928
+
1929
+ async * #getBodyStream(fragmentRequest) {
1930
+ const data = await this.#retreiveData();
1931
+
1932
+ if (typeof data.body !== 'function') {
1933
+ yield data.body;
1934
+ return;
1935
+ }
1936
+
1937
+ let filehandle;
1938
+
1939
+ try {
1940
+ const requestedPosition = fragmentRequest?.offset ?? 0;
1941
+ const requestedSize = fragmentRequest?.size ?? data.size;
1942
+ const maxChunkSize = fragmentRequest ? 256 * 1024 : this.#maxChunkSize;
1943
+
1944
+ filehandle = await currentFsPromiseModule.open(this.#filePath, 'r');
1945
+
1946
+ for (let offset = 0; offset < requestedSize && !this.#blocked; offset += maxChunkSize) {
1947
+ const size = Math.min(maxChunkSize, requestedSize - offset);
1948
+ const chunk = await filehandle.read(Buffer.alloc(size), 0, size, offset + requestedPosition);
1949
+ yield chunk.buffer;
1950
+ }
1951
+ } finally {
1952
+ filehandle?.close();
1953
+ }
1954
+ }
1955
+
1956
+ #retreiveData() {
1957
+ if (!this.#dataPromise) {
1958
+ this.#dataPromise = new Promise(async resolve => {
1959
+ let filehandle;
1960
+
1961
+ try {
1962
+ if (this.#blocked) {
1963
+ throw new Error(`${this.#filePath} is blocked`);
1964
+ }
1965
+
1966
+ filehandle = await currentFsPromiseModule.open(this.#filePath, 'r');
1967
+ const stat = await filehandle.stat();
1968
+ const size = stat.size;
1969
+ const readAsDirectory = stat.isDirectory() && this.#urlPathForDirectory !== null;
1970
+ const body = size > this.#maxChunkSize || this.#isStreamableFileFormat(readAsDirectory) ? (fragmentRequest => this.#getBodyStream(fragmentRequest)) : (readAsDirectory ? (() => this.#getDirectoryStream()) : await filehandle.readFile());
1971
+
1972
+ resolve({
1973
+ code: this.#code,
1974
+ headers: this.getMergedWithOtherHeaders({
1975
+ 'Content-Type': this.#contentType ??
1976
+ (readAsDirectory ? 'text/html; charset=utf-8' : null) ??
1977
+ mimeTypes[this.#filePath.split('.').at(-1).toLowerCase()] ??
1978
+ 'application/octet-stream',
1979
+ 'Content-Length': readAsDirectory ? null : size,
1980
+ }
1981
+ ),
1982
+ body,
1983
+ size,
1984
+ });
1985
+ } catch (e) {
1986
+ safePrint(e, true);
1987
+
1988
+ const notFountData = {
1989
+ code: 404,
1990
+ headers: this.getMergedWithOtherHeaders({ 'Content-Type': 'application/json' }),
1991
+ body: JSON.stringify({
1992
+ message: `File not found`
1993
+ })
1994
+ };
1995
+
1996
+ try {
1997
+ const response = await this.#makeNotFoundResponse?.(this.#filePath);
1998
+
1999
+ if (response instanceof Response) {
2000
+ const code = await response.getCode();
2001
+ const headers = await response.getHeaders();
2002
+ const body = await response.getBody();
2003
+
2004
+ notFountData.code = code;
2005
+ notFountData.headers = headers;
2006
+ notFountData.body = body;
2007
+ }
2008
+ } catch (error) {
2009
+ safePrint(error, true);
2010
+ }
2011
+
2012
+ resolve(notFountData);
2013
+ } finally {
2014
+ await filehandle?.close();
2015
+ }
2016
+ });
2017
+ }
2018
+
2019
+ return this.#dataPromise;
2020
+ }
2021
+
2022
+ block() {
2023
+ this.#blocked = true;
2024
+ this.#dataPromise = this.#proxiedResponse = null;
2025
+ }
2026
+
2027
+ isBlocked() {
2028
+ return this.#blocked;
2029
+ }
2030
+ }
2031
+
2032
+ export class JsonResponse extends Response {
2033
+ #object;
2034
+ #code;
2035
+ #cachedBuffer = null;
2036
+
2037
+ constructor(object = {}, code = 200, cookies = null) {
2038
+ super();
2039
+ this.setCookies(cookies);
2040
+ this.#object = object;
2041
+ this.#code = code;
2042
+ }
2043
+
2044
+ getCode() {
2045
+ return this.#code;
2046
+ }
2047
+
2048
+ getHeaders() {
2049
+ return this.getMergedWithOtherHeaders({
2050
+ 'Content-Type': 'application/json',
2051
+ 'Content-Length': `${this.getBody().length}`,
2052
+ });
2053
+ }
2054
+
2055
+ getBody() {
2056
+ if (!this.#cachedBuffer) {
2057
+ this.#cachedBuffer = Buffer.from(JSON.stringify(this.#object), 'utf8');
2058
+ }
2059
+
2060
+ return this.#cachedBuffer;
2061
+ }
2062
+
2063
+ getObject() {
2064
+ return this.#object;
2065
+ }
2066
+
2067
+ setObject(object) {
2068
+ this.#cachedBuffer = null;
2069
+ this.#object = object;
2070
+ }
2071
+ }
2072
+
2073
+ export class HTMLResponse extends Response {
2074
+ #string;
2075
+ #code;
2076
+ #cachedBuffer = null;
2077
+
2078
+ constructor(string = '', code = 200, cookies = null) {
2079
+ super();
2080
+ this.setCookies(cookies);
2081
+ this.#string = string;
2082
+ this.#code = code;
2083
+ }
2084
+
2085
+ getCode() {
2086
+ return this.#code;
2087
+ }
2088
+
2089
+ getHeaders() {
2090
+ return this.getMergedWithOtherHeaders({
2091
+ 'Content-Type': 'text/html; charset=utf-8',
2092
+ 'Content-Length': `${this.getBody().length}`,
2093
+ });
2094
+ }
2095
+
2096
+ getBody() {
2097
+ if (!this.#cachedBuffer) {
2098
+ this.#cachedBuffer = Buffer.from(this.#string, 'utf8');
2099
+ }
2100
+
2101
+ return this.#cachedBuffer;
2102
+ }
2103
+
2104
+ getString() {
2105
+ return this.#string;
2106
+ }
2107
+
2108
+ setString(string) {
2109
+ this.#cachedBuffer = null;
2110
+ this.#string = string;
2111
+ }
2112
+ }
2113
+
2114
+ export class RedirectResponse extends Response {
2115
+ #url;
2116
+ #code;
2117
+
2118
+ constructor(url, code = 301, cookies = null) {
2119
+ super();
2120
+ this.setCookies(cookies);
2121
+ this.#url = url;
2122
+ this.#code = code;
2123
+ }
2124
+
2125
+ getCode() {
2126
+ return this.#code;
2127
+ }
2128
+
2129
+ getHeaders() {
2130
+ return this.getMergedWithOtherHeaders({ 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store, no-cache, must-revalidate', 'Location': this.#url, });
2131
+ }
2132
+
2133
+ getBody() {
2134
+ return '';
2135
+ }
2136
+
2137
+ getUrl() {
2138
+ return this.#url;
2139
+ }
2140
+
2141
+ setUrl(url) {
2142
+ this.#url = url;
2143
+ }
2144
+ }
2145
+
2146
+ export function serve(routes, port = 80, staticFileDirectoryOrDirectories = null, handleNotFoundError = null, handleServerError = null) {
2147
+ port = +port;
2148
+ routes = normalizeRoutes(routes, handleServerError);
2149
+ staticFileDirectoryOrDirectories = normalizeStaticFileDirectories(staticFileDirectoryOrDirectories);
2150
+ safePrint(routes);
2151
+ safePrint(staticFileDirectoryOrDirectories);
2152
+
2153
+ try {
2154
+ unserve(port);
2155
+
2156
+
2157
+ const callback = async (req, res, heads = null) => {
2158
+ try {
2159
+ const [code, headers, body, isHandledAsWebSocket] = await handleRequest(req, routes, staticFileDirectoryOrDirectories, handleNotFoundError, heads);
2160
+
2161
+ if (isHandledAsWebSocket || !(res instanceof http.ServerResponse)) {
2162
+ return;
2163
+ }
2164
+
2165
+ res.writeHead(code, headers);
2166
+
2167
+ if (typeof body === 'function') {
2168
+ for await (const chunk of body()) {
2169
+ if (res.writableEnded || res.destroyed) {
2170
+ break;
2171
+ }
2172
+
2173
+ res.write(chunk);
2174
+ let waitTime = 0;
2175
+
2176
+ while (res.writableNeedDrain && !res.writableEnded && !res.destroyed && waitTime < keepAliveTimeout) {
2177
+ await new Promise(resolve => setTimeout(resolve, 50));
2178
+ waitTime += 50;
2179
+ }
2180
+ }
2181
+ } else {
2182
+ res.end(body);
2183
+ }
2184
+ } catch (error) {
2185
+ safePrint(error, true);
2186
+ } finally {
2187
+ if (res instanceof net.Socket) {
2188
+ if (!res.writableEnded) {
2189
+ res.end();
2190
+ }
2191
+
2192
+ await new Promise(resolve => setTimeout(resolve, 2000));
2193
+
2194
+ if (!res.destroyed) {
2195
+ res.destroy();
2196
+ }
2197
+ } else if (res instanceof http.ServerResponse && !res.writableEnded && !res.destroyed) {
2198
+ res.end();
2199
+ }
2200
+ }
2201
+ };
2202
+
2203
+ const server = http.createServer(callback);
2204
+ server.on('upgrade', callback);
2205
+
2206
+ servers.set(port, server);
2207
+
2208
+ server.keepAliveTimeout = keepAliveTimeout;
2209
+ server.headersTimeout = keepAliveTimeout;
2210
+
2211
+ server.on('error', (error) => {
2212
+ unserve(port);
2213
+ safePrint(error, true);
2214
+ });
2215
+
2216
+ server.listen(port, () => {
2217
+ safePrint(`Server started on port ${port}`);
2218
+ });
2219
+ } catch (e) {
2220
+ safePrint(e, true);
2221
+ }
2222
+
2223
+
2224
+ }
2225
+
2226
+ export function escapeHtml(str) {
2227
+ return str?.toString().replace(/[&<>"']/gm, (c) => escapeHtmlMap[c]) ?? '';
2228
+ }
2229
+
2230
+ export function unescapeHtml(htmlStr) {
2231
+ return htmlStr?.toString()
2232
+ .replace(/&(?:#(\d+|x[\dA-Fa-f]+)|([^;]+));/gm, (entity, digits, entityName) => unescapeHtmlMap[entity] ?? entityName ?? String.fromCharCode("0" + digits));
2233
+ }
2234
+
2235
+ export function unserve(port = 80) {
2236
+ port = +port;
2237
+
2238
+ servers.get(port)?.close(() => {
2239
+ safePrint(`Server closed on port ${port}`);
2240
+ });
2241
+
2242
+ servers.delete(port);
2243
+ }
2244
+
2245
+ function wrapInMiddlewares(callback, preMiddlewares = [], postMiddlewares = [], handleServerError = null) {
2246
+ let resultCallback = async (request, handleOptions = false) => {
2247
+ let response;
2248
+
2249
+ try {
2250
+ for (const pre of preMiddlewares) {
2251
+ const req = await pre(request);
2252
+
2253
+ if (req instanceof Request) {
2254
+ request = req;
2255
+ }
2256
+ }
2257
+
2258
+ response = wrapInResponseClass(handleOptions ? new CustomResponse(Buffer.from(''), 200, { 'Content-Type': null }) : await callback(request));
2259
+ } catch (error) {
2260
+ if (error instanceof Response) {
2261
+ response = error;
2262
+ } else {
2263
+ throw error;
2264
+ }
2265
+ }
2266
+
2267
+ for (const post of postMiddlewares) {
2268
+ const res = await post(request, response);
2269
+
2270
+ if (res !== undefined) {
2271
+ response = wrapInResponseClass(res);
2272
+ }
2273
+ }
2274
+
2275
+ return response;
2276
+ };
2277
+
2278
+ if (handleServerError) {
2279
+ const tempCallback = resultCallback;
2280
+
2281
+ resultCallback = async (request, handleOptions = false) => {
2282
+ try {
2283
+ return await tempCallback(request, handleOptions);
2284
+ } catch (error) {
2285
+ if (error instanceof Response) {
2286
+ throw error;
2287
+ }
2288
+
2289
+ safePrint(error);
2290
+ return wrapInResponseClass(await handleServerError(request, error));
2291
+ }
2292
+ };
2293
+ }
2294
+
2295
+ return resultCallback;
2296
+ }
2297
+
2298
+ function normalizeStaticFileDirectories(staticFileDirectoryOrDirectories) {
2299
+ if (staticFileDirectoryOrDirectories !== null && staticFileDirectoryOrDirectories !== undefined) {
2300
+ if (!Array.isArray(staticFileDirectoryOrDirectories)) {
2301
+ staticFileDirectoryOrDirectories = [staticFileDirectoryOrDirectories];
2302
+ }
2303
+
2304
+ staticFileDirectoryOrDirectories = staticFileDirectoryOrDirectories.filter(x => x !== null && typeof x === 'object' || typeof x === 'string').map(x => {
2305
+ let serverFilePath = '', urlPath = '', showWholeDirectory = false, maxAgeInSeconds = -1, preMiddlewares = [], postMiddlewares = [];
2306
+ const defaultMaxAge = 5 * 24 * 60 * 60;
2307
+
2308
+ if (typeof x === 'string') {
2309
+ serverFilePath = urlPath = x;
2310
+ showWholeDirectory = false;
2311
+ maxAgeInSeconds = defaultMaxAge;
2312
+ preMiddlewares = [];
2313
+ postMiddlewares = [];
2314
+ } else {
2315
+ ({ serverFilePath, urlPath, showWholeDirectory, maxAgeInSeconds, preMiddlewares, postMiddlewares } = x);
2316
+ }
2317
+
2318
+ urlPath = `${urlPath}`.split('/').filter(x => x).join('/');
2319
+ serverFilePath = `${serverFilePath}`.split('/').filter(x => x).join('/');
2320
+ showWholeDirectory = !!showWholeDirectory;
2321
+ maxAgeInSeconds = Math.floor(+(maxAgeInSeconds ?? defaultMaxAge));
2322
+ preMiddlewares = Array.isArray(preMiddlewares) ? preMiddlewares.filter(x => typeof x === 'function') : [];
2323
+ postMiddlewares = Array.isArray(postMiddlewares) ? postMiddlewares.filter(x => typeof x === 'function') : [];
2324
+
2325
+ return {
2326
+ urlPath,
2327
+ serverFilePath,
2328
+ showWholeDirectory,
2329
+ maxAgeInSeconds,
2330
+ preMiddlewares,
2331
+ postMiddlewares,
2332
+ };
2333
+ });
2334
+ } else {
2335
+ staticFileDirectoryOrDirectories = [];
2336
+ }
2337
+
2338
+ return staticFileDirectoryOrDirectories;
2339
+ }
2340
+
2341
+ function normalizeRoutes(routes, handleServerError) {
2342
+ const flatten = {};
2343
+
2344
+ function flattenRecursively(root, path = '', preMiddlewares = [], postMiddlewares = []) {
2345
+ if (!root) {
2346
+ return;
2347
+ }
2348
+
2349
+ if (root.preMiddlewares instanceof Array) {
2350
+ preMiddlewares = [...preMiddlewares, ...root.preMiddlewares];
2351
+ }
2352
+
2353
+ if (root.postMiddlewares instanceof Array) {
2354
+ postMiddlewares = [...root.postMiddlewares, ...postMiddlewares];
2355
+ }
2356
+
2357
+ if (root && typeof root === 'object' && !Array.isArray(root)) {
2358
+ for (const prop in root) {
2359
+ const newPath = (path + '/' + prop).split('/').filter(x => x).join('/');
2360
+ flattenRecursively(root[prop], newPath, preMiddlewares, postMiddlewares);
2361
+ }
2362
+ } else if (typeof root === 'function') {
2363
+ setObjectProperty(flatten, path, wrapInMiddlewares(root, preMiddlewares, postMiddlewares, handleServerError));
2364
+ }
2365
+ }
2366
+
2367
+ flattenRecursively(routes);
2368
+
2369
+ const result = Object.create(null);
2370
+
2371
+ for (const route in flatten) {
2372
+ const split = route.split('/').filter(x => x);
2373
+ let method = split.at(-1)?.toUpperCase();
2374
+
2375
+ if (['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'].includes(method)) {
2376
+ split.pop();
2377
+ } else {
2378
+ method = 'GET';
2379
+ }
2380
+
2381
+ let parent = result;
2382
+
2383
+ for (const fragment of split) {
2384
+ if (!parent[fragment]) {
2385
+ setObjectProperty(parent, fragment, Object.create(null));
2386
+ }
2387
+
2388
+ parent = parent[fragment];
2389
+ }
2390
+
2391
+ setObjectProperty(parent, `/${method}/`, flatten[route]);
2392
+ }
2393
+
2394
+ return result;
2395
+ }
2396
+
2397
+ function wrapInResponseClass(response) {
2398
+ if (!(response instanceof Response)) {
2399
+ if (typeof response === 'object') {
2400
+ response = new JsonResponse(response, 200);
2401
+ } else {
2402
+ response = new HTMLResponse(`${response ?? ''}`, 200);
2403
+ }
2404
+ }
2405
+
2406
+ return response;
2407
+ }
2408
+
2409
+ const staticCache = new Map();
2410
+
2411
+ export function clearStaticCache(path = null) {
2412
+ invalidateStaticCache(path);
2413
+ }
2414
+
2415
+ export function blockStaticCache(path = null) {
2416
+ invalidateStaticCache(path, false);
2417
+ }
2418
+
2419
+ function invalidateStaticCache(path = null, clear = true) {
2420
+ if (path === null) {
2421
+ for (const v of staticCache.values()) {
2422
+ v.block();
2423
+ }
2424
+
2425
+ if (clear) {
2426
+ staticCache.clear();
2427
+ }
2428
+ } else {
2429
+ path = `${path}`.split('/').filter(x => x).join('/');
2430
+
2431
+ if (!clear && !staticCache.has(path)) {
2432
+ staticCache.set(path, new FileResponse(path));
2433
+ }
2434
+
2435
+ staticCache.get(path)?.block();
2436
+
2437
+ if (clear) {
2438
+ staticCache.delete(path);
2439
+ }
2440
+ }
2441
+ }
2442
+
2443
+ async function handleRequest(req, routes, staticFileDirectories, handleNotFoundError, heads) {
2444
+ let response = new JsonResponse({
2445
+ message: 'Invalid data'
2446
+ }, 400);
2447
+
2448
+ let requestHeaders;
2449
+ let responseBodyIsIncluded = true;
2450
+
2451
+ try {
2452
+ const request = new Request(req, heads);
2453
+
2454
+ const method = request.getMethod();
2455
+ const path = request.getPath();
2456
+ const pathParams = {};
2457
+ requestHeaders = request.getHeaders();
2458
+ responseBodyIsIncluded = method !== 'HEAD';
2459
+
2460
+ let routeHandler = routes;
2461
+ let handleOptions = false;
2462
+
2463
+ const staticFileOrDirectory = method === 'GET' || !responseBodyIsIncluded ? staticFileDirectories.find(x => x.urlPath === path || path.startsWith(x.urlPath + '/')) : null;
2464
+
2465
+ if (staticFileOrDirectory) {
2466
+ routeHandler = () => {
2467
+ const filePath = decodeURI(path)
2468
+ .replace(staticFileOrDirectory.urlPath, staticFileOrDirectory.serverFilePath)
2469
+ .replaceAll('\\', '/')
2470
+ .split('/')
2471
+ .filter(x => !x.match(/^\s*\.{0,2}\s*$/gs))
2472
+ .join('/');
2473
+
2474
+ let resp = staticCache.get(filePath);
2475
+
2476
+ if (!resp) {
2477
+ resp = new FileResponse(filePath);
2478
+ resp.enableFraments();
2479
+
2480
+ if (staticFileOrDirectory.maxAgeInSeconds > 0) {
2481
+ resp.addCustomHeaders({
2482
+ 'Cache-Control': `public, max-age=${staticFileOrDirectory.maxAgeInSeconds}`,
2483
+ });
2484
+ }
2485
+
2486
+ if (handleNotFoundError) {
2487
+ resp.setNotFoundErrorCustomResponseHandler(() => handleNotFoundError(request));
2488
+ }
2489
+
2490
+ if (staticFileOrDirectory.showWholeDirectory) {
2491
+ resp.setUrlPathForDirectory(path);
2492
+ }
2493
+
2494
+ staticCache.set(filePath, resp);
2495
+
2496
+ if (staticCache.size > 500) {
2497
+ for (const [k, v] of staticCache.entries()) {
2498
+ if (!v.isBlocked()) {
2499
+ staticCache.delete(k);
2500
+ break;
2501
+ }
2502
+ }
2503
+ }
2504
+ }
2505
+
2506
+ return resp.getProxy();
2507
+ };
2508
+
2509
+ routeHandler = wrapInMiddlewares(routeHandler, staticFileOrDirectory.preMiddlewares, staticFileOrDirectory.postMiddlewares);
2510
+ } else {
2511
+ function getRouteHandler(fragments, root, methodPath, accumulatedPathParams) {
2512
+ if (!fragments.at(0) || !root) {
2513
+ return typeof root?.[methodPath] === 'function' ? root[methodPath] : null;
2514
+ }
2515
+
2516
+ const fragment = fragments.shift();
2517
+ let newRoot = root[fragment];
2518
+ let result = null;
2519
+
2520
+ if (newRoot) {
2521
+ result = getRouteHandler(fragments, newRoot, methodPath, accumulatedPathParams);
2522
+ }
2523
+
2524
+ if (!result) {
2525
+ for (let k in root) {
2526
+ if (k.match(/^{\w+}$/gm)) {
2527
+ newRoot = root[k];
2528
+ result = getRouteHandler(fragments, newRoot, methodPath, accumulatedPathParams);
2529
+
2530
+ if (result) {
2531
+ setObjectProperty(accumulatedPathParams, k.replace(/[{}]/gm, ''), decodeURIComponent(fragment));
2532
+ break;
2533
+ }
2534
+ }
2535
+ }
2536
+ }
2537
+
2538
+ fragments.unshift(fragment);
2539
+ return result;
2540
+ }
2541
+
2542
+ const fragments = path.split('/');
2543
+ let accumulatedPathParams = {};
2544
+ routeHandler = getRouteHandler(fragments, routes, `/${method}/`, accumulatedPathParams);
2545
+
2546
+ if (typeof routeHandler !== 'function') {
2547
+ if (method === 'OPTIONS') {
2548
+ handleOptions = true;
2549
+ accumulatedPathParams = {};
2550
+ routeHandler = getRouteHandler(
2551
+ fragments,
2552
+ routes,
2553
+ `/${requestHeaders?.['access-control-request-method']}/`.toUpperCase(),
2554
+ accumulatedPathParams,
2555
+ );
2556
+ } else if (!responseBodyIsIncluded) {
2557
+ accumulatedPathParams = {};
2558
+ routeHandler = getRouteHandler(
2559
+ fragments,
2560
+ routes,
2561
+ `/GET/`,
2562
+ accumulatedPathParams,
2563
+ );
2564
+ }
2565
+ }
2566
+
2567
+ Object.assign(pathParams, accumulatedPathParams);
2568
+ }
2569
+
2570
+ if (typeof routeHandler === 'function') {
2571
+ request.setPathParams(pathParams);
2572
+ response = await routeHandler(request, handleOptions);
2573
+
2574
+ if (request.isHandledAsWebSocket()) {
2575
+ await request.waitForWebSocketToClose();
2576
+ return [null, null, null, true];
2577
+ }
2578
+ } else {
2579
+ response = await handleNotFoundError?.(request);
2580
+
2581
+ if (!(response instanceof Response)) {
2582
+ response = new JsonResponse({
2583
+ message: `Route ${method} "${path}" not found`
2584
+ }, 404);
2585
+ }
2586
+ }
2587
+ } catch (error) {
2588
+ if (error instanceof Response) {
2589
+ response = error;
2590
+ } else {
2591
+ safePrint(error, true);
2592
+
2593
+ response = new JsonResponse({
2594
+ message: 'Something went wrong'
2595
+ }, 500);
2596
+ }
2597
+ }
2598
+
2599
+ try {
2600
+ return await Promise.all([response.getCode(requestHeaders), response.getHeaders(requestHeaders), responseBodyIsIncluded ? response.getBody(requestHeaders) : '', false]);
2601
+ } catch (error) {
2602
+ safePrint(error, true);
2603
+
2604
+ response = new JsonResponse({
2605
+ message: 'Something went wrong'
2606
+ }, 500);
2607
+
2608
+ return [response.getCode(), response.getHeaders(), responseBodyIsIncluded ? response.getBody() : '', false];
2609
+ }
2577
2610
  }