diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js index 9eac4da91..f7b17e6d1 100644 --- a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js +++ b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js @@ -69,8 +69,6 @@ in your Node-RED user directory (${RED.settings.userDir}). var nodeUrl = n.url; var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1; var nodeMethod = n.method || "GET"; - var paytoqs = false; - var paytobody = false; var redirectList = []; var sendErrorsToCatch = n.senderr; node.headers = n.headers || []; @@ -78,15 +76,12 @@ in your Node-RED user directory (${RED.settings.userDir}). if (n.tls) { var tlsNode = RED.nodes.getNode(n.tls); } - this.ret = n.ret || "txt"; - this.authType = n.authType || "basic"; - if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; } - else { this.reqTimeout = 120000; } - - if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; } - else if (n.paytoqs === "body") { paytobody = true; } - + node.ret = n.ret || "txt"; + node.authType = n.authType || "basic"; + if (RED.settings.httpRequestTimeout) { node.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; } + else { node.reqTimeout = 120000; } node.insecureHTTPParser = n.insecureHTTPParser + node.paytoqs = n.paytoqs var prox, noprox; if (process.env.http_proxy) { prox = process.env.http_proxy; } @@ -196,20 +191,20 @@ in your Node-RED user directory (${RED.settings.userDir}). } } - var method = nodeMethod.toUpperCase() || "GET"; + /** @type {boolean|'query'|'body'} */ + let payloadHandling = node.paytoqs + let method = nodeMethod.toUpperCase() || "GET"; if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set node.warn(RED._("common.errors.nooverride")); } if (msg.method && n.method && (n.method === "use")) { - method = msg.method.toUpperCase(); // use the msg parameter + method = msg.method.toUpperCase(); // use the msg parameter + payloadHandling = msg.payloadHandling } + if (payloadHandling === true) { payloadHandling = "query" } - // var isHttps = (/^https/i.test(url)); - + /** @type {import('got').Options} */ var opts = {}; - // set defaultport, else when using HttpsProxyAgent, it's defaultPort of 443 will be used :(. - // Had to remove this to get http->https redirect to work - // opts.defaultPort = isHttps?443:80; opts.timeout = node.reqTimeout; opts.throwHttpErrors = false; // TODO: add UI option to auto decompress. Setting to false for 1.x compatibility @@ -472,7 +467,7 @@ in your Node-RED user directory (${RED.settings.userDir}). } - if (method == 'GET' && typeof msg.payload !== "undefined" && paytoqs) { + if (method == "GET" && typeof msg.payload !== "undefined" && payloadHandling === "query") { if (typeof msg.payload === "object") { try { if (url.indexOf("?") !== -1) { @@ -481,18 +476,16 @@ in your Node-RED user directory (${RED.settings.userDir}). url += "?" + querystring.stringify(msg.payload); } } catch(err) { - node.error(RED._("httpin.errors.invalid-payload"),msg); nodeDone(); return; } } else { - node.error(RED._("httpin.errors.invalid-payload"),msg); nodeDone(); return; } - } else if ( method == "GET" && typeof msg.payload !== "undefined" && paytobody) { + } else if ( method == "GET" && typeof msg.payload !== "undefined" && payloadHandling === "body") { opts.allowGetBody = true; if (typeof msg.payload === "object") { opts.body = JSON.stringify(msg.payload); diff --git a/test/nodes/core/network/21-httprequest_spec.js b/test/nodes/core/network/21-httprequest_spec.js index c6c6ac4ac..b0307d7f4 100644 --- a/test/nodes/core/network/21-httprequest_spec.js +++ b/test/nodes/core/network/21-httprequest_spec.js @@ -294,6 +294,18 @@ describe('HTTP Request Node', function() { url: req.originalUrl }); }) + testApp.get('/getBodyParams', function(req,res) { + // Either body-parser or express is discarding the body OR + // GOT never sent a body. (there is nothing in params/query/body) + // Oddly, if we set options.json (instead of options.body) in the GOT + // request, then req.body will have the values! + // Has the 21-httprequest node ever been able to send a payload in body + // for a GET request? + res.json({ + // body:JSON.parse(req.body), + url: req.originalUrl + }); + }) testApp.get('/returnError/:code', function(req,res) { res.status(parseInt(req.params.code)).json({gotError:req.params.code}); }) @@ -1117,6 +1129,89 @@ describe('HTTP Request Node', function() { }); }); + it('should allow the payload to be sent in the body for a GET request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",paytoqs:"body",ret:"obj",url:getTestURL('/getBodyParams')}, + {id:"n2", type:"helper"}]; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + const payload = {a:"one",b:true,c:3} + n2.on("input", function(msg) { + try { + // Either Express does not deliver the body of a GET request OR GOT doesn't send it! + // That means we cannot test this! Oddly, if the HTTP-Req node sets options.json instead + // of options.body, then the body is delivered. + // msg.should.have.property('payload',{ + // body:payload, + // url: '/getBodyParams' + // }); + msg.should.have.property('payload').and.be.an.Object() + msg.payload.should.have.property('url', '/getBodyParams') + + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:payload}); + }); + }); + + it('should allow the message to specify that the payload be append to querystring for a GET request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"obj",url:getTestURL('/getQueryParams')}, + {id:"n2", type:"helper"}]; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + msg.should.have.property('payload',{ + query:{a:'1',b:'2',c:'3'}, + url: '/getQueryParams?a=1&b=2&c=3' + }); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:{a:1,b:2,c:3},method:"get",payloadHandling:'query'}); + }); + }); + + + it('should allow the message to specify that the payload be sent in the body for a GET request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"obj",url:getTestURL('/getBodyParams')}, + {id:"n2", type:"helper"}]; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + const payload = {a:"one",b:true,c:3} + n2.on("input", function(msg) { + try { + // Either Express does not deliver the body of a GET request OR GOT doesn't send it! + // That means we cannot test this! Oddly, if the HTTP-Req node sets options.json instead + // of options.body, then the body is delivered. + // msg.should.have.property('payload',{ + // payload: payload, + // url: '/getBodyParams' + // }); + msg.should.have.property('payload').and.be.an.Object() + msg.payload.should.have.property('url', '/getBodyParams') + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:payload,method:"get",payloadHandling:'body'}); + }); + }); + it('should send a msg for non-2xx response status - 400', function(done) { var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/400')}, {id:"n2", type:"helper"}];