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..c7724d9b4 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},
+ includeRawBody: {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-rawdata").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-rawdata").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..1d46a3cb2 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,16 +26,12 @@ module.exports = function(RED) {
var mediaTyper = require('media-typer');
var isUtf8 = require('is-utf8');
var hashSum = require("hash-sum");
-
- function rawBodyParser(req, res, next) {
- if (req.skipRawBodyParser) { next(); } // don't parse this if told to skip
- if (req._body) { return next(); }
- req.body = "";
- req._body = true;
-
- var isText = true;
- var checkUTF = false;
-
+
+ function rawBodyParser(req, _res, next) {
+ if(!req._nodeRedReqStream) {
+ return next();
+ }
+ var isText = true, checkUTF = false;
if (req.headers['content-type']) {
var contentType = typer.parse(req.headers['content-type'])
if (contentType.type) {
@@ -51,28 +47,29 @@ module.exports = function(RED) {
&& (parsedType.subtype !== "x-protobuf")) {
checkUTF = true;
} else {
- // application/octet-stream or application/cbor
isText = false;
}
-
}
}
- getBody(req, {
+ getBody(req._nodeRedReqStream, {
length: req.headers['content-length'],
encoding: isText ? "utf8" : null
}, function (err, buf) {
- if (err) { return next(err); }
+ if (err) {
+ return next(err);
+ }
if (!isText && checkUTF && isUtf8(buf)) {
buf = buf.toString()
}
- req.body = buf;
- next();
+ Object.defineProperty(req, "rawRequestBody", {
+ value: buf,
+ enumerable: true
+ })
+ return next();
});
}
- var corsSetup = false;
-
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.
@@ -188,6 +185,7 @@ module.exports = function(RED) {
}
this.method = n.method;
this.upload = n.upload;
+ this.includeRawBody = n.includeRawBody;
this.swaggerDoc = n.swaggerDoc;
var node = this;
@@ -227,7 +225,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 +254,35 @@ 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);
}
+
+ // unique id for httpInNodeSettings based on method name and url path
+ var httpInNodeId = `${this.method.toLowerCase()}:${this.url}`
+ // get httpInNodeSettings from RED.httpNode
+ var httpInNodes = RED.httpNode.get('httpInNodes')
+
+ // Add httpInNode to RED.httpNode
+ httpInNodes.set(httpInNodeId, this);
+
this.on("close",function() {
+ // remove httpInNodeSettings from RED.httpNode
+ httpInNodes.remove(httpInNodeId)
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 +294,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 +371,6 @@ module.exports = function(RED) {
done();
});
}
+
RED.nodes.registerType("http response",HTTPOut);
}
diff --git a/packages/node_modules/@node-red/nodes/locales/de/messages.json b/packages/node_modules/@node-red/nodes/locales/de/messages.json
index a51e504cf..332712902 100644
--- a/packages/node_modules/@node-red/nodes/locales/de/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/de/messages.json
@@ -451,6 +451,7 @@
"upload": "Dateiuploads akzeptieren",
"status": "Statuscode",
"headers": "Kopfzeilen",
+ "rawBody": "Rohdaten einbeziehen?",
"other": "andere",
"paytoqs": {
"ignore": "Ignorieren",
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 bc89992e2..76ff2394b 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
@@ -515,6 +515,7 @@
"doc": "Docs",
"return": "Return",
"upload": "Accept file uploads?",
+ "rawBody": "Include Raw Data?",
"status": "Status code",
"headers": "Headers",
"other": "other",
diff --git a/packages/node_modules/@node-red/nodes/locales/es-ES/messages.json b/packages/node_modules/@node-red/nodes/locales/es-ES/messages.json
index b8ac84f1c..56465e451 100644
--- a/packages/node_modules/@node-red/nodes/locales/es-ES/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/es-ES/messages.json
@@ -518,6 +518,7 @@
"status": "Status code",
"headers": "Headers",
"other": "otro",
+ "rawBody": "¿Incluir datos sin procesar?",
"paytoqs": {
"ignore": "Ignore",
"query": "Append to query-string parameters",
diff --git a/packages/node_modules/@node-red/nodes/locales/fr/messages.json b/packages/node_modules/@node-red/nodes/locales/fr/messages.json
index 99002f48f..b675efcb5 100644
--- a/packages/node_modules/@node-red/nodes/locales/fr/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/fr/messages.json
@@ -518,6 +518,7 @@
"status": "Code d'état",
"headers": "En-têtes",
"other": "Autre",
+ "rawBody": "Inclure les données brutes ?",
"paytoqs": {
"ignore": "Ignorer",
"query": "Joindre aux paramètres de chaîne de requête",
diff --git a/packages/node_modules/@node-red/nodes/locales/ja/messages.json b/packages/node_modules/@node-red/nodes/locales/ja/messages.json
index 8d38ac077..eefdf5762 100644
--- a/packages/node_modules/@node-red/nodes/locales/ja/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/ja/messages.json
@@ -518,6 +518,7 @@
"status": "ステータスコード",
"headers": "ヘッダ",
"other": "その他",
+ "rawBody": "生データを含める?",
"paytoqs": {
"ignore": "無視",
"query": "クエリパラメータに追加",
diff --git a/packages/node_modules/@node-red/nodes/locales/ko/messages.json b/packages/node_modules/@node-red/nodes/locales/ko/messages.json
index c82e0f51b..abef1c89c 100644
--- a/packages/node_modules/@node-red/nodes/locales/ko/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/ko/messages.json
@@ -397,7 +397,8 @@
"binaryBuffer": "바이너리 버퍼",
"jsonObject": "JSON오브젝트",
"authType": "종류별",
- "bearerToken": "토큰"
+ "bearerToken": "토큰",
+ "rawBody": "원시 데이터를 포함할까요?"
},
"setby": "- msg.method에 정의 -",
"basicauth": "인증을 사용",
diff --git a/packages/node_modules/@node-red/nodes/locales/pt-BR/messages.json b/packages/node_modules/@node-red/nodes/locales/pt-BR/messages.json
index 51e1fd897..003c484af 100644
--- a/packages/node_modules/@node-red/nodes/locales/pt-BR/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/pt-BR/messages.json
@@ -506,6 +506,7 @@
"status": "Código de estado",
"headers": "Cabeçalhos",
"other": "outro",
+ "rawBody": "Incluir dados brutos?",
"paytoqs" : {
"ignore": "Ignorar",
"query": "Anexar aos parâmetros da cadeia de caracteres de consulta",
diff --git a/packages/node_modules/@node-red/nodes/locales/ru/messages.json b/packages/node_modules/@node-red/nodes/locales/ru/messages.json
index 2694ac6a5..49ed92239 100644
--- a/packages/node_modules/@node-red/nodes/locales/ru/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/ru/messages.json
@@ -411,6 +411,7 @@
"status": "Код состояния",
"headers": "Заголовки",
"other": "другое",
+ "rawBody": "Включить необработанные данные?",
"paytoqs": {
"ignore": "Игнорировать",
"query": "Добавлять к параметрам строки запроса",
diff --git a/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json b/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json
index 7d5616a8f..23b6b07ee 100644
--- a/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json
@@ -508,6 +508,7 @@
"status": "状态码",
"headers": "头",
"other": "其他",
+ "rawBody": "包含原始数据?",
"paytoqs": {
"ignore": "忽略",
"query": "附加到查询字符串参数",
diff --git a/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json b/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json
index 7d16c5817..2f9f3b560 100644
--- a/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json
@@ -416,7 +416,8 @@
"binaryBuffer": "二進制buffer",
"jsonObject": "解析的JSON對象",
"authType": "類型",
- "bearerToken": "Token"
+ "bearerToken": "Token",
+ "rawBody": "包含原始數據?"
},
"setby": "- 用 msg.method 設定 -",
"basicauth": "基本認證",
diff --git a/packages/node_modules/node-red/red.js b/packages/node_modules/node-red/red.js
index 5f3c9da25..273ef71e2 100755
--- a/packages/node_modules/node-red/red.js
+++ b/packages/node_modules/node-red/red.js
@@ -47,6 +47,7 @@ var fs = require("fs-extra");
const cors = require('cors');
var RED = require("./lib/red.js");
+var PassThrough = require('stream').PassThrough;
var server;
var app = express();
@@ -65,6 +66,7 @@ var knownOpts = {
"version": Boolean,
"define": [String, Array]
};
+
var shortHands = {
"?":["--help"],
"p":["--port"],
@@ -287,6 +289,63 @@ httpsPromise.then(function(startupHttps) {
}
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] != "/") {
root = "/" + root;