diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httpin.html b/packages/node_modules/@node-red/nodes/core/network/21-httpin.html
index f828077a1..59c2bfb6f 100644
--- a/packages/node_modules/@node-red/nodes/core/network/21-httpin.html
+++ b/packages/node_modules/@node-red/nodes/core/network/21-httpin.html
@@ -25,11 +25,22 @@
-
+
+
+
+
@@ -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},
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 22e83b411..060d479b6 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
@@ -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);
}
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
index 6d33e78aa..97fdf5571 100644
--- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
@@ -516,6 +516,7 @@
"doc": "Docs",
"return": "Return",
"upload": "Accept file uploads?",
+ "parsing": "Skip body parsing?",
"status": "Status code",
"headers": "Headers",
"other": "other",
diff --git a/packages/node_modules/node-red/red.js b/packages/node_modules/node-red/red.js
index d98b69f8f..af0c34fa7 100755
--- a/packages/node_modules/node-red/red.js
+++ b/packages/node_modules/node-red/red.js
@@ -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;