diff --git a/packages/node_modules/@node-red/nodes/core/io/21-httprequest.html b/packages/node_modules/@node-red/nodes/core/io/21-httprequest.html
index e0d1f5ba2..d4cd94aba 100644
--- a/packages/node_modules/@node-red/nodes/core/io/21-httprequest.html
+++ b/packages/node_modules/@node-red/nodes/core/io/21-httprequest.html
@@ -97,6 +97,8 @@
self signed certificates.
followRedirects
If set to false
prevent following Redirect (HTTP 301).true
by default
+ requestTimeout
+ If set to a positive number, will override the globally set httpRequestTimeout
parameter.
Outputs
diff --git a/packages/node_modules/@node-red/nodes/core/io/21-httprequest.js b/packages/node_modules/@node-red/nodes/core/io/21-httprequest.js
index 59f4f8fdc..f5d731515 100644
--- a/packages/node_modules/@node-red/nodes/core/io/21-httprequest.js
+++ b/packages/node_modules/@node-red/nodes/core/io/21-httprequest.js
@@ -92,6 +92,15 @@ module.exports = function(RED) {
opts.maxRedirects = 21;
opts.jar = request.jar();
opts.proxy = null;
+ if (msg.requestTimeout) {
+ if (isNaN(msg.requestTimeout)) {
+ node.warn(RED._("httpin.errors.timeout-isnan"))
+ } else if (msg.requestTimeout < 0) {
+ node.warn(RED._("httpin.errors.timeout-isnegative"))
+ } else {
+ opts.timeout = msg.requestTimeout;
+ }
+ }
var ctSet = "Content-Type"; // set default camel case
var clSet = "Content-Length";
if (msg.headers) {
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 f55179204..a6c6e1da2 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
@@ -410,7 +410,9 @@
"json-error": "JSON parse error",
"no-url": "No url specified",
"deprecated-call":"Deprecated call to __method__",
- "invalid-transport":"non-http transport requested"
+ "invalid-transport":"non-http transport requested",
+ "timeout-isnan": "Timeout value is not a valid number, ignoring",
+ "timeout-isnegative": "Timeout value is negative, ignoring"
},
"status": {
"requesting": "requesting"
diff --git a/test/nodes/core/io/21-httprequest_spec.js b/test/nodes/core/io/21-httprequest_spec.js
index 8c39d8eac..a9ac49c99 100644
--- a/test/nodes/core/io/21-httprequest_spec.js
+++ b/test/nodes/core/io/21-httprequest_spec.js
@@ -35,11 +35,11 @@ var auth = require('basic-auth');
describe('HTTP Request Node', function() {
var testApp;
var testServer;
- var testPort = 9000;
+ var testPort = 10234;
var testSslServer;
- var testSslPort = 9100;
+ var testSslPort = 10334;
var testProxyServer;
- var testProxyPort = 9200;
+ var testProxyPort = 10444;
//save environment variables
var preEnvHttpProxyLowerCase;
@@ -132,6 +132,11 @@ describe('HTTP Request Node', function() {
res.send('hello');
}, 10000);
});
+ testApp.get('/timeout50ms', function(req, res){
+ setTimeout(function() {
+ res.send('hello');
+ }, 50);
+ });
testApp.get('/checkCookie', function(req, res){
var value = req.cookies.data;
res.send(value);
@@ -844,7 +849,7 @@ describe('HTTP Request Node', function() {
});
});
- it('shuold output an error when request timeout occurred', function(done) {
+ it('should output an error when request timeout occurred', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout')},
{id:"n2", type:"helper"}];
var timeout = RED.settings.httpRequestTimeout;
@@ -871,8 +876,95 @@ describe('HTTP Request Node', function() {
n1.receive({payload:"foo"});
});
});
- });
+ it('should output an error when request timeout occurred when set via msg.requestTimeout', function(done) {
+ var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout')},
+ {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('statusCode','ESOCKETTIMEDOUT');
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == 'http request';
+ });
+ logEvents.should.have.length(1);
+ var tstmp = logEvents[0][0].timestamp;
+ logEvents[0][0].should.eql({level:helper.log().ERROR, id:'n1',type:'http request',msg:'common.notification.errors.no-response', timestamp:tstmp});
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ n1.receive({payload:"foo", requestTimeout: 50});
+ });
+ });
+ it('should show a warning if msg.requestTimeout is not a number', function(done) {
+ var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')},
+ {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('statusCode', 200);
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == 'http request';
+ });
+ logEvents.should.have.length(2);
+ var tstmp = logEvents[0][0].timestamp;
+ logEvents[0][0].should.eql({level:helper.log().WARN, id:'n1',type:'http request',msg:'httpin.errors.timeout-isnan', timestamp:tstmp});
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ n1.receive({payload:"foo", requestTimeout: "foo"});
+ });
+ });
+ it('should show a warning if msg.requestTimeout is negative', function(done) {
+ var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')},
+ {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('statusCode', 200);
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == 'http request';
+ });
+ logEvents.should.have.length(2);
+ var tstmp = logEvents[0][0].timestamp;
+ logEvents[0][0].should.eql({level:helper.log().WARN, id:'n1',type:'http request',msg:'httpin.errors.timeout-isnegative', timestamp:tstmp});
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ n1.receive({payload:"foo", requestTimeout: -4});
+ });
+ });
+ it('should pass if response time is faster than timeout set via msg.requestTimeout', function(done) {
+ var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout50ms')},
+ {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('statusCode',200);
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ n1.receive({payload:"foo", requestTimeout: 100});
+ });
+ });
+
+ });
describe('HTTP header', function() {
it('should receive cookie', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/setCookie')},
@@ -913,7 +1005,7 @@ describe('HTTP Request Node', function() {
});
});
- it('should send cookie with obejct data', function(done) {
+ it('should send cookie with object data', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')},
{id:"n2", type:"helper"}];
helper.load(httpRequestNode, flow, function() {