forked from svrjs/svrjs
Replace shallow cloning with deep cloning when needed. Also create deep cloning JavaScript file and corresponding tests
This commit is contained in:
parent
3eb7f3d4d9
commit
b3cd8d699d
6 changed files with 125 additions and 8 deletions
|
@ -4,16 +4,18 @@ const defaultPageCSS = require("../res/defaultPageCSS.js");
|
|||
const generateErrorStack = require("../utils/generateErrorStack.js");
|
||||
const serverHTTPErrorDescs = require("../res/httpErrorDescriptions.js");
|
||||
const generateServerString = require("../utils/generateServerString.js");
|
||||
const deepClone = require("../utils/deepClone.js");
|
||||
|
||||
let serverconsole = {};
|
||||
|
||||
function clientErrorHandler(err, socket) {
|
||||
const config = Object.assign({}, process.serverConfig);
|
||||
const config = deepClone(process.serverConfig);
|
||||
|
||||
config.generateServerString = () =>
|
||||
generateServerString(config.exposeServerVersion);
|
||||
|
||||
// getCustomHeaders() in SVR.JS 3.x
|
||||
config.getCustomHeaders = () => Object.assign({}, config.customHeaders);
|
||||
config.getCustomHeaders = () => deepClone(config.customHeaders);
|
||||
|
||||
// Prevent multiple error handlers from one request
|
||||
if (socket.__assigned__) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
const svrjsInfo = require("../../svrjs.json");
|
||||
const deepClone = require("../utils/deepClone.js");
|
||||
|
||||
let serverconsole = {};
|
||||
|
||||
|
@ -28,7 +27,7 @@ function noproxyHandler(req, socket, head) {
|
|||
socket.on("error", () => {});
|
||||
|
||||
// SVR.JS configuration object (modified)
|
||||
const config = Object.assign({}, process.serverConfig);
|
||||
const config = deepClone(process.serverConfig);
|
||||
|
||||
var reqip = socket.remoteAddress;
|
||||
var reqport = socket.remotePort;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const generateServerString = require("../utils/generateServerString");
|
||||
const deepClone = require("../utils/deepClone.js");
|
||||
const svrjsInfo = require("../../svrjs.json");
|
||||
const { name } = svrjsInfo;
|
||||
|
||||
|
@ -29,7 +30,7 @@ function proxyHandler(req, socket, head) {
|
|||
socket.on("error", () => {});
|
||||
|
||||
// SVR.JS configuration object (modified)
|
||||
const config = Object.assign({}, process.serverConfig);
|
||||
const config = deepClone(process.serverConfig);
|
||||
|
||||
config.generateServerString = () => {
|
||||
return generateServerString(config.exposeServerVersion);
|
||||
|
|
|
@ -9,6 +9,7 @@ const ipMatch = require("../utils/ipMatch.js");
|
|||
const matchHostname = require("../utils/matchHostname.js");
|
||||
const generateServerString = require("../utils/generateServerString.js");
|
||||
const parseURL = require("../utils/urlParser.js");
|
||||
const deepClone = require("../utils/deepClone.js");
|
||||
|
||||
let serverconsole = {};
|
||||
let middleware = [];
|
||||
|
@ -31,7 +32,7 @@ function requestHandler(req, res) {
|
|||
};
|
||||
|
||||
// SVR.JS configuration object (modified)
|
||||
const config = Object.assign({}, process.serverConfig);
|
||||
const config = deepClone(process.serverConfig);
|
||||
|
||||
config.generateServerString = () => {
|
||||
return generateServerString(config.exposeServerVersion);
|
||||
|
@ -39,7 +40,7 @@ function requestHandler(req, res) {
|
|||
|
||||
// getCustomHeaders() in SVR.JS 3.x
|
||||
config.getCustomHeaders = () => {
|
||||
let ph = Object.assign({}, config.customHeaders);
|
||||
let ph = deepClone(config.customHeaders);
|
||||
if (config.customHeadersVHost) {
|
||||
let vhostP = null;
|
||||
config.customHeadersVHost.every((vhost) => {
|
||||
|
|
37
src/utils/deepClone.js
Normal file
37
src/utils/deepClone.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Function to deep clone an object or array
|
||||
function deepClone(obj, _objectsArray, _clonesArray) {
|
||||
if (!_objectsArray) _objectsArray = [];
|
||||
if (!_clonesArray) _clonesArray = [];
|
||||
if (typeof obj !== "object" || obj === null) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
let objectsArrayIndex = _objectsArray.indexOf(obj);
|
||||
if (objectsArrayIndex != -1) {
|
||||
return _clonesArray[objectsArrayIndex];
|
||||
}
|
||||
|
||||
let clone;
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
clone = [];
|
||||
_objectsArray.push(obj);
|
||||
_clonesArray.push(clone);
|
||||
obj.forEach((item, index) => {
|
||||
clone[index] = deepClone(item, _objectsArray, _clonesArray);
|
||||
});
|
||||
} else {
|
||||
clone = {};
|
||||
_objectsArray.push(obj);
|
||||
_clonesArray.push(clone);
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
clone[key] = deepClone(obj[key], _objectsArray, _clonesArray);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
module.exports = deepClone;
|
77
tests/utils/deepClone.test.js
Normal file
77
tests/utils/deepClone.test.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
const deepClone = require("../../src/utils/deepClone");
|
||||
|
||||
describe("Deep cloning function", () => {
|
||||
test("should clone a simple object", () => {
|
||||
const original = { a: 1, b: 2 };
|
||||
const cloned = deepClone(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
});
|
||||
|
||||
test("should clone a nested object", () => {
|
||||
const original = { a: 1, b: { c: 2, d: { e: 3 } } };
|
||||
const cloned = deepClone(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
expect(cloned.b).not.toBe(original.b);
|
||||
expect(cloned.b.d).not.toBe(original.b.d);
|
||||
});
|
||||
|
||||
test("should clone an array", () => {
|
||||
const original = [1, 2, 3];
|
||||
const cloned = deepClone(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
});
|
||||
|
||||
test("should clone an array of objects", () => {
|
||||
const original = [{ a: 1 }, { b: 2 }];
|
||||
const cloned = deepClone(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
expect(cloned[0]).not.toBe(original[0]);
|
||||
expect(cloned[1]).not.toBe(original[1]);
|
||||
});
|
||||
|
||||
test("should clone an object with arrays", () => {
|
||||
const original = { a: [1, 2], b: { c: [3, 4] } };
|
||||
const cloned = deepClone(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
expect(cloned.a).not.toBe(original.a);
|
||||
expect(cloned.b).not.toBe(original.b);
|
||||
expect(cloned.b.c).not.toBe(original.b.c);
|
||||
});
|
||||
|
||||
test("should return the same value for non-objects", () => {
|
||||
expect(deepClone(null)).toBe(null);
|
||||
expect(deepClone(undefined)).toBe(undefined);
|
||||
expect(deepClone(42)).toBe(42);
|
||||
expect(deepClone("string")).toBe("string");
|
||||
expect(deepClone(true)).toBe(true);
|
||||
});
|
||||
|
||||
test("should handle circular references", () => {
|
||||
const original = {};
|
||||
original.self = original;
|
||||
const cloned = deepClone(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
expect(cloned.self).toBe(cloned);
|
||||
});
|
||||
|
||||
test("should handle complex nested structures", () => {
|
||||
const original = {
|
||||
a: 1,
|
||||
b: [2, 3, { c: 4 }],
|
||||
d: { e: 5, f: [6, { g: 7 }] },
|
||||
};
|
||||
const cloned = deepClone(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
expect(cloned.b).not.toBe(original.b);
|
||||
expect(cloned.b[2]).not.toBe(original.b[2]);
|
||||
expect(cloned.d).not.toBe(original.d);
|
||||
expect(cloned.d.f).not.toBe(original.d.f);
|
||||
expect(cloned.d.f[1]).not.toBe(original.d.f[1]);
|
||||
});
|
||||
});
|
Reference in a new issue