mirror of
https://github.com/node-red/node-red.git
synced 2025-12-27 07:31:07 +01:00
Merge pull request #5037 from debadutta98/@feature/issue-5029
feat: Add an option to the HTTP In to include the raw body.
This commit is contained in:
@@ -25,11 +25,22 @@
|
||||
<option value="patch">PATCH</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row form-row-http-in-upload hide">
|
||||
|
||||
<div id="form-reqBody-http-in-controller" class="form-row hide" style="display: flex;">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-upload" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-upload" style="width: 70%;" data-i18n="httpin.label.upload"></label>
|
||||
<div style="display: flex; margin-left: 5px; flex-direction: column-reverse; gap:35%;">
|
||||
<div id="form-row-http-in-upload" class="hide" style="display: flex;">
|
||||
<input type="checkbox" id="node-input-upload" style="margin-right: 5%; margin-bottom: 5%;">
|
||||
<label for="node-input-upload" style="text-wrap: nowrap;" data-i18n="httpin.label.upload"></label>
|
||||
</div>
|
||||
<div id="form-row-http-in-parsing" class="hide" style="display: flex;">
|
||||
<input type="checkbox" id="node-input-skipBodyParsing" style="margin-right: 5%; margin-bottom: 5%;">
|
||||
<label for="node-input-skipBodyParsing" style="text-wrap: nowrap;" data-i18n="httpin.label.parsing"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="httpin.label.url"></span></label>
|
||||
<input id="node-input-url" type="text" placeholder="/url">
|
||||
@@ -74,6 +85,7 @@
|
||||
label:RED._("node-red:httpin.label.url")},
|
||||
method: {value:"get",required:true},
|
||||
upload: {value:false},
|
||||
skipBodyParsing: {value:false},
|
||||
swaggerDoc: {type:"swagger-doc", required:false}
|
||||
},
|
||||
inputs:0,
|
||||
@@ -115,16 +127,22 @@
|
||||
$('.row-swagger-doc').hide();
|
||||
}
|
||||
$("#node-input-method").on("change", function() {
|
||||
if ($(this).val() === "post") {
|
||||
$(".form-row-http-in-upload").show();
|
||||
var method = $(this).val();
|
||||
if(["post", "put", "patch","delete"].includes(method)){
|
||||
$("#form-reqBody-http-in-controller").show();
|
||||
$("#form-row-http-in-parsing").show();
|
||||
if (method === "post") {
|
||||
$("#form-row-http-in-upload").show();
|
||||
} else {
|
||||
$("#form-row-http-in-upload").hide();
|
||||
}
|
||||
} else {
|
||||
$(".form-row-http-in-upload").hide();
|
||||
$("#form-row-http-in-parsing").hide();
|
||||
$("#form-row-http-in-upload").hide();
|
||||
$("#form-reqBody-http-in-controller").hide();
|
||||
}
|
||||
}).change();
|
||||
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
var headerTypes = [
|
||||
{value:"content-type",label:"Content-Type",hasValue: false},
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
var rootApp;
|
||||
var bodyParser = require("body-parser");
|
||||
var multer = require("multer");
|
||||
var cookieParser = require("cookie-parser");
|
||||
@@ -26,6 +27,7 @@ module.exports = function(RED) {
|
||||
var mediaTyper = require('media-typer');
|
||||
var isUtf8 = require('is-utf8');
|
||||
var hashSum = require("hash-sum");
|
||||
var rawDataRoutes = new Set();
|
||||
|
||||
function rawBodyParser(req, res, next) {
|
||||
if (req.skipRawBodyParser) { next(); } // don't parse this if told to skip
|
||||
@@ -71,7 +73,65 @@ module.exports = function(RED) {
|
||||
});
|
||||
}
|
||||
|
||||
var corsSetup = false;
|
||||
/**
|
||||
* This method retrieves the root app by traversing the parent hierarchy.
|
||||
* @param {import('express').Application} app
|
||||
* @returns {import('express').Application}
|
||||
*/
|
||||
function getRootApp(app) {
|
||||
if (typeof app.parent === 'undefined') {
|
||||
return app;
|
||||
}
|
||||
return getRootApp(app.parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* It provide the unique route key
|
||||
* @param {{method: string, url: string}} obj
|
||||
* @returns
|
||||
*/
|
||||
function getRouteKey(obj) {
|
||||
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(/\/$/, '');
|
||||
return `${method}:${url}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* This middleware is for capture raw body
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} _res
|
||||
* @param {import('express').NextFunction} next
|
||||
* @returns
|
||||
*/
|
||||
function rawBodyCapture(req, _res, next) {
|
||||
var routeKey = getRouteKey({ method: req.method, url: req._parsedUrl.pathname });
|
||||
// Check if routeKey exist in rawDataRoutes
|
||||
if (rawDataRoutes.has(routeKey)) {
|
||||
// Convert the request stream to buffer
|
||||
getBody(req, {
|
||||
length: req.headers['content-length'],
|
||||
}, function (err, buf) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
req.body = buf;
|
||||
// Skip the body parsing
|
||||
req.skipRawBodyParser = true;
|
||||
req._body = true;
|
||||
next();
|
||||
})
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
if(typeof RED.httpNode === 'function' && (rootApp = getRootApp(RED.httpNode))) {
|
||||
// Add middleware to the stack
|
||||
rootApp.use(rawBodyCapture);
|
||||
// Move the middleware 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
|
||||
@@ -125,6 +185,7 @@ module.exports = function(RED) {
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function createResponseWrapper(node,res) {
|
||||
var wrapper = {
|
||||
_res: res
|
||||
@@ -188,9 +249,16 @@ module.exports = function(RED) {
|
||||
}
|
||||
this.method = n.method;
|
||||
this.upload = n.upload;
|
||||
this.skipBodyParsing = n.skipBodyParsing;
|
||||
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.skipBodyParsing) {
|
||||
rawDataRoutes.add(routeKey);
|
||||
}
|
||||
|
||||
this.errorHandler = function(err,req,res,next) {
|
||||
node.warn(err);
|
||||
@@ -227,7 +295,9 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
var maxApiRequestSize = RED.settings.apiMaxLength || '5mb';
|
||||
|
||||
var jsonParser = bodyParser.json({limit:maxApiRequestSize});
|
||||
|
||||
var urlencParser = bodyParser.urlencoded({limit:maxApiRequestSize,extended:true});
|
||||
|
||||
var metricsHandler = function(req,res,next) { next(); }
|
||||
@@ -254,25 +324,27 @@ module.exports = function(RED) {
|
||||
var mp = multer({ storage: multer.memoryStorage() }).any();
|
||||
multipartParser = function(req,res,next) {
|
||||
mp(req,res,function(err) {
|
||||
req._body = true;
|
||||
next(err);
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
if (this.method == "get") {
|
||||
RED.httpNode.get(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,this.callback,this.errorHandler);
|
||||
RED.httpNode.get(this.url, cookieParser(), httpMiddleware, corsHandler, metricsHandler, this.callback, this.errorHandler);
|
||||
} else if (this.method == "post") {
|
||||
RED.httpNode.post(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,multipartParser,rawBodyParser,this.callback,this.errorHandler);
|
||||
RED.httpNode.post(this.url,cookieParser(), httpMiddleware, corsHandler, metricsHandler, jsonParser, urlencParser, multipartParser, rawBodyParser, this.callback, this.errorHandler);
|
||||
} else if (this.method == "put") {
|
||||
RED.httpNode.put(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
|
||||
RED.httpNode.put(this.url, cookieParser(), httpMiddleware, corsHandler, metricsHandler, jsonParser, urlencParser, rawBodyParser, this.callback, this.errorHandler);
|
||||
} else if (this.method == "patch") {
|
||||
RED.httpNode.patch(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
|
||||
RED.httpNode.patch(this.url, cookieParser(), httpMiddleware, corsHandler, metricsHandler, jsonParser, urlencParser, rawBodyParser, this.callback, this.errorHandler);
|
||||
} else if (this.method == "delete") {
|
||||
RED.httpNode.delete(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
|
||||
RED.httpNode.delete(this.url, cookieParser(), httpMiddleware, corsHandler, metricsHandler, jsonParser, urlencParser, rawBodyParser, this.callback, this.errorHandler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.on("close",function() {
|
||||
// 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]) {
|
||||
@@ -284,9 +356,9 @@ module.exports = function(RED) {
|
||||
this.warn(RED._("httpin.errors.not-created"));
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.registerType("http in",HTTPIn);
|
||||
|
||||
|
||||
function HTTPOut(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
@@ -361,5 +433,6 @@ module.exports = function(RED) {
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("http response",HTTPOut);
|
||||
}
|
||||
|
||||
@@ -516,6 +516,7 @@
|
||||
"doc": "Docs",
|
||||
"return": "Return",
|
||||
"upload": "Accept file uploads?",
|
||||
"parsing": "Skip body parsing?",
|
||||
"status": "Status code",
|
||||
"headers": "Headers",
|
||||
"other": "other",
|
||||
|
||||
2
packages/node_modules/node-red/red.js
vendored
2
packages/node_modules/node-red/red.js
vendored
@@ -66,6 +66,7 @@ var knownOpts = {
|
||||
"define": [String, Array],
|
||||
"no-telemetry": Boolean
|
||||
};
|
||||
|
||||
var shortHands = {
|
||||
"?":["--help"],
|
||||
"p":["--port"],
|
||||
@@ -292,7 +293,6 @@ httpsPromise.then(function(startupHttps) {
|
||||
server = http.createServer(function(req,res) {app(req,res);});
|
||||
}
|
||||
server.setMaxListeners(0);
|
||||
|
||||
function formatRoot(root) {
|
||||
if (root[0] != "/") {
|
||||
root = "/" + root;
|
||||
|
||||
Reference in New Issue
Block a user