Skip to content

Commit 0716583

Browse files
feat: handle circular structure serialization
1 parent dae209a commit 0716583

1 file changed

Lines changed: 75 additions & 3 deletions

File tree

lib/response.js

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,9 +1023,7 @@ function sendfile(res, file, options, callback) {
10231023
function stringify (value, replacer, spaces, escape) {
10241024
// v8 checks arguments.length for optimizing simple call
10251025
// https://bugs.chromium.org/p/v8/issues/detail?id=4730
1026-
var json = replacer || spaces
1027-
? JSON.stringify(value, replacer, spaces)
1028-
: JSON.stringify(value);
1026+
var json = stringifyWithFallback(value, replacer, spaces);
10291027

10301028
if (escape && typeof json === 'string') {
10311029
json = json.replace(/[<>&]/g, function (c) {
@@ -1045,3 +1043,77 @@ function stringify (value, replacer, spaces, escape) {
10451043

10461044
return json
10471045
}
1046+
1047+
function stringifyWithFallback(value, replacer, spaces) {
1048+
// v8 checks arguments.length for optimizing simple call
1049+
// https://bugs.chromium.org/p/v8/issues/detail?id=4730
1050+
try {
1051+
return replacer || spaces
1052+
? JSON.stringify(value, replacer, spaces)
1053+
: JSON.stringify(value);
1054+
} catch (err) {
1055+
if (!isCircularJsonError(err)) {
1056+
throw err;
1057+
}
1058+
1059+
return JSON.stringify(value, createCircularReplacer(replacer), spaces);
1060+
}
1061+
}
1062+
1063+
function createCircularReplacer(replacer) {
1064+
var stack = [];
1065+
1066+
return function (key, value) {
1067+
var replacement = value;
1068+
1069+
if (typeof replacer === "function") {
1070+
replacement = replacer.call(this, key, value);
1071+
} else if (
1072+
Array.isArray(replacer) &&
1073+
key !== "" &&
1074+
!Array.isArray(this) &&
1075+
!hasReplacerKey(replacer, key)
1076+
) {
1077+
return undefined;
1078+
}
1079+
1080+
if (typeof replacement !== "object" || replacement === null) {
1081+
return replacement;
1082+
}
1083+
1084+
while (stack.length > 0 && stack[stack.length - 1] !== this) {
1085+
stack.pop();
1086+
}
1087+
1088+
if (stack.indexOf(replacement) !== -1) {
1089+
return "[Circular]";
1090+
}
1091+
1092+
stack.push(replacement);
1093+
return replacement;
1094+
};
1095+
}
1096+
1097+
function hasReplacerKey(replacer, key) {
1098+
for (var i = 0; i < replacer.length; i++) {
1099+
var value = replacer[i];
1100+
1101+
if (typeof value === "string" || typeof value === "number") {
1102+
if (String(value) === key) {
1103+
return true;
1104+
}
1105+
} else if (
1106+
typeof value === "object" &&
1107+
value !== null &&
1108+
String(value) === key
1109+
) {
1110+
return true;
1111+
}
1112+
}
1113+
1114+
return false;
1115+
}
1116+
1117+
function isCircularJsonError(err) {
1118+
return err instanceof TypeError && /circular structure/i.test(err.message);
1119+
}

0 commit comments

Comments
 (0)