mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Add middleware to capture raw request body for HTTP nodes
This commit is contained in:
parent
57ccd1e15f
commit
aaa0467ba0
@ -26,6 +26,7 @@ module.exports = function(RED) {
|
||||
var mediaTyper = require('media-typer');
|
||||
var isUtf8 = require('is-utf8');
|
||||
var hashSum = require("hash-sum");
|
||||
var PassThrough = require('stream').PassThrough
|
||||
|
||||
function rawBodyParser(req, _res, next) {
|
||||
if(!req._nodeRedReqStream) {
|
||||
@ -70,6 +71,85 @@ module.exports = function(RED) {
|
||||
});
|
||||
}
|
||||
|
||||
function getRootRouter(app) {
|
||||
if (typeof app.parent === 'undefined') {
|
||||
return app;
|
||||
}
|
||||
return getRootRouter(app.parent)
|
||||
}
|
||||
|
||||
function createRouteId(req) {
|
||||
var method = req.method.toLowerCase(), url = req.url;
|
||||
return `${method}:${url}`;
|
||||
}
|
||||
|
||||
var rootApp = getRootRouter(RED.httpNode)
|
||||
|
||||
// Check if middleware already exists
|
||||
var isMiddlewarePresent = rootApp._router.stack.some(layer => layer.name === 'setupRawBodyCapture')
|
||||
|
||||
if (!isMiddlewarePresent) {
|
||||
// Initialize HTTP node settings storage
|
||||
var httpInNodes = new Map()
|
||||
rootApp.set('httpInNodes', httpInNodes);
|
||||
|
||||
// This middleware must always be at the root to handle the raw request body correctly
|
||||
function setupRawBodyCapture(req, _res, next) {
|
||||
var httpInNodeId = createRouteId(req)
|
||||
|
||||
// Check if settings for this ID exist
|
||||
if (httpInNodes.has(httpInNodeId)) {
|
||||
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();
|
||||
|
||||
// Function to handle 'data' event
|
||||
function onData(chunk) {
|
||||
passThrough.write(chunk);
|
||||
}
|
||||
|
||||
// Function to handle 'end' or 'error' events
|
||||
function onEnd(err) {
|
||||
if (err) {
|
||||
passThrough.destroy(err);
|
||||
} else {
|
||||
passThrough.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Attach event listeners to the request stream
|
||||
req.on('data', onData);
|
||||
req.on('end', onEnd);
|
||||
req.on('error', onEnd);
|
||||
|
||||
// Remove listeners once the request is closed
|
||||
req.once('close', function () {
|
||||
req.removeListener('data', onData);
|
||||
req.removeListener('end', onEnd);
|
||||
req.removeListener('error', onEnd);
|
||||
});
|
||||
|
||||
// Attach the passThrough stream to the request
|
||||
Object.defineProperty(req, "_nodeRedReqStream", {
|
||||
value: passThrough
|
||||
});
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
// Proceed to the next middleware if no settings found
|
||||
return next();
|
||||
}
|
||||
// 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
|
||||
// need to ensure it captures everything documented by Express and HTTP modules.
|
||||
@ -122,6 +202,7 @@ module.exports = function(RED) {
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function createResponseWrapper(node,res) {
|
||||
var wrapper = {
|
||||
_res: res
|
||||
@ -270,16 +351,16 @@ module.exports = function(RED) {
|
||||
} else if (this.method == "delete") {
|
||||
RED.httpNode.delete(this.url, cookieParser(), httpMiddleware, corsHandler, metricsHandler, jsonParser, urlencParser, rawBodyParser, this.callback, this.errorHandler);
|
||||
}
|
||||
|
||||
// unique id for httpInNodeSettings based on method name and url path
|
||||
var httpInNodeId = `${this.method.toLowerCase()}:${this.url}`
|
||||
|
||||
// unique id for httpInNodeSettings based on method name and url path
|
||||
var httpInNodeId = createRouteId({url: this.url, method: this.method})
|
||||
|
||||
// get httpInNodeSettings from RED.httpNode
|
||||
var httpInNodes = RED.httpNode.get('httpInNodes')
|
||||
var httpInNodes = rootApp.get('httpInNodes')
|
||||
|
||||
// Add httpInNode to RED.httpNode
|
||||
httpInNodes.set(httpInNodeId, this);
|
||||
|
||||
|
||||
this.on("close",function() {
|
||||
// remove httpInNodeSettings from RED.httpNode
|
||||
httpInNodes.remove(httpInNodeId)
|
||||
|
58
packages/node_modules/node-red/red.js
vendored
58
packages/node_modules/node-red/red.js
vendored
@ -287,64 +287,8 @@ httpsPromise.then(function(startupHttps) {
|
||||
} else {
|
||||
server = http.createServer(function(req,res) {app(req,res);});
|
||||
}
|
||||
|
||||
server.setMaxListeners(0);
|
||||
|
||||
var httpInNodes = new Map()
|
||||
|
||||
app.set('httpInNodes', httpInNodes);
|
||||
|
||||
// This middleware must always be at the root to handle the raw request body correctly.
|
||||
app.use(function(req, _res, next) {
|
||||
var method = req.method.toLowerCase(), url = req.url;
|
||||
var httpInNodeId = `${method}:${url}`
|
||||
|
||||
// Check if settings for this ID exist
|
||||
if (httpInNodes.has(httpInNodeId)) {
|
||||
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
|
||||
const passThrough = new PassThrough();
|
||||
|
||||
// Function to handle 'data' event
|
||||
function onData(chunk) {
|
||||
passThrough.write(chunk);
|
||||
}
|
||||
|
||||
// Function to handle 'end' or 'error' events
|
||||
function onEnd(err) {
|
||||
if (err) {
|
||||
passThrough.destroy(err);
|
||||
} else {
|
||||
passThrough.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Attach event listeners to the request stream
|
||||
req.on('data', onData);
|
||||
req.on('end', onEnd);
|
||||
req.on('error', onEnd);
|
||||
|
||||
// Remove listeners once the request is closed
|
||||
req.once('close', function() {
|
||||
req.removeListener('data', onData);
|
||||
req.removeListener('end', onEnd);
|
||||
req.removeListener('error', onEnd);
|
||||
});
|
||||
|
||||
// Attach the passThrough stream to the request
|
||||
Object.defineProperty(req, "_nodeRedReqStream", {
|
||||
value: passThrough
|
||||
});
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
// Proceed to the next middleware if no settings found
|
||||
return next();
|
||||
});
|
||||
|
||||
function formatRoot(root) {
|
||||
if (root[0] != "/") {
|
||||
|
Loading…
x
Reference in New Issue
Block a user