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 generateErrorStack = require("../utils/generateErrorStack.js");
|
||||||
const serverHTTPErrorDescs = require("../res/httpErrorDescriptions.js");
|
const serverHTTPErrorDescs = require("../res/httpErrorDescriptions.js");
|
||||||
const generateServerString = require("../utils/generateServerString.js");
|
const generateServerString = require("../utils/generateServerString.js");
|
||||||
|
const deepClone = require("../utils/deepClone.js");
|
||||||
|
|
||||||
let serverconsole = {};
|
let serverconsole = {};
|
||||||
|
|
||||||
function clientErrorHandler(err, socket) {
|
function clientErrorHandler(err, socket) {
|
||||||
const config = Object.assign({}, process.serverConfig);
|
const config = deepClone(process.serverConfig);
|
||||||
|
|
||||||
config.generateServerString = () =>
|
config.generateServerString = () =>
|
||||||
generateServerString(config.exposeServerVersion);
|
generateServerString(config.exposeServerVersion);
|
||||||
|
|
||||||
// getCustomHeaders() in SVR.JS 3.x
|
// getCustomHeaders() in SVR.JS 3.x
|
||||||
config.getCustomHeaders = () => Object.assign({}, config.customHeaders);
|
config.getCustomHeaders = () => deepClone(config.customHeaders);
|
||||||
|
|
||||||
// Prevent multiple error handlers from one request
|
// Prevent multiple error handlers from one request
|
||||||
if (socket.__assigned__) {
|
if (socket.__assigned__) {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// eslint-disable-next-line no-unused-vars
|
const deepClone = require("../utils/deepClone.js");
|
||||||
const svrjsInfo = require("../../svrjs.json");
|
|
||||||
|
|
||||||
let serverconsole = {};
|
let serverconsole = {};
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ function noproxyHandler(req, socket, head) {
|
||||||
socket.on("error", () => {});
|
socket.on("error", () => {});
|
||||||
|
|
||||||
// SVR.JS configuration object (modified)
|
// SVR.JS configuration object (modified)
|
||||||
const config = Object.assign({}, process.serverConfig);
|
const config = deepClone(process.serverConfig);
|
||||||
|
|
||||||
var reqip = socket.remoteAddress;
|
var reqip = socket.remoteAddress;
|
||||||
var reqport = socket.remotePort;
|
var reqport = socket.remotePort;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const generateServerString = require("../utils/generateServerString");
|
const generateServerString = require("../utils/generateServerString");
|
||||||
|
const deepClone = require("../utils/deepClone.js");
|
||||||
const svrjsInfo = require("../../svrjs.json");
|
const svrjsInfo = require("../../svrjs.json");
|
||||||
const { name } = svrjsInfo;
|
const { name } = svrjsInfo;
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ function proxyHandler(req, socket, head) {
|
||||||
socket.on("error", () => {});
|
socket.on("error", () => {});
|
||||||
|
|
||||||
// SVR.JS configuration object (modified)
|
// SVR.JS configuration object (modified)
|
||||||
const config = Object.assign({}, process.serverConfig);
|
const config = deepClone(process.serverConfig);
|
||||||
|
|
||||||
config.generateServerString = () => {
|
config.generateServerString = () => {
|
||||||
return generateServerString(config.exposeServerVersion);
|
return generateServerString(config.exposeServerVersion);
|
||||||
|
|
|
@ -9,6 +9,7 @@ const ipMatch = require("../utils/ipMatch.js");
|
||||||
const matchHostname = require("../utils/matchHostname.js");
|
const matchHostname = require("../utils/matchHostname.js");
|
||||||
const generateServerString = require("../utils/generateServerString.js");
|
const generateServerString = require("../utils/generateServerString.js");
|
||||||
const parseURL = require("../utils/urlParser.js");
|
const parseURL = require("../utils/urlParser.js");
|
||||||
|
const deepClone = require("../utils/deepClone.js");
|
||||||
|
|
||||||
let serverconsole = {};
|
let serverconsole = {};
|
||||||
let middleware = [];
|
let middleware = [];
|
||||||
|
@ -31,7 +32,7 @@ function requestHandler(req, res) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// SVR.JS configuration object (modified)
|
// SVR.JS configuration object (modified)
|
||||||
const config = Object.assign({}, process.serverConfig);
|
const config = deepClone(process.serverConfig);
|
||||||
|
|
||||||
config.generateServerString = () => {
|
config.generateServerString = () => {
|
||||||
return generateServerString(config.exposeServerVersion);
|
return generateServerString(config.exposeServerVersion);
|
||||||
|
@ -39,7 +40,7 @@ function requestHandler(req, res) {
|
||||||
|
|
||||||
// getCustomHeaders() in SVR.JS 3.x
|
// getCustomHeaders() in SVR.JS 3.x
|
||||||
config.getCustomHeaders = () => {
|
config.getCustomHeaders = () => {
|
||||||
let ph = Object.assign({}, config.customHeaders);
|
let ph = deepClone(config.customHeaders);
|
||||||
if (config.customHeadersVHost) {
|
if (config.customHeadersVHost) {
|
||||||
let vhostP = null;
|
let vhostP = null;
|
||||||
config.customHeadersVHost.every((vhost) => {
|
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