Compare commits
	
		
			8 Commits
		
	
	
		
			commercial
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5a395a299a | |||
|  | eb36af8269 | ||
| 50c0449206 | |||
| d48707d265 | |||
| 60f85144a9 | |||
| 5dfe25ed95 | |||
| c9d6b46f0f | |||
| 0a67728239 | 
							
								
								
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -43,3 +43,12 @@ jspm_packages | ||||
| 
 | ||||
| # Optional REPL history | ||||
| .node_repl_history | ||||
| 
 | ||||
| # Snapcraft | ||||
| /parts/ | ||||
| /prime/ | ||||
| /stage/ | ||||
| .snapcraft | ||||
| *.snap | ||||
| *.tar.bz2 | ||||
| 
 | ||||
|  | ||||
| @ -47,7 +47,13 @@ function applyConfig(config) { | ||||
|   } else { | ||||
|     state.Promise = require('bluebird'); | ||||
|   } | ||||
|   state.tlsOptions = {}; // TODO just close the sockets that would use this early? or use the admin servername
 | ||||
|   state.tlsOptions = { | ||||
|     // Handles disconnected devices
 | ||||
|     // TODO allow user to opt-in to wildcard hosting for a better error page?
 | ||||
|     SNICallback: function (servername, cb) { | ||||
|       return state.greenlock.tlsOptions.SNICallback(state.config.webminDomain || state.servernames[0], cb); | ||||
|     } | ||||
|   }; // TODO just close the sockets that would use this early? or use the admin servername
 | ||||
|   state.config = config; | ||||
|   state.servernames = config.servernames || []; | ||||
|   state.secret = state.config.secret; | ||||
|  | ||||
							
								
								
									
										35
									
								
								lib/ago-test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/ago-test.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var timeago = require('./ago.js').AGO; | ||||
| 
 | ||||
| function test() { | ||||
|   [ 1.5 * 1000 // a moment ago
 | ||||
|   , 4.5 * 1000 // moments ago
 | ||||
|   , 10  * 1000 // 10 seconds ago
 | ||||
|   , 59  * 1000 // a minute ago
 | ||||
|   , 60  * 1000 // a minute ago
 | ||||
|   , 61  * 1000 // a minute ago
 | ||||
|   , 119  * 1000 // a minute ago
 | ||||
|   , 120  * 1000 // 2 minutes ago
 | ||||
|   , 121 * 1000 // 2 minutes ago
 | ||||
|   , (60 * 60 * 1000) - 1000 // 59 minutes ago
 | ||||
|   , 1 * 60 * 60 * 1000 // an hour ago
 | ||||
|   , 1.5 * 60 * 60 * 1000 // an hour ago
 | ||||
|   , 2.5 * 60 * 60 * 1000 // 2 hours ago
 | ||||
|   , 1.5 * 24 * 60 * 60 * 1000 // a day ago
 | ||||
|   , 2.5 * 24 * 60 * 60 * 1000 // 2 days ago
 | ||||
|   , 7 * 24 * 60 * 60 * 1000 // a week ago
 | ||||
|   , 14 * 24 * 60 * 60 * 1000 // 2 weeks ago
 | ||||
|   , 27 * 24 * 60 * 60 * 1000 // 3 weeks ago
 | ||||
|   , 28 * 24 * 60 * 60 * 1000 // 4 weeks ago
 | ||||
|   , 29 * 24 * 60 * 60 * 1000 // 4 weeks ago
 | ||||
|   , 1.5 * 30 * 24 * 60 * 60 * 1000 // a month ago
 | ||||
|   , 2.5 * 30 * 24 * 60 * 60 * 1000 // 2 months ago
 | ||||
|   , (12 * 30 * 24 * 60 * 60 * 1000) + 1000 // 12 months ago
 | ||||
|   , 13 * 30 * 24 * 60 * 60 * 1000 // over a year ago
 | ||||
|   ].forEach(function (d) { | ||||
|     console.log(d, '=', timeago(d)); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| test(); | ||||
							
								
								
									
										50
									
								
								lib/ago.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								lib/ago.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| ;(function (exports) { | ||||
| 'use strict'; | ||||
| 
 | ||||
| exports.AGO = function timeago(ms) { | ||||
|   var ago = Math.floor(ms / 1000); | ||||
|   var part = 0; | ||||
| 
 | ||||
|   if (ago < 2) { return "a moment ago"; } | ||||
|   if (ago < 5) { return "moments ago"; } | ||||
|   if (ago < 60) { return ago + " seconds ago"; } | ||||
| 
 | ||||
|   if (ago < 120) { return "a minute ago"; } | ||||
|   if (ago < 3600) { | ||||
|     while (ago >= 60) { ago -= 60; part += 1; } | ||||
|     return part + " minutes ago"; | ||||
|   } | ||||
| 
 | ||||
|   if (ago < 7200) { return "an hour ago"; } | ||||
|   if (ago < 86400) { | ||||
|     while (ago >= 3600) { ago -= 3600; part += 1; } | ||||
|     return part + " hours ago"; | ||||
|   } | ||||
| 
 | ||||
|   if (ago < 172800) { return "a day ago"; } | ||||
|   if (ago < 604800) { | ||||
|     while (ago >= 172800) { ago -= 172800; part += 1; } | ||||
|     return part + " days ago"; | ||||
|   } | ||||
| 
 | ||||
|   if (ago < 1209600) { return "a week ago"; } | ||||
|   if (ago < 2592000) { | ||||
|     while (ago >= 604800) { ago -= 604800; part += 1; } | ||||
|     return part + " weeks ago"; | ||||
|   } | ||||
| 
 | ||||
|   if (ago < 5184000) { return "a month ago"; } | ||||
|   if (ago < 31536001) { | ||||
|     while (ago >= 2592000) { ago -= 2592000; part += 1; } | ||||
|     return part + " months ago"; | ||||
|   } | ||||
| 
 | ||||
|   if (ago < 315360000) { // 10 years
 | ||||
|     return "more than year ago"; | ||||
|   } | ||||
| 
 | ||||
|   // TODO never
 | ||||
|   return ""; | ||||
| }; | ||||
| 
 | ||||
| }('undefined' !== typeof module ? module.exports : window)); | ||||
| @ -1,6 +1,7 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var Devices = module.exports; | ||||
| // TODO enumerate store's keys and device's keys for documentation
 | ||||
| Devices.addPort = function (store, serverport, newDevice) { | ||||
|   // TODO make special
 | ||||
|   return Devices.add(store, serverport, newDevice, true); | ||||
| @ -14,6 +15,7 @@ Devices.add = function (store, servername, newDevice, isPort) { | ||||
|   if (!store._domains) { store._domains = {}; } | ||||
|   if (!store._domains[servername]) { store._domains[servername] = []; } | ||||
|   store._domains[servername].push(newDevice); | ||||
|   Devices.touch(store, servername); | ||||
| 
 | ||||
|   // add device
 | ||||
|   // TODO only use a device id 
 | ||||
| @ -126,7 +128,11 @@ Devices.active = function (store, id) { | ||||
| }; | ||||
| */ | ||||
| Devices.exist = function (store, servername) { | ||||
|   return !!(Devices.list(store, servername).length); | ||||
|   if (Devices.list(store, servername).length) { | ||||
|     Devices.touch(store, servername); | ||||
|     return true; | ||||
|   } | ||||
|   return false; | ||||
| }; | ||||
| Devices.next = function (store, servername) { | ||||
|   var devices = Devices.list(store, servername); | ||||
| @ -138,5 +144,20 @@ Devices.next = function (store, servername) { | ||||
|   device = devices[devices._index || 0]; | ||||
|   devices._index = (devices._index || 0) + 1; | ||||
| 
 | ||||
|   if (device) { Devices.touch(store, servername); } | ||||
|   return device; | ||||
| }; | ||||
| Devices.touchDevice = function (store, device) { | ||||
|   // TODO use device.id (which will be pubkey thumbprint) and store._devices[id].domainsMap
 | ||||
|   Object.keys(device.domainsMap).forEach(function (servername) { | ||||
|     Devices.touch(store, servername); | ||||
|   }); | ||||
| }; | ||||
| Devices.touch = function (store, servername) { | ||||
|   if (!store._recency) { store._recency = {}; } | ||||
|   store._recency[servername] = Date.now(); | ||||
| }; | ||||
| Devices.lastSeen = function (store, servername) { | ||||
|   if (!store._recency) { store._recency = {}; } | ||||
|   return store._recency[servername] || 0; | ||||
| }; | ||||
|  | ||||
| @ -10,7 +10,7 @@ function noSniCallback(tag) { | ||||
|     var err = new Error("[noSniCallback] no handler set for '" + tag + "':'" + servername + "'"); | ||||
|     console.error(err.message); | ||||
|     cb(new Error(err)); | ||||
|   } | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| module.exports.create = function (state) { | ||||
| @ -72,20 +72,11 @@ module.exports.create = function (state) { | ||||
|   state.tlsInvalidSniServer.on('tlsClientError', function () { | ||||
|     console.error('tlsClientError InvalidSniServer'); | ||||
|   }); | ||||
|   state.httpsInvalid = function (servername, socket) { | ||||
|     // none of these methods work:
 | ||||
|     // httpsServer.emit('connection', socket);  // this didn't work
 | ||||
|     // tlsServer.emit('connection', socket);    // this didn't work either
 | ||||
|     //console.log('chunkLen', firstChunk.byteLength);
 | ||||
| 
 | ||||
|     console.log('[httpsInvalid] servername', servername); | ||||
|     //state.tlsInvalidSniServer.emit('connection', wrapSocket(socket));
 | ||||
|     var tlsInvalidSniServer = tls.createServer(state.tlsOptions, function (tlsSocket) { | ||||
|       console.log('[tlsInvalid] tls connection'); | ||||
|       // things get a little messed up here
 | ||||
|       var httpInvalidSniServer = http.createServer(function (req, res) { | ||||
|         if (!servername) { | ||||
|   state.createHttpInvalid = function (opts) { | ||||
|     return http.createServer(function (req, res) { | ||||
|       if (!opts.servername) { | ||||
|         res.statusCode = 422; | ||||
|         res.setHeader('Content-Type', 'text/plain; charset=utf-8'); | ||||
|         res.end( | ||||
|           "3. An inexplicable temporal shift of the quantum realm... that makes me feel uncomfortable.\n\n" | ||||
|         + "[ERROR] No SNI header was sent. I can only think of two possible explanations for this:\n" | ||||
| @ -95,15 +86,30 @@ module.exports.create = function (state) { | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       // TODO use req.headers.host instead of servername (since domain fronting is disabled anyway)
 | ||||
|       res.statusCode = 502; | ||||
|       res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||
|       res.end( | ||||
|           "You came in hot looking for '" + servername + "' and, granted, the IP address for that domain" | ||||
|         + " must be pointing here (or else how could you be here?), nevertheless either it's not registered" | ||||
|         + " in the internal system at all (which Seth says isn't even a thing) or there is no device" | ||||
|         + " connected on the south side of the network which has informed me that it's ready to have traffic" | ||||
|         + " for that domain forwarded to it (sorry I didn't check that deeply to determine which).\n\n" | ||||
|         + "Either way, you're doing strange things that make me feel uncomfortable... Please don't touch me there any more."); | ||||
|         "<h1>Oops!</h1>" | ||||
|       + "<p>It looks like '" + encodeURIComponent(opts.servername) + "' isn't connected right now.</p>" | ||||
|       + "<p><small>Last seen: " + opts.ago + "</small></p>" | ||||
|       + "<p><small>Error: 502 Bad Gateway</small></p>" | ||||
|       ); | ||||
|     }); | ||||
|       httpInvalidSniServer.emit('connection', tlsSocket); | ||||
|   }; | ||||
|   state.httpsInvalid = function (opts, socket) { | ||||
|     // none of these methods work:
 | ||||
|     // httpsServer.emit('connection', socket);  // this didn't work
 | ||||
|     // tlsServer.emit('connection', socket);    // this didn't work either
 | ||||
|     //console.log('chunkLen', firstChunk.byteLength);
 | ||||
| 
 | ||||
|     console.log('[httpsInvalid] servername', opts.servername); | ||||
|     //state.tlsInvalidSniServer.emit('connection', wrapSocket(socket));
 | ||||
|     var tlsInvalidSniServer = tls.createServer(state.tlsOptions, function (tlsSocket) { | ||||
|       console.log('[tlsInvalid] tls connection'); | ||||
|       // We create an entire http server object because it's difficult to figure out
 | ||||
|       // how to access the original tlsSocket to get the servername
 | ||||
|       state.createHttpInvalid(opts).emit('connection', tlsSocket); | ||||
|     }); | ||||
|     tlsInvalidSniServer.on('tlsClientError', function () { | ||||
|       console.error('tlsClientError InvalidSniServer httpsInvalid'); | ||||
|  | ||||
| @ -172,6 +172,7 @@ var Server = { | ||||
| , _initSocketHandlers: function (state, srv) { | ||||
|     function refreshTimeout() { | ||||
|       srv.lastActivity = Date.now(); | ||||
|       Devices.touchDevice(state.deviceLists, srv); | ||||
|     } | ||||
| 
 | ||||
|     function checkTimeout() { | ||||
|  | ||||
| @ -2,6 +2,16 @@ | ||||
| 
 | ||||
| var sni = require('sni'); | ||||
| var pipeWs = require('./pipe-ws.js'); | ||||
| var ago = require('./ago.js').AGO; | ||||
| var up = Date.now(); | ||||
| 
 | ||||
| function fromUptime(ms) { | ||||
|   if (ms) { | ||||
|     return ago(Date.now() - ms); | ||||
|   } else { | ||||
|     return "Not seen since relay restarted, " + ago(Date.now() - up); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| module.exports.createTcpConnectionHandler = function (state) { | ||||
|   var Devices = state.Devices; | ||||
| @ -27,6 +37,16 @@ module.exports.createTcpConnectionHandler = function (state) { | ||||
|       var str; | ||||
|       var m; | ||||
| 
 | ||||
|       if (!firstChunk) { | ||||
|         try { | ||||
|           conn.end(); | ||||
|         } catch(e) { | ||||
|           console.error("[lib/unwrap-tls.js] Error:", e); | ||||
|           conn.destroy(); | ||||
|         } | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       //conn.pause();
 | ||||
|       conn.unshift(firstChunk); | ||||
| 
 | ||||
| @ -38,8 +58,15 @@ module.exports.createTcpConnectionHandler = function (state) { | ||||
| 
 | ||||
|       // defer after return (instead of being in many places)
 | ||||
|       function deferData(fn) { | ||||
|         if (fn) { | ||||
|         if ('httpsInvalid' === fn) { | ||||
|           state[fn]({ | ||||
|             servername: servername | ||||
|           , ago: fromUptime(Devices.lastSeen(state.deviceLists, servername)) | ||||
|           }, conn); | ||||
|         } else if (fn) { | ||||
|           state[fn](servername, conn); | ||||
|         } else { | ||||
|           console.error("[SANITY ERROR] '" + fn + "' doesn't have a state handler"); | ||||
|         } | ||||
|         /* | ||||
|         process.nextTick(function () { | ||||
| @ -48,32 +75,80 @@ module.exports.createTcpConnectionHandler = function (state) { | ||||
|         */ | ||||
|       } | ||||
| 
 | ||||
|       function tryTls() { | ||||
|         var vhost; | ||||
|       var httpOutcomes = { | ||||
|         missingServername: function () { | ||||
|           console.log("[debug] [http] missing servername"); | ||||
|           // TODO use a more specific error page
 | ||||
|           deferData('handleInsecureHttp'); | ||||
|         } | ||||
|       , requiresSetup: function () { | ||||
|           console.log("[debug] [http] requires setup"); | ||||
|           // TODO Insecure connections for setup will not work on secure domains (i.e. .app)
 | ||||
|           state.httpSetupServer.emit('connection', conn); | ||||
|         } | ||||
|       , isInternal: function () { | ||||
|           console.log("[debug] [http] is known internally (admin)"); | ||||
|           if (/well-known/.test(str)) { | ||||
|             deferData('handleHttp'); | ||||
|           } else { | ||||
|             deferData('handleInsecureHttp'); | ||||
|           } | ||||
|         } | ||||
|       , isVhost: function () { | ||||
|           console.log("[debug] [http] is vhost (normal server)"); | ||||
|           if (/well-known/.test(str)) { | ||||
|             deferData('handleHttp'); | ||||
|           } else { | ||||
|             deferData('handleInsecureHttp'); | ||||
|           } | ||||
|         } | ||||
|       , assumeExternal: function () { | ||||
|           console.log("[debug] [http] assume external"); | ||||
|           var service = 'http'; | ||||
| 
 | ||||
|         if (!state.servernames.length) { | ||||
|           console.info("[Setup] https => admin => setup => (needs bogus tls certs to start?)"); | ||||
|           deferData('httpsSetupServer'); | ||||
|           if (!Devices.exist(state.deviceLists, servername)) { | ||||
|             // It would be better to just re-read the host header rather
 | ||||
|             // than creating a whole server object, but this is a "rare"
 | ||||
|             // case and I'm feeling lazy right now.
 | ||||
|             console.log("[debug] [http] no device connected"); | ||||
|             state.createHttpInvalid({ | ||||
|               servername: servername | ||||
|             , ago: fromUptime(Devices.lastSeen(state.deviceLists, servername)) | ||||
|             }).emit('connection', conn); | ||||
|             return; | ||||
|           } | ||||
| 
 | ||||
|         if (-1 !== state.servernames.indexOf(servername)) { | ||||
|           if (state.debug) { console.log("[Admin]", servername); } | ||||
|           deferData('httpsTunnel'); | ||||
|           // TODO make https redirect configurable on a per-domain basis
 | ||||
|           // /^\/\.well-known\/acme-challenge\//.test(str)
 | ||||
|           if (/well-known/.test(str)) { | ||||
|             // HTTP
 | ||||
|             console.log("[debug] [http] passthru"); | ||||
|             pipeWs(servername, service, Devices.next(state.deviceLists, servername), conn, serviceport); | ||||
|             return; | ||||
|           } else { | ||||
|             console.log("[debug] [http] redirect to https"); | ||||
|             deferData('handleInsecureHttp'); | ||||
|           } | ||||
| 
 | ||||
|         if (state.config.nowww && /^www\./i.test(servername)) { | ||||
|           console.log("TODO: use www bare redirect"); | ||||
|         } | ||||
| 
 | ||||
|         if (!servername) { | ||||
|       }; | ||||
|       var tlsOutcomes = { | ||||
|         missingServername: function () { | ||||
|           if (state.debug) { console.log("No SNI was given, so there's nothing we can do here"); } | ||||
|           deferData('httpsInvalid'); | ||||
|           return; | ||||
|         } | ||||
| 
 | ||||
|         function run() { | ||||
|       , requiresSetup: function () { | ||||
|           console.info("[Setup] https => admin => setup => (needs bogus tls certs to start?)"); | ||||
|           deferData('httpsSetupServer'); | ||||
|         } | ||||
|       , isInternal: function () { | ||||
|           if (state.debug) { console.log("[Admin]", servername); } | ||||
|           deferData('httpsTunnel'); | ||||
|         } | ||||
|       , isVhost: function (vhost) { | ||||
|           if (state.debug) { console.log("[tcp] [vhost]", state.config.vhost, "=>", vhost); } | ||||
|           deferData('httpsVhost'); | ||||
|         } | ||||
|       , assumeExternal: function () { | ||||
|          var nextDevice = Devices.next(state.deviceLists, servername); | ||||
|           if (!nextDevice) { | ||||
|             if (state.debug) { console.log("No devices match the given servername"); } | ||||
| @ -82,27 +157,33 @@ module.exports.createTcpConnectionHandler = function (state) { | ||||
|           } | ||||
| 
 | ||||
|           if (state.debug) { console.log("pipeWs(servername, service, deviceLists['" + servername + "'], socket)"); } | ||||
|           deferData(); | ||||
|           pipeWs(servername, service, nextDevice, conn, serviceport); | ||||
|         } | ||||
|       }; | ||||
| 
 | ||||
|       function handleConnection(outcomes) { | ||||
|         var vhost; | ||||
| 
 | ||||
|         // No routing information available
 | ||||
|         if (!servername) { outcomes.missingServername(); return; } | ||||
|         // Server needs to be set up
 | ||||
|         if (!state.servernames.length) { outcomes.requiresSetup(); return; } | ||||
|         // This is one of the admin domains
 | ||||
|         if (-1 !== state.servernames.indexOf(servername)) { outcomes.isInternal(); return; } | ||||
| 
 | ||||
|         // TODO don't run an fs check if we already know this is working elsewhere
 | ||||
|         //if (!state.validHosts) { state.validHosts = {}; }
 | ||||
|         if (state.config.vhost) { | ||||
|           vhost = state.config.vhost.replace(/:hostname/, (servername||'reallydoesntexist')); | ||||
|           if (state.debug) { console.log("[tcp] [vhost]", state.config.vhost, "=>", vhost); } | ||||
|           //state.httpsVhost(servername, conn);
 | ||||
|           //return;
 | ||||
|           vhost = state.config.vhost.replace(/:hostname/, servername); | ||||
|           require('fs').readdir(vhost, function (err, nodes) { | ||||
|             if (state.debug && err) { console.log("VHOST error", err); } | ||||
|             if (err || !nodes) { run(); return; } | ||||
|             //if (nodes) { deferData('httpsVhost'); return; }
 | ||||
|             deferData('httpsVhost'); | ||||
|             if (err || !nodes) { outcomes.assumeExternal(); return; } | ||||
|             outcomes.isVhost(vhost); | ||||
|           }); | ||||
|           return; | ||||
|         } | ||||
| 
 | ||||
|         run(); | ||||
|         outcomes.assumeExternal(); | ||||
|       } | ||||
| 
 | ||||
|       // https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
 | ||||
| @ -111,40 +192,19 @@ module.exports.createTcpConnectionHandler = function (state) { | ||||
|         service = 'https'; | ||||
|         servername = (sni(firstChunk)||'').toLowerCase().trim(); | ||||
|         if (state.debug) { console.log("[tcp] tls hello from '" + servername + "'"); } | ||||
|         tryTls(); | ||||
|         handleConnection(tlsOutcomes); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if (firstChunk[0] > 32 && firstChunk[0] < 127) { | ||||
|         // (probably) HTTP
 | ||||
|         str = firstChunk.toString(); | ||||
|         m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im); | ||||
|         servername = (m && m[1].toLowerCase() || '').split(':')[0]; | ||||
|         if (state.debug) { console.log("[tcp] http hostname '" + servername + "'"); } | ||||
| 
 | ||||
|         if (/HTTP\//i.test(str)) { | ||||
|           if (!state.servernames.length) { | ||||
|             console.info("[tcp] No admin servername. Entering setup mode."); | ||||
|             deferData(); | ||||
|             state.httpSetupServer.emit('connection', conn); | ||||
|             return; | ||||
|           } | ||||
| 
 | ||||
|           service = 'http'; | ||||
|           // TODO make https redirect configurable
 | ||||
|           // /^\/\.well-known\/acme-challenge\//.test(str)
 | ||||
|           if (/well-known/.test(str)) { | ||||
|             // HTTP
 | ||||
|             if (Devices.exist(state.deviceLists, servername)) { | ||||
|               deferData(); | ||||
|               pipeWs(servername, service, Devices.next(state.deviceLists, servername), conn, serviceport); | ||||
|               return; | ||||
|             } | ||||
|             deferData('handleHttp'); | ||||
|             return; | ||||
|           } | ||||
| 
 | ||||
|           // redirect to https
 | ||||
|           deferData('handleInsecureHttp'); | ||||
|           handleConnection(httpOutcomes); | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
|  | ||||
							
								
								
									
										24
									
								
								snap/snapcraft.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								snap/snapcraft.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| name: telebit-relay | ||||
| version: '0.20.0' | ||||
| summary: Because friends don't let friends localhost | ||||
| description: | | ||||
|   A server that works in combination with Telebit Remote | ||||
|   to allow you to serve http and https from any computer, | ||||
|   anywhere through a secure tunnel. | ||||
| 
 | ||||
| grade: stable | ||||
| confinement: strict | ||||
| 
 | ||||
| apps: | ||||
|   telebit-relay: | ||||
|     command: telebit-relay --config $SNAP_COMMON/config.yml | ||||
|     plugs: [network, network-bind] | ||||
|     daemon: simple | ||||
| 
 | ||||
| parts: | ||||
|   telebit-relay: | ||||
|     plugin: nodejs | ||||
|     node-engine: 10.13.0 | ||||
|     source: . | ||||
|     override-build: | | ||||
|       snapcraftctl build | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user