From 7321cd20db631cc76b35f40af3e89702d2888f58 Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Sat, 8 Feb 2025 10:59:37 +0530 Subject: [PATCH] Rename createRouteId to getRouteId and update related references; improve raw body capture stream handling --- .../@node-red/nodes/core/network/21-httpin.js | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 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 5c6ea0010..4d7786838 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 @@ -78,7 +78,7 @@ module.exports = function(RED) { return getRootApp(app.parent) } - function createRouteId(req) { + function getRouteId(req) { var method = req.method.toLowerCase(), url = req.url; return `${method}:${url}`; } @@ -94,49 +94,63 @@ module.exports = function(RED) { rootApp.set('httpInNodes', httpInNodes); - // This middleware must always be at the root to handle the raw request body correctly + /** + * This middleware must always be at the root to handle the raw request body correctly + * @param {import('express').Request} req + * @param {import('express').Response} _res + * @param {import('express').NextFunction} next + * @returns + */ function setupRawBodyCapture(req, _res, next) { - var httpInNodeId = createRouteId(req) + var httpInNodeId = getRouteId(req) // Check if settings for this ID exist if (httpInNodes.has(httpInNodeId)) { + // Get the httpInNode by routeId var httpInNode = httpInNodes.get(httpInNodeId); + // If raw body inclusion is disabled, skip the processing if (!httpInNode.includeRawBody) { return next(); } + // Create a PassThrough stream to capture the request body - var passThrough = new PassThrough(); + var cloneStream = new PassThrough(); // Function to handle 'data' event function onData(chunk) { - passThrough.write(chunk); + // Continue pushing chunks into clone stream if it is writable. + if (!cloneStream.writable) { + cloneStream.write(chunk); + } } // Function to handle 'end' or 'error' events function onEnd(err) { if (err) { - passThrough.destroy(err); + // If clone stream is already destroyed don't call destory function again + if (!cloneStream.destroyed) { + cloneStream.destroy(err); + } } else { - passThrough.end(); + cloneStream.end(); } } // Attach event listeners to the request stream - req.on('data', onData); - req.once('end', onEnd); - req.once('error', onEnd); + req.on('data', onData) + .once('end', onEnd) + .once('error', onEnd) + // Remove listeners once the request is closed + .once('close', () => { + req.removeListener('data', onData); + }) - // Remove listeners once the request is closed - req.once('close', function () { - req.removeListener('data', onData); - }); - - // Attach the passThrough stream to the request + // Attach the clone stream to the request Object.defineProperty(req, "_nodeRedReqStream", { - value: passThrough + value: cloneStream }); - + // Proceed to the next middleware if no settings found return next(); } // Proceed to the next middleware if no settings found @@ -351,7 +365,7 @@ module.exports = function(RED) { } // unique id for httpInNode based on method name and url path - var httpInNodeId = createRouteId({url: this.url, method: this.method}) + var httpInNodeId = getRouteId({url: this.url, method: this.method}) // get httpInNode from RED.httpNode var httpInNodes = rootApp.get('httpInNodes')