forked from coolaj86/walnut.js
		
	
		
			
	
	
		
			125 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			125 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | 'use strict'; | ||
|  | 
 | ||
|  | var https = require('https'); | ||
|  | var path = require('path'); | ||
|  | var fs = require('fs'); | ||
|  | var PromiseA = global.Promise || require('bluebird').Promise; | ||
|  | 
 | ||
|  | exports.create = function (ip, localPort, externalPort) { | ||
|  |   return new PromiseA(function (resolve, reject) { | ||
|  |     var token = Math.random().toString(16).split('.')[1]; | ||
|  |     var tokenPath = Math.random().toString(16).split('.')[1]; | ||
|  |     var options; | ||
|  |     var server; | ||
|  |     var options; | ||
|  |     var certsPath = path.join(__dirname, 'certs', 'server'); | ||
|  |     var caCertsPath = path.join(__dirname, 'certs', 'ca'); | ||
|  | 
 | ||
|  | 
 | ||
|  |     function testConnection() { | ||
|  |       var awesome = false; | ||
|  |       var timetok; | ||
|  |       var webreq; | ||
|  |       var options = { | ||
|  |         // not hostname because we set headers.host on our own
 | ||
|  |         host: ip | ||
|  |       , headers: { | ||
|  |           // whatever's on the fake cert
 | ||
|  |           'Host': 'redirect-www.org' | ||
|  |         } | ||
|  |       , port: externalPort | ||
|  |       , path: '/' + tokenPath | ||
|  |       , ca: fs.readFileSync(path.join(caCertsPath, 'my-root-ca.crt.pem')) | ||
|  |       }; | ||
|  |       options.agent = new https.Agent(options); | ||
|  | 
 | ||
|  |       timetok = setTimeout(function () { | ||
|  |         reject(new Error("timed out while testing NAT loopback for port " + externalPort)); | ||
|  |       }, 2000); | ||
|  | 
 | ||
|  |       function finishHim(err) { | ||
|  |         clearTimeout(timetok); | ||
|  |         server.close(function () { | ||
|  |           if (!err && awesome) { | ||
|  |             resolve(); | ||
|  |           } | ||
|  |         }); | ||
|  | 
 | ||
|  |         if (err || !awesome) { | ||
|  |           if (err) { | ||
|  |             reject(err); | ||
|  |           } | ||
|  |           else if (!awesome) { | ||
|  |             reject(new Error("loopback failed. Why? here's my best guess: " | ||
|  |               + "the ssl cert matched, so you've probably got two boxes and this isn't the right one")); | ||
|  |           } | ||
|  |           return; | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       webreq = https.request(options, function(res) { | ||
|  |         res.on('data', function (resToken) { | ||
|  |           if (resToken.toString() === token) { | ||
|  |             awesome = true; | ||
|  |             return; | ||
|  |           } | ||
|  |         }); | ||
|  |         res.on('error', function (err) { | ||
|  |           console.error('[ERROR] https.request.response'); | ||
|  |           console.error(err); | ||
|  |           finishHim(new Error("loopback failed. Why? here's my best guess: " | ||
|  |             + "the connection was interrupted")); | ||
|  |           }); | ||
|  |         res.on('end', function () { | ||
|  |           finishHim(); | ||
|  |         }); | ||
|  |       }); | ||
|  | 
 | ||
|  |       webreq.on('error', function (err) { | ||
|  |         console.error('[ERROR] https.request'); | ||
|  |         console.error(err); | ||
|  |         if (/ssl|cert|chain/i.test(err.message || err.toString())) { | ||
|  |           finishHim(new Error("loopback failed. Why? here's my best guess: " | ||
|  |             + "the ssl cert validation may have failed (might port-forward to the wrong box)")); | ||
|  |         } else { | ||
|  |           finishHim(new Error("loopback failed. Why? here's my best guess: " | ||
|  |             + "port forwarding isn't configured for " + ip + ":" + externalPort + " to " + localPort)); | ||
|  |         } | ||
|  |       }); | ||
|  |       webreq.end(); | ||
|  |     } | ||
|  | 
 | ||
|  |     //
 | ||
|  |     // SSL Certificates
 | ||
|  |     //
 | ||
|  |     options = { | ||
|  |       key: fs.readFileSync(path.join(certsPath, 'my-server.key.pem')) | ||
|  |     , ca: [ fs.readFileSync(path.join(caCertsPath, 'my-root-ca.crt.pem')) ] | ||
|  |     , cert: fs.readFileSync(path.join(certsPath, 'my-server.crt.pem')) | ||
|  |     , requestCert: false | ||
|  |     , rejectUnauthorized: false | ||
|  |     }; | ||
|  | 
 | ||
|  |     //
 | ||
|  |     // Serve an Express App securely with HTTPS
 | ||
|  |     //
 | ||
|  |     server = https.createServer(options); | ||
|  |     function listen(app) { | ||
|  |       server.on('request', app); | ||
|  |       server.listen(localPort, function () { | ||
|  |         localPort = server.address().port; | ||
|  |         setTimeout(testConnection, 2000); | ||
|  |       }); | ||
|  |     } | ||
|  | 
 | ||
|  |     listen(function (req, res) { | ||
|  |       if (('/' + tokenPath) === req.url) { | ||
|  |         res.end(token); | ||
|  |         return; | ||
|  |       } | ||
|  | 
 | ||
|  |       res.end('loopback failure'); | ||
|  |     }); | ||
|  |   }); | ||
|  | }; |