Plato on Github
Report Home
request/tests/test-tunnel.js
Maintainability
79.64
Lines of code
473
Difficulty
38.83
Estimated Errors
3.38
Function weight
By Complexity
By SLOC
'use strict' var server = require('./server') , tape = require('tape') , request = require('../index') , https = require('https') , net = require('net') , fs = require('fs') , path = require('path') , util = require('util') , url = require('url') , destroyable = require('server-destroy') var events = [] , caFile = path.resolve(__dirname, 'ssl/ca/ca.crt') , ca = fs.readFileSync(caFile) , clientCert = fs.readFileSync(path.resolve(__dirname, 'ssl/ca/client.crt')) , clientKey = fs.readFileSync(path.resolve(__dirname, 'ssl/ca/client-enc.key')) , clientPassword = 'password' , sslOpts = { key : path.resolve(__dirname, 'ssl/ca/localhost.key'), cert : path.resolve(__dirname, 'ssl/ca/localhost.crt') } , mutualSSLOpts = { key : path.resolve(__dirname, 'ssl/ca/localhost.key'), cert : path.resolve(__dirname, 'ssl/ca/localhost.crt'), ca : caFile, requestCert : true, rejectUnauthorized : true } // this is needed for 'https over http, tunnel=false' test // from https://github.com/coolaj86/node-ssl-root-cas/blob/v1.1.9-beta/ssl-root-cas.js#L4267-L4281 var httpsOpts = https.globalAgent.options httpsOpts.ca = httpsOpts.ca || [] httpsOpts.ca.push(ca) var s = server.createServer() , ss = server.createSSLServer(null, sslOpts) , ss2 = server.createSSLServer(ss.port + 1, mutualSSLOpts) // XXX when tunneling https over https, connections get left open so the server // doesn't want to close normally (and same issue with http server on v0.8.x) destroyable(s) destroyable(ss) destroyable(ss2) function event() { events.push(util.format.apply(null, arguments)) } function setListeners(server, type) { server.on('/', function(req, res) { event('%s response', type) res.end(type + ' ok') }) server.on('request', function(req, res) { if (/^https?:/.test(req.url)) { // This is a proxy request var dest = req.url.split(':')[0] // Is it a redirect? var match = req.url.match(/\/redirect\/(https?)$/) if (match) { dest += '->' + match[1] } event('%s proxy to %s', type, dest) request(req.url, { followRedirect : false }).pipe(res) } }) server.on('/redirect/http', function(req, res) { event('%s redirect to http', type) res.writeHead(301, { location : s.url }) res.end() }) server.on('/redirect/https', function(req, res) { event('%s redirect to https', type) res.writeHead(301, { location : ss.url }) res.end() }) server.on('connect', function(req, client, head) { var u = url.parse(req.url) var server = net.connect(u.host, u.port, function() { event('%s connect to %s', type, req.url) client.write('HTTP/1.1 200 Connection established\r\n\r\n') client.pipe(server) server.write(head) server.pipe(client) }) }) } setListeners(s, 'http') setListeners(ss, 'https') setListeners(ss2, 'https') tape('setup', function(t) { s.listen(s.port, function() { ss.listen(ss.port, function() { ss2.listen(ss2.port, 'localhost', function() { t.end() }) }) }) }) // monkey-patch since you can't set a custom certificate authority for the // proxy in tunnel-agent (this is necessary for "* over https" tests) var customCaCount = 0 var httpsRequestOld = https.request https.request = function(options) { if (customCaCount) { options.ca = ca customCaCount-- } return httpsRequestOld.apply(this, arguments) } function runTest(name, opts, expected) { tape(name, function(t) { opts.ca = ca if (opts.proxy === ss.url) { customCaCount = (opts.url === ss.url ? 2 : 1) } request(opts, function(err, res, body) { event(err ? 'err ' + err.message : res.statusCode + ' ' + body) t.deepEqual(events, expected) events = [] t.end() }) }) } // HTTP OVER HTTP runTest('http over http, tunnel=true', { url : s.url, proxy : s.url, tunnel : true }, [ 'http connect to localhost:' + s.port, 'http response', '200 http ok' ]) runTest('http over http, tunnel=false', { url : s.url, proxy : s.url, tunnel : false }, [ 'http proxy to http', 'http response', '200 http ok' ]) runTest('http over http, tunnel=default', { url : s.url, proxy : s.url }, [ 'http proxy to http', 'http response', '200 http ok' ]) // HTTP OVER HTTPS runTest('http over https, tunnel=true', { url : s.url, proxy : ss.url, tunnel : true }, [ 'https connect to localhost:' + s.port, 'http response', '200 http ok' ]) runTest('http over https, tunnel=false', { url : s.url, proxy : ss.url, tunnel : false }, [ 'https proxy to http', 'http response', '200 http ok' ]) runTest('http over https, tunnel=default', { url : s.url, proxy : ss.url }, [ 'https proxy to http', 'http response', '200 http ok' ]) // HTTPS OVER HTTP runTest('https over http, tunnel=true', { url : ss.url, proxy : s.url, tunnel : true }, [ 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) runTest('https over http, tunnel=false', { url : ss.url, proxy : s.url, tunnel : false }, [ 'http proxy to https', 'https response', '200 https ok' ]) runTest('https over http, tunnel=default', { url : ss.url, proxy : s.url }, [ 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) // HTTPS OVER HTTPS runTest('https over https, tunnel=true', { url : ss.url, proxy : ss.url, tunnel : true }, [ 'https connect to localhost:' + ss.port, 'https response', '200 https ok' ]) runTest('https over https, tunnel=false', { url : ss.url, proxy : ss.url, tunnel : false, pool : false // must disable pooling here or Node.js hangs }, [ 'https proxy to https', 'https response', '200 https ok' ]) runTest('https over https, tunnel=default', { url : ss.url, proxy : ss.url }, [ 'https connect to localhost:' + ss.port, 'https response', '200 https ok' ]) // HTTP->HTTP OVER HTTP runTest('http->http over http, tunnel=true', { url : s.url + '/redirect/http', proxy : s.url, tunnel : true }, [ 'http connect to localhost:' + s.port, 'http redirect to http', 'http connect to localhost:' + s.port, 'http response', '200 http ok' ]) runTest('http->http over http, tunnel=false', { url : s.url + '/redirect/http', proxy : s.url, tunnel : false }, [ 'http proxy to http->http', 'http redirect to http', 'http proxy to http', 'http response', '200 http ok' ]) runTest('http->http over http, tunnel=default', { url : s.url + '/redirect/http', proxy : s.url }, [ 'http proxy to http->http', 'http redirect to http', 'http proxy to http', 'http response', '200 http ok' ]) // HTTP->HTTPS OVER HTTP runTest('http->https over http, tunnel=true', { url : s.url + '/redirect/https', proxy : s.url, tunnel : true }, [ 'http connect to localhost:' + s.port, 'http redirect to https', 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) runTest('http->https over http, tunnel=false', { url : s.url + '/redirect/https', proxy : s.url, tunnel : false }, [ 'http proxy to http->https', 'http redirect to https', 'http proxy to https', 'https response', '200 https ok' ]) runTest('http->https over http, tunnel=default', { url : s.url + '/redirect/https', proxy : s.url }, [ 'http proxy to http->https', 'http redirect to https', 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) // HTTPS->HTTP OVER HTTP runTest('https->http over http, tunnel=true', { url : ss.url + '/redirect/http', proxy : s.url, tunnel : true }, [ 'http connect to localhost:' + ss.port, 'https redirect to http', 'http connect to localhost:' + s.port, 'http response', '200 http ok' ]) runTest('https->http over http, tunnel=false', { url : ss.url + '/redirect/http', proxy : s.url, tunnel : false }, [ 'http proxy to https->http', 'https redirect to http', 'http proxy to http', 'http response', '200 http ok' ]) runTest('https->http over http, tunnel=default', { url : ss.url + '/redirect/http', proxy : s.url }, [ 'http connect to localhost:' + ss.port, 'https redirect to http', 'http connect to localhost:' + s.port, 'http response', '200 http ok' ]) // HTTPS->HTTPS OVER HTTP runTest('https->https over http, tunnel=true', { url : ss.url + '/redirect/https', proxy : s.url, tunnel : true }, [ 'http connect to localhost:' + ss.port, 'https redirect to https', 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) runTest('https->https over http, tunnel=false', { url : ss.url + '/redirect/https', proxy : s.url, tunnel : false }, [ 'http proxy to https->https', 'https redirect to https', 'http proxy to https', 'https response', '200 https ok' ]) runTest('https->https over http, tunnel=default', { url : ss.url + '/redirect/https', proxy : s.url }, [ 'http connect to localhost:' + ss.port, 'https redirect to https', 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) // MUTUAL HTTPS OVER HTTP runTest('mutual https over http, tunnel=true', { url : ss2.url, proxy : s.url, tunnel : true, cert : clientCert, key : clientKey, passphrase : clientPassword }, [ 'http connect to localhost:' + ss2.port, 'https response', '200 https ok' ]) // XXX causes 'Error: socket hang up' // runTest('mutual https over http, tunnel=false', { // url : ss2.url, // proxy : s.url, // tunnel : false, // cert : clientCert, // key : clientKey, // passphrase : clientPassword // }, [ // 'http connect to localhost:' + ss2.port, // 'https response', // '200 https ok' // ]) runTest('mutual https over http, tunnel=default', { url : ss2.url, proxy : s.url, cert : clientCert, key : clientKey, passphrase : clientPassword }, [ 'http connect to localhost:' + ss2.port, 'https response', '200 https ok' ]) tape('cleanup', function(t) { s.destroy(function() { ss.destroy(function() { ss2.destroy(function() { t.end() }) }) }) })