From caeb5d3538dfc96aa3d5e04d9ae9aa2bf6ef773f Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Sun, 9 Feb 2025 21:09:30 +0530 Subject: [PATCH] Refactor raw body capture middleware to use a Set for route management and improve code consistency --- .../@node-red/nodes/core/network/21-httpin.js | 58 ++++++++----------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httpin.js b/packages/node_modules/@node-red/nodes/core/network/21-httpin.js index 81d1f4e9b..34030f13d 100644 --- a/packages/node_modules/@node-red/nodes/core/network/21-httpin.js +++ b/packages/node_modules/@node-red/nodes/core/network/21-httpin.js @@ -26,11 +26,9 @@ module.exports = function(RED) { var mediaTyper = require('media-typer'); var isUtf8 = require('is-utf8'); var hashSum = require("hash-sum"); - var PassThrough = require('stream').PassThrough - var rootApp = getRootApp(RED.httpNode) - - // Check if middleware already exists - var isMiddlewareExists = rootApp._router.stack.some(layer => layer.name === 'setupRawBodyCapture') + var PassThrough = require('stream').PassThrough; + var rootApp = getRootApp(RED.httpNode); + var rawDataRoutes = new Set(); /** * This middleware parses the raw body if the user enables it. @@ -69,7 +67,7 @@ module.exports = function(RED) { encoding: isText ? "utf8" : null }, function (err, buf) { if (err) { - return next(err) + return next(err); } if (!isText && checkUTF && isUtf8(buf)) { buf = buf.toString() @@ -77,7 +75,7 @@ module.exports = function(RED) { Object.defineProperty(req, "rawRequestBody", { value: buf, enumerable: true - }) + }); return next(); }); } @@ -91,7 +89,7 @@ module.exports = function(RED) { if (typeof app.parent === 'undefined') { return app; } - return getRootApp(app.parent) + return getRootApp(app.parent); } /** @@ -100,9 +98,9 @@ module.exports = function(RED) { * @returns */ function getRouteKey(obj) { - var method = obj.method.toUpperCase() + var method = obj.method.toUpperCase(); // Normalize the URL by replacing double slashes with a single slash and removing the trailing slash - var url = obj.url.replace(/\/{2,}/g, '/').replace(/\/$/, '') + var url = obj.url.replace(/\/{2,}/g, '/').replace(/\/$/, ''); return `${method}:${url}`; } @@ -114,17 +112,9 @@ module.exports = function(RED) { * @returns */ function setupRawBodyCapture(req, _res, next) { - var routeKey = getRouteKey({method: req.method, url: req._parsedUrl.pathname}) - var httpInNodes = rootApp.get('httpInNodes') + var routeKey = getRouteKey({ method: req.method, url: req._parsedUrl.pathname }); // Check if settings for this ID exist - if (httpInNodes.has(routeKey)) { - // Get the httpInNode by routeId - var httpInNode = httpInNodes.get(routeKey); - - // If raw body inclusion is disabled, skip the processing - if (!httpInNode.includeRawBody) { - return next(); - } + if (rawDataRoutes.has(routeKey)) { // Create a PassThrough stream to capture the request body var cloneStream = new PassThrough(); @@ -159,7 +149,7 @@ module.exports = function(RED) { // Remove listeners once the request is closed .once('close', () => { req.removeListener('data', onData); - }) + }); // Attach the clone stream to the request Object.defineProperty(req, "_nodeRedReqStream", { @@ -172,14 +162,10 @@ module.exports = function(RED) { return next(); } - if (!isMiddlewareExists) { - // Initialize HTTP node storage - rootApp.set('httpInNodes', new Map()); - // Add middleware to the stack - rootApp.use(setupRawBodyCapture) - // Move the router to top of the stack - rootApp._router.stack.unshift(rootApp._router.stack.pop()) - } + // Add middleware to the stack + rootApp.use(setupRawBodyCapture); + // Move the router to top of the stack + rootApp._router.stack.unshift(rootApp._router.stack.pop()); function createRequestWrapper(node,req) { // This misses a bunch of properties (eg headers). Before we use this function @@ -301,6 +287,12 @@ module.exports = function(RED) { this.swaggerDoc = n.swaggerDoc; var node = this; + var routeKey = getRouteKey({method: this.method, url: RED.httpNode.path() + this.url}); + + // If the user enables raw body, add it to the raw data routes. + if(this.includeRawBody) { + rawDataRoutes.add(routeKey); + } this.errorHandler = function(err,req,res,next) { node.warn(err); @@ -383,14 +375,10 @@ module.exports = function(RED) { RED.httpNode.delete(this.url, cookieParser(), httpMiddleware, corsHandler, metricsHandler, jsonParser, urlencParser, rawBodyParser, this.callback, this.errorHandler); } - var routeKey = getRouteKey({method: this.method, url: RED.httpNode.path() + this.url}) - var httpInNodes = rootApp.get('httpInNodes') - - httpInNodes.set(routeKey, this); - this.on("close",function() { - httpInNodes.delete(routeKey) + // Remove it from the raw data routes if the node is closed + rawDataRoutes.delete(routeKey); var node = this; RED.httpNode._router.stack.forEach(function(route,i,routes) { if (route.route && route.route.path === node.url && route.route.methods[node.method]) {