fulmine.js 5.19.5 → 5.19.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/middlewares.js +5 -2
- package/src/response.js +25 -16
- package/src/utils.js +20 -5
package/package.json
CHANGED
package/src/middlewares.js
CHANGED
|
@@ -1082,8 +1082,11 @@ function createBodyParser(defaultType, beforeReturn, checkOptions, charsetPolicy
|
|
|
1082
1082
|
return beforeReturn(req, res, next, options, empty, encoding);
|
|
1083
1083
|
}
|
|
1084
1084
|
|
|
1085
|
-
// skip reading too large body; NaN compares false, so no declared length passes
|
|
1086
|
-
|
|
1085
|
+
// skip reading too large body; NaN compares false, so no declared length passes.
|
|
1086
|
+
// Not while inflating: content-length counts the compressed bytes and the limit is
|
|
1087
|
+
// about the ones that come out, which body-parser says by leaving the length unset.
|
|
1088
|
+
// The limit is still enforced per chunk as they inflate, see keepChunk
|
|
1089
|
+
if (!inflate && lengthNumber > limit) {
|
|
1087
1090
|
return next(
|
|
1088
1091
|
bodyError("request entity too large", 413, "entity.too.large", {
|
|
1089
1092
|
expected: lengthNumber,
|
package/src/response.js
CHANGED
|
@@ -76,6 +76,12 @@ function invalidChunkError(chunk) {
|
|
|
76
76
|
`The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received ${received}`
|
|
77
77
|
);
|
|
78
78
|
err.code = "ERR_INVALID_ARG_TYPE";
|
|
79
|
+
// node stamps the code into the stack's first line and then puts the name back, so err.name
|
|
80
|
+
// reads TypeError while the error page, which prints the stack, shows the bracketed form
|
|
81
|
+
err.name = "TypeError [ERR_INVALID_ARG_TYPE]";
|
|
82
|
+
void err.stack;
|
|
83
|
+
// through the index signature: name is not optional on Error, so a plain delete is a type error
|
|
84
|
+
delete (/** @type {Record<string, unknown>} */ (/** @type {unknown} */ (err)).name);
|
|
79
85
|
return err;
|
|
80
86
|
}
|
|
81
87
|
const { sign } = require("cookie-signature");
|
|
@@ -842,6 +848,25 @@ module.exports = class Response extends LazyWritable {
|
|
|
842
848
|
* @returns {this}
|
|
843
849
|
*/
|
|
844
850
|
send(body) {
|
|
851
|
+
// undefined means nothing was passed, and Express treats that differently from a value
|
|
852
|
+
// that happens to be empty: no content-type and no ETag for send(), both for send(null)
|
|
853
|
+
// and send(""). It writes no header for it either, so it does not refuse a head that has
|
|
854
|
+
// gone out: after a res.write(), express answers this and we hung up on it.
|
|
855
|
+
if (body === undefined) {
|
|
856
|
+
if (!this.headersSent) {
|
|
857
|
+
// freshness and the bodiless statuses, as every express send goes through: a 204
|
|
858
|
+
// answered with json(undefined) loses the type json had set
|
|
859
|
+
if (this.req.fresh) {
|
|
860
|
+
this.status(304);
|
|
861
|
+
}
|
|
862
|
+
if (this.statusCode === 204 || this.statusCode === 304) {
|
|
863
|
+
delete this.headers["content-type"];
|
|
864
|
+
delete this.headers["content-length"];
|
|
865
|
+
delete this.headers["transfer-encoding"];
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return this.end("");
|
|
869
|
+
}
|
|
845
870
|
if (this.headersSent) {
|
|
846
871
|
// what express's send meets first once the head is out is setHeader's refusal
|
|
847
872
|
throw headersSentError("set");
|
|
@@ -854,22 +879,6 @@ module.exports = class Response extends LazyWritable {
|
|
|
854
879
|
body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
|
|
855
880
|
}
|
|
856
881
|
const isBuffer = Buffer.isBuffer(body);
|
|
857
|
-
// undefined means nothing was passed, and Express treats that differently from a value
|
|
858
|
-
// that happens to be empty: no content-type and no ETag for send(), both for send(null)
|
|
859
|
-
// and send("").
|
|
860
|
-
if (body === undefined) {
|
|
861
|
-
// still through freshness and the bodiless statuses, as every express send is: a 204
|
|
862
|
-
// answered with json(undefined) loses the type json had set
|
|
863
|
-
if (this.req.fresh) {
|
|
864
|
-
this.status(304);
|
|
865
|
-
}
|
|
866
|
-
if (this.statusCode === 204 || this.statusCode === 304) {
|
|
867
|
-
delete this.headers["content-type"];
|
|
868
|
-
delete this.headers["content-length"];
|
|
869
|
-
delete this.headers["transfer-encoding"];
|
|
870
|
-
}
|
|
871
|
-
return this.end("");
|
|
872
|
-
}
|
|
873
882
|
// null is an object as far as Express's switch is concerned, so it becomes the empty
|
|
874
883
|
// string without ever reaching the branch that gives a string its content-type. It still
|
|
875
884
|
// earns an ETag. send("") takes the string branch and does get one.
|
package/src/utils.js
CHANGED
|
@@ -416,6 +416,10 @@ function patternToRegex(pattern, isPrefix = false, caseSensitive = true, strict
|
|
|
416
416
|
}
|
|
417
417
|
groupContent += pattern[i++];
|
|
418
418
|
}
|
|
419
|
+
// a group nobody closed, which express refuses rather than reading to the end
|
|
420
|
+
if (braceDepth > 0) {
|
|
421
|
+
throw new Error(`Unexpected end at index ${len}, expected }: ${pattern}`);
|
|
422
|
+
}
|
|
419
423
|
i++;
|
|
420
424
|
|
|
421
425
|
// When a :parameter precedes this group, that parameter is the one that gives ground
|
|
@@ -512,12 +516,12 @@ function patternToRegex(pattern, isPrefix = false, caseSensitive = true, strict
|
|
|
512
516
|
|
|
513
517
|
// these have no meaning in a path, and the route is refused rather than matching them
|
|
514
518
|
// literally: a path that quietly stops matching is worse than one that fails at startup.
|
|
515
|
-
// Escape them to use them as literals.
|
|
516
|
-
if ("?+()[]!".includes(ch)) {
|
|
519
|
+
// Escape them to use them as literals. A } here closes a group nobody opened.
|
|
520
|
+
if ("?+()[]!}".includes(ch)) {
|
|
517
521
|
throw new Error(`Unexpected ${ch} at index ${i}: ${pattern}`);
|
|
518
522
|
}
|
|
519
523
|
|
|
520
|
-
if (".^$|
|
|
524
|
+
if (".^$|".includes(ch)) {
|
|
521
525
|
regexPattern += "\\" + ch;
|
|
522
526
|
} else {
|
|
523
527
|
regexPattern += ch;
|
|
@@ -556,9 +560,20 @@ function escapePathLiteral(literal) {
|
|
|
556
560
|
return literal.replace(/[:*{}?+()[\]!.^$|\\]/g, "\\$&");
|
|
557
561
|
}
|
|
558
562
|
|
|
563
|
+
// Everything path-to-regexp gives a meaning to: a parameter, a wildcard, a group, a character it
|
|
564
|
+
// reserves, and the backslash that makes one of them literal. A dot or a dash is not in here, so
|
|
565
|
+
// the ordinary path stays a string compare.
|
|
566
|
+
const PATH_SYNTAX = /[:*{}?+()[\]!\\]/;
|
|
567
|
+
|
|
559
568
|
/**
|
|
560
569
|
* Whether a path has anything in it that a string comparison cannot answer, which is a parameter,
|
|
561
|
-
* a wildcard
|
|
570
|
+
* a wildcard, an optional group, or a reserved character. A regular expression is already
|
|
571
|
+
* compiled, so it needs nothing.
|
|
572
|
+
*
|
|
573
|
+
* The escapes are what the short list of three used to miss: "/\\(a\\)" is the way to ask for a
|
|
574
|
+
* literal parenthesis, and comparing it as text looked for the backslashes on the wire and
|
|
575
|
+
* answered 404. A reserved character written bare, "/(a)", has to reach the compiler too, which
|
|
576
|
+
* is what refuses it the way express does.
|
|
562
577
|
*
|
|
563
578
|
* @param {string|RegExp} pattern
|
|
564
579
|
* @returns {boolean}
|
|
@@ -568,7 +583,7 @@ function needsConversionToRegex(pattern) {
|
|
|
568
583
|
return false;
|
|
569
584
|
}
|
|
570
585
|
|
|
571
|
-
return
|
|
586
|
+
return PATH_SYNTAX.test(pattern);
|
|
572
587
|
}
|
|
573
588
|
|
|
574
589
|
/**
|