1
0
Fork 0
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:
Dorian Niemiec 2024-08-27 21:09:43 +02:00
parent 3eb7f3d4d9
commit b3cd8d699d
6 changed files with 125 additions and 8 deletions

View file

@ -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__) {

View file

@ -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;

View file

@ -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);

View file

@ -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
View 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;

View 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]);
});
});