| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports.create = function (securePort, insecurePort, redirects) { | 
					
						
							| 
									
										
										
										
											2015-03-06 03:04:51 +00:00
										 |  |  |   var PromiseA = require('bluebird').Promise; | 
					
						
							|  |  |  |   var http = require('http'); | 
					
						
							|  |  |  |   var escapeRe; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |   function redirectHttps(req, res) { | 
					
						
							| 
									
										
										
										
											2015-09-25 08:06:47 +00:00
										 |  |  |     res.setHeader('Strict-Transport-Security', 'max-age=10886400; includeSubDomains; preload'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |     var insecureRedirects; | 
					
						
							|  |  |  |     var host = req.headers.host || ''; | 
					
						
							|  |  |  |     var url = req.url; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // because I have domains for which I don't want to pay for SSL certs
 | 
					
						
							|  |  |  |     insecureRedirects = redirects.sort(function (a, b) { | 
					
						
							|  |  |  |       var hlen = b.from.hostname.length - a.from.hostname.length; | 
					
						
							|  |  |  |       var plen; | 
					
						
							|  |  |  |       if (!hlen) { | 
					
						
							|  |  |  |         plen = b.from.path.length - a.from.path.length; | 
					
						
							|  |  |  |         return plen; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return hlen; | 
					
						
							|  |  |  |     }).forEach(function (redirect) { | 
					
						
							|  |  |  |       var origHost = host; | 
					
						
							| 
									
										
										
										
											2015-03-06 03:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (!escapeRe) { | 
					
						
							|  |  |  |         escapeRe = require('escape-string-regexp'); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |       // TODO if '*' === hostname[0], omit '^'
 | 
					
						
							|  |  |  |       host = host.replace( | 
					
						
							|  |  |  |         new RegExp('^' + escapeRe(redirect.from.hostname)) | 
					
						
							|  |  |  |       , redirect.to.hostname | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       if (host === origHost) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       url = url.replace( | 
					
						
							|  |  |  |         new RegExp('^' + escapeRe(redirect.from.path)) | 
					
						
							|  |  |  |       , redirect.to.path | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-14 21:44:02 +00:00
										 |  |  |     var escapeHtml = require('escape-html'); | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |     var newLocation = 'https://' | 
					
						
							|  |  |  |       + host.replace(/:\d+/, ':' + securePort) + url | 
					
						
							|  |  |  |       ; | 
					
						
							| 
									
										
										
										
											2015-07-08 21:20:57 -06:00
										 |  |  |     var safeLocation = escapeHtml(newLocation); | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     var metaRedirect = '' | 
					
						
							|  |  |  |       + '<html>\n' | 
					
						
							|  |  |  |       + '<head>\n' | 
					
						
							|  |  |  |       + '  <style>* { background-color: white; color: white; text-decoration: none; }</style>\n' | 
					
						
							| 
									
										
										
										
											2015-07-08 21:20:57 -06:00
										 |  |  |       + '  <META http-equiv="refresh" content="0;URL=' + safeLocation + '">\n' | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |       + '</head>\n' | 
					
						
							|  |  |  |       + '<body style="display: none;">\n' | 
					
						
							|  |  |  |       + '  <p>You requested an insecure resource. Please use this instead: \n' | 
					
						
							| 
									
										
										
										
											2015-07-08 21:20:57 -06:00
										 |  |  |       + '    <a href="' + safeLocation + '">' + safeLocation + '</a></p>\n' | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |       + '</body>\n' | 
					
						
							|  |  |  |       + '</html>\n' | 
					
						
							|  |  |  |       ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // DO NOT HTTP REDIRECT
 | 
					
						
							|  |  |  |     /* | 
					
						
							|  |  |  |     res.setHeader('Location', newLocation); | 
					
						
							|  |  |  |     res.statusCode = 302; | 
					
						
							|  |  |  |     */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // BAD NEWS BEARS
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // When people are experimenting with the API and posting tutorials
 | 
					
						
							|  |  |  |     // they'll use cURL and they'll forget to prefix with https://
 | 
					
						
							|  |  |  |     // If we allow that, then many users will be sending private tokens
 | 
					
						
							|  |  |  |     // and such with POSTs in clear text and, worse, it will work!
 | 
					
						
							|  |  |  |     // To minimize this, we give browser users a mostly optimal experience,
 | 
					
						
							|  |  |  |     // but people experimenting with the API get a message letting them know
 | 
					
						
							|  |  |  |     // that they're doing it wrong and thus forces them to ensure they encrypt.
 | 
					
						
							| 
									
										
										
										
											2015-07-08 21:20:57 -06:00
										 |  |  |     res.setHeader('Content-Type', 'text/html; charset=utf-8'); | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |     res.end(metaRedirect); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // TODO localhost-only server shutdown mechanism
 | 
					
						
							|  |  |  |   // that closes all sockets, waits for them to finish,
 | 
					
						
							|  |  |  |   // and then hands control over completely to respawned server
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //
 | 
					
						
							| 
									
										
										
										
											2015-02-20 22:34:17 +00:00
										 |  |  |   // Redirect HTTP to HTTPS
 | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |   //
 | 
					
						
							|  |  |  |   // This simply redirects from the current insecure location to the encrypted location
 | 
					
						
							|  |  |  |   //
 | 
					
						
							| 
									
										
										
										
											2015-02-20 22:34:17 +00:00
										 |  |  |   var insecureServer; | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |   insecureServer = http.createServer(); | 
					
						
							|  |  |  |   insecureServer.on('request', redirectHttps); | 
					
						
							| 
									
										
										
										
											2015-02-20 22:34:17 +00:00
										 |  |  |   insecureServer.listen(insecurePort, function () { | 
					
						
							| 
									
										
										
										
											2015-03-06 03:04:51 +00:00
										 |  |  |     console.log("\nListening on https://localhost:" + insecureServer.address().port); | 
					
						
							|  |  |  |     console.log("(redirecting all traffic to https)\n"); | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2015-03-06 03:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return PromiseA.resolve(insecureServer); | 
					
						
							| 
									
										
										
										
											2015-02-18 23:06:56 -07:00
										 |  |  | }; |