diff --git a/package.json b/package.json index 07ec5e84d..2dccbba81 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "grunt-sass": "~2.0.0", "grunt-simple-mocha": "~0.4.1", "grunt-webdriver": "^2.0.3", + "http-proxy": "^1.16.2", "istanbul": "0.4.5", "mocha": "^5.1.1", "should": "^8.4.0", diff --git a/test/nodes/core/io/21-httprequest_spec.js b/test/nodes/core/io/21-httprequest_spec.js index 28c166d39..76b0d8d32 100644 --- a/test/nodes/core/io/21-httprequest_spec.js +++ b/test/nodes/core/io/21-httprequest_spec.js @@ -16,23 +16,76 @@ var when = require("when"); var http = require("http"); +var https = require("https"); var should = require("should"); var express = require("express"); var bodyParser = require('body-parser'); var stoppable = require('stoppable'); var helper = require("../../helper.js"); var httpRequestNode = require("../../../../nodes/core/io/21-httprequest.js"); +var tlsNode = require("../../../../nodes/core/io/05-tls.js"); var hashSum = require("hash-sum"); +var httpProxy = require('http-proxy'); +var cookieParser = require('cookie-parser'); +var RED = require("../../../../red/red.js"); +var fs = require('fs-extra'); +var auth = require('basic-auth'); describe('HTTP Request Node', function() { var testApp; var testServer; var testPort = 9000; + var testSslServer; + var testSslPort = 9100; + var testProxyServer; + var testProxyPort = 9200; + + //save environment variables + var preEnvHttpProxyLowerCase; + var preEnvHttpProxyUpperCase; + var preEnvNoProxyLowerCase; + var preEnvNoProxyUpperCase; function startServer(done) { testPort += 1; testServer = stoppable(http.createServer(testApp)); testServer.listen(testPort,function(err) { + testSslPort += 1; + var sslOptions = { + key: fs.readFileSync('test/resources/ssl/server.key'), + cert: fs.readFileSync('test/resources/ssl/server.crt') + /* + Country Name (2 letter code) [AU]: + State or Province Name (full name) [Some-State]: + Locality Name (eg, city) []: + Organization Name (eg, company) [Internet Widgits Pty Ltd]: + Organizational Unit Name (eg, section) []: + Common Name (e.g. server FQDN or YOUR name) []:localhost + Email Address []: + + Please enter the following 'extra' attributes to be sent with your certificate request + A challenge password []: + An optional company name []: + */ + }; + testSslServer = https.createServer(sslOptions,testApp); + testSslServer.listen(testSslPort); + + testProxyPort += 1; + testProxyServer = httpProxy.createProxyServer({target:'http://localhost:' + testPort}); + testProxyServer.on('proxyReq', function(proxyReq, req, res, options) { + proxyReq.setHeader('x-testproxy-header', 'foobar'); + }); + testProxyServer.on('proxyRes', function (proxyRes, req, res, options) { + if (req.url == getTestURL('/proxyAuthenticate')){ + var user = auth.parse(req.headers['proxy-authorization']); + if (!(user.name == "foouser" && user.pass == "barpassword")){ + proxyRes.headers['proxy-authenticate'] = 'BASIC realm="test"'; + proxyRes.statusCode = 407; + } + } + }); + testProxyServer.listen(testProxyPort); done(err); }); } @@ -41,20 +94,119 @@ describe('HTTP Request Node', function() { return "http://localhost:"+testPort+url; } + function getSslTestURL(url) { + return "https://localhost:"+testSslPort+url; + } + + function getSslTestURLWithoutProtocol(url) { + return "localhost:"+testSslPort+url; + } + + function saveProxySetting() { + preEnvHttpProxyLowerCase = process.env.http_proxy; + preEnvHttpProxyUpperCase = process.env.HTTP_PROXY; + preEnvNoProxyLowerCase = process.env.no_proxy; + preEnvNoProxyUpperCase = process.env.NO_PROXY; + delete process.env.http_proxy; + delete process.env.HTTP_PROXY; + delete process.env.no_proxy; + delete process.env.NO_PROXY; + } + + function restoreProxySetting() { + process.env.http_proxy = preEnvHttpProxyLowerCase; + process.env.HTTP_PROXY = preEnvHttpProxyUpperCase; + // On Windows, if environment variable of NO_PROXY that includes lower cases + // such as No_Proxy is replaced with NO_PROXY. + process.env.no_proxy = preEnvNoProxyLowerCase; + process.env.NO_PROXY = preEnvNoProxyUpperCase; + if (preEnvHttpProxyLowerCase == undefined){ + delete process.env.http_proxy; + } + if (preEnvHttpProxyUpperCase == undefined){ + delete process.env.HTTP_PROXY; + } + if (preEnvNoProxyLowerCase == undefined){ + delete process.env.no_proxy; + } + if (preEnvNoProxyUpperCase == undefined){ + delete process.env.NO_PROXY; + } + } + before(function(done) { testApp = express(); testApp.use(bodyParser.raw({type:"*/*"})); - testApp.get('/statusCode204', function(req,res) { res.status(204).end();}) + testApp.use(cookieParser()); + testApp.get('/statusCode204', function(req,res) { res.status(204).end();}); testApp.get('/text', function(req, res){ res.send('hello'); }); + testApp.get('/redirectToText', function(req, res){ res.status(302).set('Location', getTestURL('/text')).end(); }); testApp.get('/json-valid', function(req, res){ res.json({a:1}); }); testApp.get('/json-invalid', function(req, res){ res.set('Content-Type', 'application/json').send("{a:1"); }); + testApp.get('/headersInspect', function(req, res){ res.set('x-test-header', 'bar').send("a"); }); + testApp.get('/timeout', function(req, res){ + setTimeout(function() { + res.send('hello'); + }, 10000); + }); + testApp.get('/checkCookie', function(req, res){ + var value = req.cookies.data; + res.send(value); + }); + testApp.get('/setCookie', function(req, res){ + res.cookie('data','hello'); + res.send(""); + }); + testApp.get('/authenticate', function(req, res){ + var user = auth.parse(req.headers['authorization']); + var result = { + user: user.name, + pass: user.pass, + }; + res.json(result); + }); + testApp.get('/proxyAuthenticate', function(req, res){ + var user = auth.parse(req.headers['proxy-authorization']); + var result = { + user: user.name, + pass: user.pass, + headers: req.headers + }; + res.json(result); + }); testApp.post('/postInspect', function(req,res) { var result = { body: req.body.toString(), headers: req.headers - } + }; res.json(result); }); + testApp.put('/putInspect', function(req,res) { + var result = { + body: req.body.toString(), + headers: req.headers + }; + res.json(result); + }); + testApp.delete('/deleteInspect', function(req,res) { res.status(204).end();}); + testApp.head('/headInspect', function(req,res) { res.status(204).end();}); + testApp.patch('/patchInspect', function(req,res) { + var result = { + body: req.body.toString(), + headers: req.headers + }; + res.json(result); + }); + testApp.trace('/traceInspect', function(req,res) { + var result = { + body: req.body.toString(), + headers: req.headers + }; + res.json(result); + }); + testApp.options('/*', function(req,res) { + res.status(200).end(); + }); startServer(function(err) { if (err) { done(err); @@ -65,408 +217,1003 @@ describe('HTTP Request Node', function() { after(function(done) { testServer.stop(() => { + testProxyServer.close(); helper.stopServer(done); }); }); + afterEach(function() { helper.unload(); }); - it('get plain text content', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",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('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } + describe('request', function() { + it('should get plain text content', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",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('payload','hello'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-length',''+('hello'.length)); + msg.headers.should.have.property('content-type').which.startWith('text/html'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should get JSON content', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/json-valid')}, + {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',{a:1}); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-type').which.startWith('application/json'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should send the payload as the body of a POST as application/json', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {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'); + msg.payload.body.should.eql('{"foo":"abcde"}'); + msg.payload.headers.should.have.property('content-type').which.startWith('application/json'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-type').which.startWith('application/json'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:{foo:"abcde"}}); + }); + }); + + it('should send a payload of 0 as the body of a POST as text/plain', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {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'); + msg.payload.body.should.eql('0'); + msg.payload.headers.should.have.property('content-length','1'); + msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:0, headers: { 'content-type': 'text/plain'}}); + }); + }); + + it('should send an Object payload as the body of a POST', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {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'); + msg.payload.body.should.eql('{"foo":"abcde"}'); + msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-type').which.startWith('application/json'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:{foo:"abcde"}, headers: { 'content-type': 'text/plain'}}); + }); + }); + + it('should send a Buffer as the body of a POST', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {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'); + msg.payload.body.should.eql('hello'); + msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-type').which.startWith('application/json'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:new Buffer('hello'), headers: { 'content-type': 'text/plain'}}); + }); + }); + + it('should send form-based request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {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); + msg.payload.body.should.equal("foo=1%202%203&bar="); + msg.payload.should.have.property('headers'); + msg.payload.headers.should.have.property('content-type','application/x-www-form-urlencoded'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:{foo:'1 2 3', bar:''}, headers: { 'content-type': 'application/x-www-form-urlencoded'}}); + }); + }); + + it('should send PUT request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"PUT",ret:"obj",url:getTestURL('/putInspect')}, + {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'); + msg.payload.body.should.eql('foo'); + msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-type').which.startWith('application/json'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", headers: { 'content-type': 'text/plain'}}); + }); + }); + + it('should send DELETE request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"DELETE",ret:"obj",url:getTestURL('/deleteInspect')}, + {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',''); + msg.should.have.property('statusCode',204); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:{foo:"abcde"}}); + }); + }); + + it('should send HEAD request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"txt",url:getTestURL('/headInspect')}, + {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',''); + msg.should.have.property('statusCode',204); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", method:"head"}); + }); + }); + + it('should send PATCH request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"PATCH",ret:"obj",url:getTestURL('/patchInspect')}, + {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'); + msg.payload.body.should.eql('foo'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('etag'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", headers: { 'content-type': 'text/plain'}}); + }); + }); + + it('should send OPTIONS request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"obj",url:getTestURL('/*')}, + {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", method:"options"}); + }); + }); + + it('should send TRACE request', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"obj",url:getTestURL('/traceInspect')}, + {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'); + msg.payload.body.should.eql('foo'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", method:"trace", headers: { 'content-type': 'text/plain'}}); + }); + }); + + it('should get Buffer content', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"bin",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('payload'); + Buffer.isBuffer(msg.payload).should.be.true(); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-type'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should return plain text when JSON fails to parse', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/json-invalid')}, + {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',"{a:1"); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-type').which.startWith('application/json'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should return the status code', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/statusCode204')}, + {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',''); + msg.should.have.property('statusCode',204); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should use msg.url', 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('payload','hello'); + msg.should.have.property('statusCode',200); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", url:"/foo"}); + }); + }); + + it('should output an error when URL is not provided', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:""}, + {id:"n2", type:"helper"}]; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + var inError = false; + n2.on("input", function(msg) { + inError = true; + }); + n1.receive({payload:"foo"}); + setTimeout(function() { + if (inError) { + done(new Error("no url allowed though")); + } else { + done(); + } + },20); + }); + }); + + it('should allow the message to provide the url', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt"}, + {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','hello'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-length',''+('hello'.length)); + msg.headers.should.have.property('content-type').which.startWith('text/html'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo",url:getTestURL('/text')}); + }); + }); + + it('should allow the url to contain mustache placeholders', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/te{{placeholder}}')}, + {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','hello'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-length',''+('hello'.length)); + msg.headers.should.have.property('content-type').which.startWith('text/html'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo",placeholder:"xt"}); + }); + }); + + it('should allow the url to be missing the http:// prefix', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/text').substring("http://".length)}, + {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','hello'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-length',''+('hello'.length)); + msg.headers.should.have.property('content-type').which.startWith('text/html'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should reject non http:// schemes - node config', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:"ftp://foo"}, + {id:"n2", type:"helper"}]; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + var inError = false; + n2.on("input", function(msg) { + inError = true; + }); + n1.receive({payload:"foo"}); + setTimeout(function() { + if (inError) { + done(new Error("non http(s):// scheme allowed through")); + } else { + done(); + } + },20); + }); + }); + + it('should reject non http:// schemes - msg.url', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt"}, + {id:"n2", type:"helper"}]; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + var inError = false; + n2.on("input", function(msg) { + inError = true; + }); + n1.receive({payload:"foo",url:"ftp://foo"}); + setTimeout(function() { + if (inError) { + done(new Error("non http(s):// scheme allowed through")); + } else { + done(); + } + },20); + }); + }); + + it('should use msg.method', 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('payload','hello'); + msg.should.have.property('statusCode',200); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", method:"POST"}); + }); + }); + + it('should allow the message to provide the method', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"txt",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('payload','hello'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-length',''+('hello'.length)); + msg.headers.should.have.property('content-type').which.startWith('text/html'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo",method:"get"}); + }); + }); + + it('should receive msg.responseUrl', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",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); + msg.should.have.property('responseUrl', getTestURL('/text')); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should receive msg.responseUrl when redirected', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/redirectToText')}, + {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','hello'); + msg.should.have.property('statusCode',200); + msg.should.have.property('responseUrl', getTestURL('/text')); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('shuold 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; + RED.settings.httpRequestTimeout = 50; + 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','ECONNRESET'); + done(); + } catch(err) { + done(err); + } finally { + RED.settings.httpRequestTimeout = timeout; + } + }); + n1.receive({payload:"foo"}); }); - n1.receive({payload:"foo"}); }); }); - it('get JSON content', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/json-valid')}, - {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',{a:1}); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } + 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')}, + {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.responseCookies.should.have.property('data'); + msg.responseCookies.data.should.have.property('value','hello'); + msg.should.have.property('statusCode',200); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should send cookie with string', 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() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + msg.should.have.property('payload','abc'); + msg.should.have.property('statusCode',200); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", cookies:{data:'abc'}}); + }); + }); + + it('should send cookie with obejct 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() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + msg.should.have.property('payload','abc'); + msg.should.have.property('statusCode',200); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", cookies:{data:{value:'abc'}}}); + }); + }); + + it('should send cookie by msg.headers', 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() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + msg.should.have.property('payload','abc'); + msg.should.have.property('statusCode',200); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", cookies:{boo:'123'}, headers:{'cookie':'data=abc'}}); + }); + }); + + it('should convert all HTTP headers into lower case', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {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); + msg.payload.should.have.property('headers'); + msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); + msg.payload.headers.should.have.property('content-length', "3"); + msg.payload.headers.should.have.property('if-modified-since','Sun, 01 Jun 2000 00:00:00 GMT'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", headers: { 'Content-Type':'text/plain', 'Content-Length': "3", 'If-Modified-Since':'Sun, 01 Jun 2000 00:00:00 GMT'}}); + }); + }); + + it('should receive HTTP header', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/headersInspect')}, + {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); + msg.headers.should.have.property('x-test-header','bar'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should ignore unmodified x-node-red-request-node header', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {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.payload.headers.should.have.property('content-type').which.startWith('application/json'); + msg.payload.headers.should.not.have.property('x-node-red-request-node'); + done(); + } catch(err) { + done(err); + } + }); + // Pass in a headers property with an unmodified x-node-red-request-node hash + // This should cause the node to ignore the headers + n1.receive({payload:{foo:"bar"}, headers: { 'content-type': 'text/plain', "x-node-red-request-node":"67690139"}}); + }); + }); + + it('should use modified msg.headers property', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {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.payload.headers.should.have.property('content-type').which.startWith('text/plain'); + msg.payload.headers.should.not.have.property('x-node-red-request-node'); + done(); + } catch(err) { + done(err); + } + }); + // Pass in a headers property with a x-node-red-request-node hash that doesn't match the contents + // This should cause the node to use the headers + n1.receive({payload:{foo:"bar"}, headers: { 'content-type': 'text/plain', "x-node-red-request-node":"INVALID_SUM"}}); }); - n1.receive({payload:"foo"}); }); }); - it('get Buffer content', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"bin",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('payload'); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type'); - done(); - } catch(err) { - done(err); - } + describe('protocol', function() { + it('should use msg.rejectUnauthorized', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURL('/text')}, + {id:"n2", type:"helper"}]; + helper.load(httpRequestNode, flow, function() { + var n2 = helper.getNode("n2"); + var n1 = helper.getNode("n1"); + n2.on("input", function(msg) { + try { + msg.should.have.property('payload','hello'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-length',''+('hello'.length)); + msg.headers.should.have.property('content-type').which.startWith('text/html'); + msg.should.have.property('responseUrl').which.startWith('https://'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo", rejectUnauthorized: false}); + }); + }); + + it('should use tls-config', function(done) { + var flow = [ + {id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURLWithoutProtocol('/text'),tls:"n3"}, + {id:"n2", type:"helper"}, + {id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"", verifyservercert:false}]; + var testNodes = [httpRequestNode, tlsNode]; + helper.load(testNodes, flow, function() { + var n3 = helper.getNode("n3"); + var n2 = helper.getNode("n2"); + var n1 = helper.getNode("n1"); + n2.on("input", function(msg) { + try { + msg.should.have.property('payload','hello'); + msg.should.have.property('statusCode',200); + msg.should.have.property('headers'); + msg.headers.should.have.property('content-length',''+('hello'.length)); + msg.headers.should.have.property('content-type').which.startWith('text/html'); + msg.should.have.property('responseUrl').which.startWith('https://'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should use http_proxy', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {id:"n2", type:"helper"}]; + saveProxySetting(); + process.env.http_proxy = "http://localhost:" + testProxyPort; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + restoreProxySetting(); + try { + msg.should.have.property('statusCode',200); + msg.payload.should.have.property('headers'); + msg.payload.headers.should.have.property('x-testproxy-header','foobar'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should use http_proxy when environment variable is invalid', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {id:"n2", type:"helper"}]; + saveProxySetting(); + process.env.http_proxy = "invalidvalue"; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + restoreProxySetting(); + try { + msg.should.have.property('statusCode',200); + msg.payload.should.have.property('headers'); + msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should use HTTP_PROXY', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {id:"n2", type:"helper"}]; + saveProxySetting(); + process.env.HTTP_PROXY = "http://localhost:" + testProxyPort; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + restoreProxySetting(); + try { + msg.should.have.property('statusCode',200); + msg.payload.should.have.property('headers'); + msg.payload.headers.should.have.property('x-testproxy-header','foobar'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should use no_proxy', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {id:"n2", type:"helper"}]; + saveProxySetting(); + process.env.http_proxy = "http://localhost:" + testProxyPort; + process.env.no_proxy = "foo,localhost"; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + restoreProxySetting(); + try { + msg.should.have.property('statusCode',200); + msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should use NO_PROXY', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, + {id:"n2", type:"helper"}]; + saveProxySetting(); + process.env.HTTP_PROXY = "http://localhost:" + testProxyPort; + process.env.NO_PROXY = "foo,localhost"; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + restoreProxySetting(); + try { + msg.should.have.property('statusCode',200); + msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); }); - n1.receive({payload:"foo"}); }); }); - it('returns plain text when JSON fails to parse', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/json-invalid')}, - {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',"{a:1"); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } + describe('authentication', function() { + it('should authenticate on server', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/authenticate')}, + {id:"n2", type:"helper"}]; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n1.credentials = {user:'userfoo', password:'passwordfoo'}; + n2.on("input", function(msg) { + try { + msg.should.have.property('statusCode',200); + msg.payload.should.have.property('user', 'userfoo'); + msg.payload.should.have.property('pass', 'passwordfoo'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should authenticate on proxy server', function(done) { + var flow = [{id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate')}, + {id:"n2", type:"helper"}]; + saveProxySetting(); + process.env.http_proxy = "http://foouser:barpassword@localhost:" + testProxyPort; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + restoreProxySetting(); + try { + msg.should.have.property('statusCode',200); + msg.payload.should.have.property('user', 'foouser'); + msg.payload.should.have.property('pass', 'barpassword'); + msg.payload.should.have.property('headers'); + msg.payload.headers.should.have.property('x-testproxy-header','foobar'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); + }); + }); + + it('should output an error when proxy authentication was failed', function(done) { + var flow = [{id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate')}, + {id:"n2", type:"helper"}]; + saveProxySetting(); + process.env.http_proxy = "http://xxxuser:barpassword@localhost:" + testProxyPort; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + restoreProxySetting(); + try { + msg.should.have.property('statusCode',407); + msg.headers.should.have.property('proxy-authenticate', 'BASIC realm="test"'); + msg.payload.should.have.property('headers'); + msg.payload.headers.should.have.property('x-testproxy-header','foobar'); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo"}); }); - n1.receive({payload:"foo"}); }); }); - - - it('return the status code', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/statusCode204')}, - {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',''); - msg.should.have.property('statusCode',204); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('allow the url to be missing the http:// prefix', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/text').substring("http://".length)}, - {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','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('reject non http:// schemes - node config', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:"ftp://foo"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var inError = false; - n2.on("input", function(msg) { - inError = true; - }); - n1.receive({payload:"foo"}); - setTimeout(function() { - if (inError) { - done(new Error("non http(s):// scheme allowed through")) - } else { - done(); - } - },20) - }); - }); - - it('reject non http:// schemes - msg.url', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var inError = false; - n2.on("input", function(msg) { - inError = true; - }); - n1.receive({payload:"foo",url:"ftp://foo"}); - setTimeout(function() { - if (inError) { - done(new Error("non http(s):// scheme allowed through")) - } else { - done(); - } - },20) - }); - }); - it('allow the message to provide the url', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt"}, - {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','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",url:getTestURL('/text')}); - }); - }); - - it('allow the message to provide the method', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"txt",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('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",method:"get"}); - }); - }); - - it('allow the url to contain mustache placeholders', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/te{{placeholder}}')}, - {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','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",placeholder:"xt"}); - }); - }); - - it('send the payload as the body of a POST as application/json', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {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'); - msg.payload.body.should.eql('{"foo":"abcde"}'); - msg.payload.headers.should.have.property('content-type').which.startWith('application/json'); - - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:"abcde"}}); - }); - }); - - it('send a payload of 0 as the body of a POST as text/plain', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {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'); - msg.payload.body.should.eql('0'); - msg.payload.headers.should.have.property('content-length','1'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:0, headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('send an Object payload as the body of a POST', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {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'); - msg.payload.body.should.eql('{"foo":"abcde"}'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:"abcde"}, headers: { 'content-type': 'text/plain'}}); - }); - }) - - it('send a Buffer as the body of a POST', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {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'); - msg.payload.body.should.eql('hello'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:new Buffer('hello'), headers: { 'content-type': 'text/plain'}}); - }); - }) - - it('send a Buffer as the body of a POST', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {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'); - msg.payload.body.should.eql('hello'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:new Buffer('hello'), headers: { 'content-type': 'text/plain'}}); - }); - }) - - it('ignores unmodified msg.headers property', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {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.payload.headers.should.have.property('content-type').which.startWith('application/json'); - msg.payload.headers.should.not.have.property('x-node-red-request-node'); - done(); - } catch(err) { - done(err); - } - }); - // Pass in a headers property with an unmodified x-node-red-request-node hash - // This should cause the node to ignore the headers - n1.receive({payload:{foo:"bar"}, headers: { 'content-type': 'text/plain', "x-node-red-request-node":"67690139"}}); - }); - }) - - it('uses modified msg.headers property', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {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.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.payload.headers.should.not.have.property('x-node-red-request-node'); - done(); - } catch(err) { - done(err); - } - }); - // Pass in a headers property with a x-node-red-request-node hash that doesn't match the contents - // This should cause the node to use the headers - n1.receive({payload:{foo:"bar"}, headers: { 'content-type': 'text/plain', "x-node-red-request-node":"INVALID_SUM"}}); - }); - }) - }); diff --git a/test/resources/ssl/server.crt b/test/resources/ssl/server.crt new file mode 100644 index 000000000..493afc252 --- /dev/null +++ b/test/resources/ssl/server.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMDCCAhgCCQDPGPyu5M6ZaDANBgkqhkiG9w0BAQsFADBZMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTgwMzE2MDY0ODU1WhgP +MjExODAyMjAwNjQ4NTVaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0 +YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMM +CWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMifGelM +k/b3HeIj98y9P5jS+Qblqpq7+gsCaL+qglMFmG0QXe6Ordkrh3xeY0uTkaFatwLM +WMzoX60nNdaVjC9U9RlQLK/3nncCveexxRUGtI8VpxN04ivBE/ULhtJeStQFrfyt +LWr1WWf8o8P/EWzZnh0Y1oHc0XqhOPHu9Nfd9kn5nfHNd/xbY8KXa4DkVSJ1lLFK +3t/nSWttchF8zKgNpoQznNGqUTjT28l0sS8fyH76DyRj3Ke6xdNxX2NRUU0PnGFI +RMsBG4Qrzo5xY7lQP7uVVgZUlxryw+NuZuC1PBXaUKJOf6CGwrTq5WB9zF1iBZCs +wD68NvtLd0kHEgECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfqNOg2v90r5x4lFo +SYmPUoX24gdwHd/mfCzDJksB8n98X1eULYZqqRF2Q7LMkYu/twxfR3EKQX1HZxQY +LpGUYX4ubJdVTy13opJs8B4NkhvRuOAP0+b7RVt4RfuxLX9tYOB98tEbf7Mj0ccq +F4sHi+PMCh64K7rNWECHar0F51yNtNXcxJPMuHZVmj0/U7h6ZxNf+GzdTi8YKmVy +5OHI7xol/II/v3QOi1L+BaEIUkqYODKuQouJVIzu4zX6JRfAaxwjJmliYoJm7OEY +dFMEQUw1Ggsos+KbkGi9mCDbveYpWcZTR8nfPwmx+oJtt47DTHUC3KSdRxgtfjGs +otmVSw== +-----END CERTIFICATE----- diff --git a/test/resources/ssl/server.key b/test/resources/ssl/server.key new file mode 100644 index 000000000..4b8d6a1a5 --- /dev/null +++ b/test/resources/ssl/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAyJ8Z6UyT9vcd4iP3zL0/mNL5BuWqmrv6CwJov6qCUwWYbRBd +7o6t2SuHfF5jS5ORoVq3AsxYzOhfrSc11pWML1T1GVAsr/eedwK957HFFQa0jxWn +E3TiK8ET9QuG0l5K1AWt/K0tavVZZ/yjw/8RbNmeHRjWgdzReqE48e701932Sfmd +8c13/FtjwpdrgORVInWUsUre3+dJa21yEXzMqA2mhDOc0apRONPbyXSxLx/IfvoP +JGPcp7rF03FfY1FRTQ+cYUhEywEbhCvOjnFjuVA/u5VWBlSXGvLD425m4LU8FdpQ +ok5/oIbCtOrlYH3MXWIFkKzAPrw2+0t3SQcSAQIDAQABAoIBAGmbryUrpZxU24tG +idRiLw9Ax8yEq7lGiMqw2vlCRdZ0VJfdDMVeoE945ZpniXeoV/oLadl0Pq6nCG56 +/JFYKfJkk51eoheDjwxxCgzkfK2j2PqVWF0ao1CLE/ljtvYYouVXlA42D3mFbCoc +SQ0MwVx+dgg1If48gp0+L17T/ll/VOOQumts5UzoKC8YABLL00g5ZL9/jZlVipgl +HfENMPWOfy3q5kSgQqvTWTMdSE6644ryV890mrwcC/RzqQBSNgRh1Lqx3jcXQSdN +x5C19gEK60hZqcvzBkKYudMHUC6I0lcuao1xwBnHUQIVKmLFPZBUIQq3tVar/YUc +d65cJpECgYEA5D9QilQpHxv875wBvBOEbyt9TqDBBN/5JpGQ9sBKpA0eNp3UzrOr ++n0TlyoDZYjkxgNJScS4TpeKde1Hk5j2kkMngjS69dn4G6wmOI79gAOGrCiJd1/I +AWb09KxUKlWBbfKuLHdl1wSMCYQornDdXxYCxhv9sMZKbEJ//tsI420CgYEA4QPf +n/dRAm+6zwNQTWOYWlj5jsG1TilBBCtoRqUqVlrAgR6rS1lgOleHkVrWH0g0Lkmh +9DxWiWuNNXxdU/5zx9AQn/JuHuL8EjDLN5r7idcg2LtEElCkr12y0I9nzS2OOZnj +MTioIh+hghzNuk09NlVJrHi48bJUVL/6Ws7ruGUCgYBT9UJAD+MscVQiI2Wz9A30 +ArBOOu2lSGnSmRsU2PjbzYN+naIJAqhRNK7/HNIxCCD3AYB05SrSpgWliUmZ7ltM +w+0FhTX8d1g/fZx1k4uGCkYAj8y5H39nnKKgWb9/7wH0Gp+c9bJ9XEvSuE1qlVOo +xWTx0JwJ6Xa4yeFhMtrbJQKBgF/FfErjwvEciRBPQsCNoWzi7eUbAYYw/OE/cHSR +HAIBQmoymYnKkrCCTMtLNFPAMaV55ZrEi7iVtFaNhlOXu8PSBSFu1/wBdHRxnC0g +o+s5S1uz6Pc6p72UTeWDBBVKTHyryQ1MJhPQDrgIdm/TLDiR+HeWMnF9C3O++lno +NGAZAoGBAKhsmatxVD9B3jvUDd/CWhXVDSZQECrfJ+Uy1q6b5NO5yMibpIZv14Nj +VT+b2qXoO1wL6htTRJXXNPmrB/JtrLiLg/vxVuA7CPSgot8SDA+/lbRhf1n/SKnD +ECXrEUmq28SgBItbY4vcy5PVEHRvlzqO/LpD6Y7iGNpR7zw9Yk3b +-----END RSA PRIVATE KEY-----