mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Add include raw data option in request body.
This commit is contained in:
		
				
					committed by
					
						 Debadutta Panda
						Debadutta Panda
					
				
			
			
				
	
			
			
			
						parent
						
							92a5a28072
						
					
				
				
					commit
					f5fa05d184
				
			| @@ -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-rawdata" class="hide" style="display: flex;"> | ||||
|                 <input type="checkbox" id="node-input-includeRawBody" style="margin-right: 5%; margin-bottom: 5%;"> | ||||
|                 <label for="node-input-includeRawBody" style="text-wrap: nowrap;" data-i18n="httpin.label.rawBody"></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}, | ||||
|             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(); | ||||
|                         $("#form-row-http-in-upload").hide(); | ||||
|                     } | ||||
|                 } else { | ||||
|                     $("#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}, | ||||
|   | ||||
| @@ -27,15 +27,11 @@ module.exports = function(RED) { | ||||
|     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,7 +254,6 @@ 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); | ||||
|                     }) | ||||
|                 }; | ||||
| @@ -272,7 +271,18 @@ module.exports = function(RED) { | ||||
|                 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,8 +294,8 @@ module.exports = function(RED) { | ||||
|             this.warn(RED._("httpin.errors.not-created")); | ||||
|         } | ||||
|     } | ||||
|     RED.nodes.registerType("http in",HTTPIn); | ||||
|      | ||||
|     RED.nodes.registerType("http in",HTTPIn); | ||||
|  | ||||
|     function HTTPOut(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
| @@ -361,5 +371,6 @@ module.exports = function(RED) { | ||||
|             done(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     RED.nodes.registerType("http response",HTTPOut); | ||||
| } | ||||
|   | ||||
| @@ -451,6 +451,7 @@ | ||||
|             "upload": "Dateiuploads akzeptieren", | ||||
|             "status": "Statuscode", | ||||
|             "headers": "Kopfzeilen", | ||||
|             "rawBody": "Rohdaten einbeziehen?", | ||||
|             "other": "andere", | ||||
|             "paytoqs": { | ||||
|                 "ignore": "Ignorieren", | ||||
|   | ||||
| @@ -515,6 +515,7 @@ | ||||
|             "doc": "Docs", | ||||
|             "return": "Return", | ||||
|             "upload": "Accept file uploads?", | ||||
|             "rawBody": "Include Raw Data?", | ||||
|             "status": "Status code", | ||||
|             "headers": "Headers", | ||||
|             "other": "other", | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -518,6 +518,7 @@ | ||||
|             "status": "ステータスコード", | ||||
|             "headers": "ヘッダ", | ||||
|             "other": "その他", | ||||
|             "rawBody": "生データを含める?", | ||||
|             "paytoqs": { | ||||
|                 "ignore": "無視", | ||||
|                 "query": "クエリパラメータに追加", | ||||
|   | ||||
| @@ -397,7 +397,8 @@ | ||||
|             "binaryBuffer": "바이너리 버퍼", | ||||
|             "jsonObject": "JSON오브젝트", | ||||
|             "authType": "종류별", | ||||
|             "bearerToken": "토큰" | ||||
|             "bearerToken": "토큰", | ||||
|             "rawBody": "원시 데이터를 포함할까요?" | ||||
|         }, | ||||
|         "setby": "- msg.method에 정의 -", | ||||
|         "basicauth": "인증을 사용", | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -411,6 +411,7 @@ | ||||
|             "status": "Код состояния", | ||||
|             "headers": "Заголовки", | ||||
|             "other": "другое", | ||||
|             "rawBody": "Включить необработанные данные?", | ||||
|             "paytoqs": { | ||||
|                 "ignore": "Игнорировать", | ||||
|                 "query": "Добавлять к параметрам строки запроса", | ||||
|   | ||||
| @@ -508,6 +508,7 @@ | ||||
|       "status": "状态码", | ||||
|       "headers": "头", | ||||
|       "other": "其他", | ||||
|       "rawBody": "包含原始数据?", | ||||
|       "paytoqs": { | ||||
|         "ignore": "忽略", | ||||
|         "query": "附加到查询字符串参数", | ||||
|   | ||||
| @@ -416,7 +416,8 @@ | ||||
|             "binaryBuffer": "二進制buffer", | ||||
|             "jsonObject": "解析的JSON對象", | ||||
|             "authType": "類型", | ||||
|             "bearerToken": "Token" | ||||
|             "bearerToken": "Token", | ||||
|             "rawBody": "包含原始數據?" | ||||
|         }, | ||||
|         "setby": "- 用 msg.method 設定 -", | ||||
|         "basicauth": "基本認證", | ||||
|   | ||||
							
								
								
									
										59
									
								
								packages/node_modules/node-red/red.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										59
									
								
								packages/node_modules/node-red/red.js
									
									
									
									
										vendored
									
									
								
							| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user