From 683367e68692b5b06018e95f0b626a41f1698cf9 Mon Sep 17 00:00:00 2001 From: Matthias Koch Date: Thu, 23 Oct 2025 19:25:18 +0200 Subject: [PATCH 1/5] Add maxHeaderSize --- index.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 5bb7894..7d3ef78 100644 --- a/index.js +++ b/index.js @@ -16,6 +16,7 @@ const { PROMETHEUS_WITH_METHOD = 'true', PROMETHEUS_WITH_STATUS = 'true', PROMETHEUS_METRIC_TYPE = 'summary', + MAX_HEADER_SIZE = 1048576 } = process.env const sleep = promisify(setTimeout); @@ -189,22 +190,27 @@ app.all('*', (req, res) => { }); -let sslOpts = { +let httpOpts = { + maxHeaderSize: process.env.MAX_HEADER_SIZE +} + +let httpsOpts = { key: require('fs').readFileSync(process.env.HTTPS_KEY_FILE || 'privkey.pem'), - cert: require('fs').readFileSync(process.env.HTTPS_CERT_FILE || 'fullchain.pem') + cert: require('fs').readFileSync(process.env.HTTPS_CERT_FILE || 'fullchain.pem'), + maxHeaderSize: process.env.MAX_HEADER_SIZE }; //Whether to enable the client certificate feature if(process.env.MTLS_ENABLE){ - sslOpts = { + httpsOpts = { requestCert: true, rejectUnauthorized: false, - ...sslOpts + ...httpsOpts } } -var httpServer = http.createServer(app).listen(process.env.HTTP_PORT || 8080); -var httpsServer = https.createServer(sslOpts,app).listen(process.env.HTTPS_PORT || 8443); +var httpServer = http.createServer(httpOpts, app).listen(process.env.HTTP_PORT || 8080); +var httpsServer = https.createServer(httpsOpts,app).listen(process.env.HTTPS_PORT || 8443); console.log(`Listening on ports ${process.env.HTTP_PORT || 8080} for http, and ${process.env.HTTPS_PORT || 8443} for https.`); let calledClose = false; From 28b0e99cb5457560286876f13aeee28d8ffd115b Mon Sep 17 00:00:00 2001 From: Matthias Koch Date: Thu, 23 Oct 2025 19:26:43 +0200 Subject: [PATCH 2/5] Refactor index.js for improved readability and consistency --- index.js | 58 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/index.js b/index.js index 7d3ef78..8ebb2c5 100644 --- a/index.js +++ b/index.js @@ -32,16 +32,16 @@ const app = express() app.set('json spaces', 2); app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']); -if(PROMETHEUS_ENABLED === 'true') { +if (PROMETHEUS_ENABLED === 'true') { app.use(metricsMiddleware); } -if(process.env.DISABLE_REQUEST_LOGS !== 'true'){ +if (process.env.DISABLE_REQUEST_LOGS !== 'true') { app.use(morgan('combined')); } -app.use(function(req, res, next){ - req.pipe(concat(function(data){ +app.use(function (req, res, next) { + req.pipe(concat(function (data) { if (req.get("Content-Encoding") === "gzip") { req.body = zlib.gunzipSync(data).toString('utf8'); @@ -54,10 +54,10 @@ app.use(function(req, res, next){ }); //Handle all paths app.all('*', (req, res) => { - - if(process.env.OVERRIDE_RESPONSE_BODY_FILE_PATH){ + + if (process.env.OVERRIDE_RESPONSE_BODY_FILE_PATH) { // Path is relative to current directory - res.sendFile(process.env.OVERRIDE_RESPONSE_BODY_FILE_PATH, { root : __dirname}); + res.sendFile(process.env.OVERRIDE_RESPONSE_BODY_FILE_PATH, { root: __dirname }); return; } @@ -83,35 +83,35 @@ app.all('*', (req, res) => { } }; - if(process.env.PRESERVE_HEADER_CASE){ - let newHeaders = {...req.headers}; + if (process.env.PRESERVE_HEADER_CASE) { + let newHeaders = { ...req.headers }; // req.headers is in lowercase, processed, deduplicated. req.rawHeaders is not. // Match on the preserved case of the header name, populate newHeaders with preserved case and processed value. for (let i = 0; i < req.rawHeaders.length; i += 2) { let preservedHeaderName = req.rawHeaders[i]; if (preservedHeaderName == preservedHeaderName.toLowerCase()) { continue; } - + newHeaders[preservedHeaderName] = req.header(preservedHeaderName); delete newHeaders[preservedHeaderName.toLowerCase()]; } echo.headers = newHeaders; } - + //Add client certificate details to the output, if present //This only works if `requestCert` is true when starting the server. - if(req.socket.getPeerCertificate){ + if (req.socket.getPeerCertificate) { echo.clientCertificate = req.socket.getPeerCertificate(); } //Include visible environment variables - if(process.env.ECHO_INCLUDE_ENV_VARS){ + if (process.env.ECHO_INCLUDE_ENV_VARS) { echo.env = process.env; } //If the Content-Type of the incoming body `is` JSON, it can be parsed and returned in the body - if(req.is('application/json')){ + if (req.is('application/json')) { try { echo.json = JSON.parse(req.body) } catch (error) { @@ -126,7 +126,7 @@ app.all('*', (req, res) => { echo.jwt = token; } else { token = token.split(" ").pop(); - const decoded = jwt.decode(token, {complete: true}); + const decoded = jwt.decode(token, { complete: true }); echo.jwt = decoded; } } @@ -144,12 +144,12 @@ app.all('*', (req, res) => { //Set the response content type to what the user wants const setResponseContentType = req.headers["x-set-response-content-type"] || req.query["x-set-response-content-type"]; - if(setResponseContentType){ + if (setResponseContentType) { res.contentType(setResponseContentType); } //Set the CORS policy - if (process.env.CORS_ALLOW_ORIGIN){ + if (process.env.CORS_ALLOW_ORIGIN) { res.header('Access-Control-Allow-Origin', process.env.CORS_ALLOW_ORIGIN); if (process.env.CORS_ALLOW_METHODS) { res.header('Access-Control-Allow-Methods', process.env.CORS_ALLOW_METHODS); @@ -163,7 +163,7 @@ app.all('*', (req, res) => { } //Ability to send an empty response back - if (process.env.ECHO_BACK_TO_CLIENT != undefined && process.env.ECHO_BACK_TO_CLIENT == "false"){ + if (process.env.ECHO_BACK_TO_CLIENT != undefined && process.env.ECHO_BACK_TO_CLIENT == "false") { res.end(); } //Ability to send just the request body in the response, nothing else @@ -179,7 +179,7 @@ app.all('*', (req, res) => { if (!process.env.LOG_IGNORE_PATH || !new RegExp(process.env.LOG_IGNORE_PATH).test(req.path)) { let spacer = 4; - if(process.env.LOG_WITHOUT_NEWLINE){ + if (process.env.LOG_WITHOUT_NEWLINE) { spacer = null; } @@ -201,16 +201,16 @@ let httpsOpts = { }; //Whether to enable the client certificate feature -if(process.env.MTLS_ENABLE){ +if (process.env.MTLS_ENABLE) { httpsOpts = { - requestCert: true, - rejectUnauthorized: false, - ...httpsOpts - } + requestCert: true, + rejectUnauthorized: false, + ...httpsOpts + } } var httpServer = http.createServer(httpOpts, app).listen(process.env.HTTP_PORT || 8080); -var httpsServer = https.createServer(httpsOpts,app).listen(process.env.HTTPS_PORT || 8443); +var httpsServer = https.createServer(httpsOpts, app).listen(process.env.HTTPS_PORT || 8443); console.log(`Listening on ports ${process.env.HTTP_PORT || 8080} for http, and ${process.env.HTTPS_PORT || 8443} for https.`); let calledClose = false; @@ -218,7 +218,7 @@ let calledClose = false; process.on('exit', function () { if (calledClose) return; console.log('Got exit event. Trying to stop Express server.'); - server.close(function() { + server.close(function () { console.log("Express server closed"); }); }); @@ -226,11 +226,11 @@ process.on('exit', function () { process.on('SIGINT', shutDown); process.on('SIGTERM', shutDown); -function shutDown(){ +function shutDown() { console.log('Got a kill signal. Trying to exit gracefully.'); calledClose = true; - httpServer.close(function() { - httpsServer.close(function() { + httpServer.close(function () { + httpsServer.close(function () { console.log("HTTP and HTTPS servers closed. Asking process to exit."); process.exit() }); From 098fbf8013b006f1e4121da56ced3bbfeaace163 Mon Sep 17 00:00:00 2001 From: Matthias Koch Date: Thu, 23 Oct 2025 19:28:09 +0200 Subject: [PATCH 3/5] Revert format --- index.js | 60 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/index.js b/index.js index 8ebb2c5..760a327 100644 --- a/index.js +++ b/index.js @@ -32,16 +32,16 @@ const app = express() app.set('json spaces', 2); app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']); -if (PROMETHEUS_ENABLED === 'true') { +if(PROMETHEUS_ENABLED === 'true') { app.use(metricsMiddleware); } -if (process.env.DISABLE_REQUEST_LOGS !== 'true') { +if(process.env.DISABLE_REQUEST_LOGS !== 'true'){ app.use(morgan('combined')); } -app.use(function (req, res, next) { - req.pipe(concat(function (data) { +app.use(function(req, res, next){ + req.pipe(concat(function(data){ if (req.get("Content-Encoding") === "gzip") { req.body = zlib.gunzipSync(data).toString('utf8'); @@ -54,10 +54,10 @@ app.use(function (req, res, next) { }); //Handle all paths app.all('*', (req, res) => { - - if (process.env.OVERRIDE_RESPONSE_BODY_FILE_PATH) { + + if(process.env.OVERRIDE_RESPONSE_BODY_FILE_PATH){ // Path is relative to current directory - res.sendFile(process.env.OVERRIDE_RESPONSE_BODY_FILE_PATH, { root: __dirname }); + res.sendFile(process.env.OVERRIDE_RESPONSE_BODY_FILE_PATH, { root : __dirname}); return; } @@ -83,35 +83,35 @@ app.all('*', (req, res) => { } }; - if (process.env.PRESERVE_HEADER_CASE) { - let newHeaders = { ...req.headers }; + if(process.env.PRESERVE_HEADER_CASE){ + let newHeaders = {...req.headers}; // req.headers is in lowercase, processed, deduplicated. req.rawHeaders is not. // Match on the preserved case of the header name, populate newHeaders with preserved case and processed value. for (let i = 0; i < req.rawHeaders.length; i += 2) { let preservedHeaderName = req.rawHeaders[i]; if (preservedHeaderName == preservedHeaderName.toLowerCase()) { continue; } - + newHeaders[preservedHeaderName] = req.header(preservedHeaderName); delete newHeaders[preservedHeaderName.toLowerCase()]; } echo.headers = newHeaders; } - + //Add client certificate details to the output, if present //This only works if `requestCert` is true when starting the server. - if (req.socket.getPeerCertificate) { + if(req.socket.getPeerCertificate){ echo.clientCertificate = req.socket.getPeerCertificate(); } //Include visible environment variables - if (process.env.ECHO_INCLUDE_ENV_VARS) { + if(process.env.ECHO_INCLUDE_ENV_VARS){ echo.env = process.env; } //If the Content-Type of the incoming body `is` JSON, it can be parsed and returned in the body - if (req.is('application/json')) { + if(req.is('application/json')){ try { echo.json = JSON.parse(req.body) } catch (error) { @@ -126,7 +126,7 @@ app.all('*', (req, res) => { echo.jwt = token; } else { token = token.split(" ").pop(); - const decoded = jwt.decode(token, { complete: true }); + const decoded = jwt.decode(token, {complete: true}); echo.jwt = decoded; } } @@ -144,12 +144,12 @@ app.all('*', (req, res) => { //Set the response content type to what the user wants const setResponseContentType = req.headers["x-set-response-content-type"] || req.query["x-set-response-content-type"]; - if (setResponseContentType) { + if(setResponseContentType){ res.contentType(setResponseContentType); } //Set the CORS policy - if (process.env.CORS_ALLOW_ORIGIN) { + if (process.env.CORS_ALLOW_ORIGIN){ res.header('Access-Control-Allow-Origin', process.env.CORS_ALLOW_ORIGIN); if (process.env.CORS_ALLOW_METHODS) { res.header('Access-Control-Allow-Methods', process.env.CORS_ALLOW_METHODS); @@ -163,7 +163,7 @@ app.all('*', (req, res) => { } //Ability to send an empty response back - if (process.env.ECHO_BACK_TO_CLIENT != undefined && process.env.ECHO_BACK_TO_CLIENT == "false") { + if (process.env.ECHO_BACK_TO_CLIENT != undefined && process.env.ECHO_BACK_TO_CLIENT == "false"){ res.end(); } //Ability to send just the request body in the response, nothing else @@ -179,7 +179,7 @@ app.all('*', (req, res) => { if (!process.env.LOG_IGNORE_PATH || !new RegExp(process.env.LOG_IGNORE_PATH).test(req.path)) { let spacer = 4; - if (process.env.LOG_WITHOUT_NEWLINE) { + if(process.env.LOG_WITHOUT_NEWLINE){ spacer = null; } @@ -201,16 +201,16 @@ let httpsOpts = { }; //Whether to enable the client certificate feature -if (process.env.MTLS_ENABLE) { - httpsOpts = { - requestCert: true, - rejectUnauthorized: false, - ...httpsOpts - } +if(process.env.MTLS_ENABLE){ + httpsOpts = { + requestCert: true, + rejectUnauthorized: false, + ...httpsOpts + } } var httpServer = http.createServer(httpOpts, app).listen(process.env.HTTP_PORT || 8080); -var httpsServer = https.createServer(httpsOpts, app).listen(process.env.HTTPS_PORT || 8443); +var httpsServer = https.createServer(httpsOpts,app).listen(process.env.HTTPS_PORT || 8443); console.log(`Listening on ports ${process.env.HTTP_PORT || 8080} for http, and ${process.env.HTTPS_PORT || 8443} for https.`); let calledClose = false; @@ -218,7 +218,7 @@ let calledClose = false; process.on('exit', function () { if (calledClose) return; console.log('Got exit event. Trying to stop Express server.'); - server.close(function () { + server.close(function() { console.log("Express server closed"); }); }); @@ -226,11 +226,11 @@ process.on('exit', function () { process.on('SIGINT', shutDown); process.on('SIGTERM', shutDown); -function shutDown() { +function shutDown(){ console.log('Got a kill signal. Trying to exit gracefully.'); calledClose = true; - httpServer.close(function () { - httpsServer.close(function () { + httpServer.close(function() { + httpsServer.close(function() { console.log("HTTP and HTTPS servers closed. Asking process to exit."); process.exit() }); From f72db68aa44bdb19ba036eca691b628e6be04cb5 Mon Sep 17 00:00:00 2001 From: Matthias Koch Date: Thu, 23 Oct 2025 19:42:04 +0200 Subject: [PATCH 4/5] Implement large header request test in tests.sh Added a test for handling large headers in requests. --- tests.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests.sh b/tests.sh index ce94ef0..056f1c4 100755 --- a/tests.sh +++ b/tests.sh @@ -176,6 +176,17 @@ else exit 1 fi +message " Make request with a large header." +LARGE_HEADER_VALUE=$(head -c 10000 Date: Thu, 23 Oct 2025 19:43:57 +0200 Subject: [PATCH 5/5] Replace process.env.MAX_HEADER_SIZE with MAX_HEADER_SIZE --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 760a327..2041572 100644 --- a/index.js +++ b/index.js @@ -191,13 +191,13 @@ app.all('*', (req, res) => { }); let httpOpts = { - maxHeaderSize: process.env.MAX_HEADER_SIZE + maxHeaderSize: MAX_HEADER_SIZE } let httpsOpts = { key: require('fs').readFileSync(process.env.HTTPS_KEY_FILE || 'privkey.pem'), cert: require('fs').readFileSync(process.env.HTTPS_CERT_FILE || 'fullchain.pem'), - maxHeaderSize: process.env.MAX_HEADER_SIZE + maxHeaderSize: MAX_HEADER_SIZE }; //Whether to enable the client certificate feature