forked from coolaj86/goldilocks.js
		
	cleaned up all of the custom HTTP handling logic
This commit is contained in:
		
							parent
							
								
									ab31bae6ff
								
							
						
					
					
						commit
						ab011d1829
					
				| @ -21,108 +21,6 @@ module.exports.create = function (deps, config) { | |||||||
|   var tls = require('tls'); |   var tls = require('tls'); | ||||||
|   var domainMatches = require('./match-domain').match; |   var domainMatches = require('./match-domain').match; | ||||||
| 
 | 
 | ||||||
|   var tcpRouter = { |  | ||||||
|     _map: { } |  | ||||||
|   , _create: function (address, port) { |  | ||||||
|       // port provides hinting for http, smtp, etc
 |  | ||||||
|       return function (conn, firstChunk, opts) { |  | ||||||
|         console.log('[tcpRouter] ' + address + ':' + port + ' ' + (opts.servername || '')); |  | ||||||
| 
 |  | ||||||
|         var m; |  | ||||||
|         var str; |  | ||||||
|         var hostname; |  | ||||||
|         var newHeads; |  | ||||||
| 
 |  | ||||||
|         // TODO test per-module
 |  | ||||||
|         // Maybe HTTP
 |  | ||||||
|         if (firstChunk[0] > 32 && firstChunk[0] < 127) { |  | ||||||
|           str = firstChunk.toString(); |  | ||||||
|           m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im); |  | ||||||
|           hostname = (m && m[1].toLowerCase() || '').split(':')[0]; |  | ||||||
|           console.log('[tcpRouter] hostname', hostname); |  | ||||||
|           if (/HTTP\//i.test(str)) { |  | ||||||
|             //conn.__service = 'http';
 |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!hostname) { |  | ||||||
|           // TODO allow tcp tunneling
 |  | ||||||
|           // TODO we need some way of tagging tcp as either terminated tls or insecure
 |  | ||||||
|           conn.write( |  | ||||||
|             "HTTP/1.1 404 Not Found\r\n" |  | ||||||
|           + "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" |  | ||||||
|           + "Content-Type: text/html\r\n" |  | ||||||
|           + "Content-Length: " + 9 + "\r\n" |  | ||||||
|           + "\r\n" |  | ||||||
|           + "Not Found" |  | ||||||
|           ); |  | ||||||
|           conn.end(); |  | ||||||
|           return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         // Poor-man's http proxy
 |  | ||||||
|         // XXX SECURITY XXX: should strip existing X-Forwarded headers
 |  | ||||||
|         newHeads = |  | ||||||
|           [ "X-Forwarded-Proto: " + (opts.encrypted ? 'https' : 'http') |  | ||||||
|           , "X-Forwarded-For: " + (opts.remoteAddress || conn.remoteAddress) |  | ||||||
|           , "X-Forwarded-Host: " + hostname |  | ||||||
|           ]; |  | ||||||
| 
 |  | ||||||
|         if (!opts.encrypted) { |  | ||||||
|           // a exists-only header that a bad client could not remove
 |  | ||||||
|           newHeads.push("X-Not-Encrypted: yes"); |  | ||||||
|         } |  | ||||||
|         if (opts.servername) { |  | ||||||
|           newHeads.push("X-Forwarded-Sni: " + opts.servername); |  | ||||||
|           if (opts.servername !== hostname) { |  | ||||||
|             // an exists-only header that a bad client could not remove
 |  | ||||||
|             newHeads.push("X-Two-Servernames: yes"); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         firstChunk = firstChunk.toString('utf8'); |  | ||||||
|         // JSON.stringify("Host: example.com\r\nNext: Header".replace(/(Host: [^\r\n]*)/i, "$1" + "\r\n" + "X: XYZ"))
 |  | ||||||
|         firstChunk = firstChunk.replace(/(Host: [^\r\n]*)/i, "$1" + "\r\n" + newHeads.join("\r\n")); |  | ||||||
| 
 |  | ||||||
|         process.nextTick(function () { |  | ||||||
|           conn.unshift(Buffer.from(firstChunk, 'utf8')); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         //
 |  | ||||||
|         // hard-coded routes for the admin interface
 |  | ||||||
|         if ( |  | ||||||
|           /\blocalhost\.admin\./.test(hostname) || /\badmin\.localhost\./.test(hostname) |  | ||||||
|           || /\blocalhost\.alpha\./.test(hostname) || /\balpha\.localhost\./.test(hostname) |  | ||||||
|         ) { |  | ||||||
|           if (!modules.admin) { |  | ||||||
|             modules.admin = require('./modules/admin.js').create(deps, config); |  | ||||||
|           } |  | ||||||
|           modules.admin.emit('connection', conn); |  | ||||||
|           return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // TODO static file handiling and such or whatever
 |  | ||||||
|         if (!modules.http) { |  | ||||||
|           modules.http = require('./modules/http.js').create(deps, config); |  | ||||||
|         } |  | ||||||
|         opts.hostname = hostname; |  | ||||||
|         conn.__opts = opts; |  | ||||||
| 
 |  | ||||||
|         modules.http.emit('connection', conn); |  | ||||||
|       }; |  | ||||||
|     } |  | ||||||
|   , get: function getTcpRouter(address, port) { |  | ||||||
|       address = address || '0.0.0.0'; |  | ||||||
| 
 |  | ||||||
|       var id = address + ':' + port; |  | ||||||
|       if (!tcpRouter._map[id]) { |  | ||||||
|         tcpRouter._map[id] = tcpRouter._create(address, port); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       return tcpRouter._map[id]; |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|   var tlsRouter = { |   var tlsRouter = { | ||||||
|     proxy: function (socket, opts, mod) { |     proxy: function (socket, opts, mod) { | ||||||
|       var newConn = deps.net.createConnection({ |       var newConn = deps.net.createConnection({ | ||||||
| @ -231,25 +129,36 @@ module.exports.create = function (deps, config) { | |||||||
| 
 | 
 | ||||||
|     // TLS byte 1 is handshake and byte 6 is client hello
 |     // TLS byte 1 is handshake and byte 6 is client hello
 | ||||||
|     if (0x16 === firstChunk[0]/* && 0x01 === firstChunk[5]*/) { |     if (0x16 === firstChunk[0]/* && 0x01 === firstChunk[5]*/) { | ||||||
|       console.log('tryTls'); |  | ||||||
|       opts.servername = (parseSni(firstChunk)||'').toLowerCase() || 'localhost.invalid'; |       opts.servername = (parseSni(firstChunk)||'').toLowerCase() || 'localhost.invalid'; | ||||||
|       tlsRouter.processSocket(conn, firstChunk, opts); |       tlsRouter.processSocket(conn, firstChunk, opts); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     console.log('tryTcp'); |     // This doesn't work with TLS, but now that we know this isn't a TLS connection we can
 | ||||||
| 
 |     // unshift the first chunk back onto the connection for future use. The unshift should
 | ||||||
|     if (opts.hyperPeek) { |     // happen after any listeners are attached to it but before any new data comes in.
 | ||||||
|       // even though we've already peeked, this logic is just as well to let be
 |     if (!opts.hyperPeek) { | ||||||
|       // since it works properly either way, unlike the tls socket
 |       process.nextTick(function () { | ||||||
|       conn.once('data', function (chunk) { |         conn.unshift(firstChunk); | ||||||
|         console.log('hyperPeek re-peek data', chunk.toString('utf8')); |  | ||||||
|         tcpRouter.get(opts.localAddress || conn.localAddress, conn.localPort)(conn, chunk, opts); |  | ||||||
|       }); |       }); | ||||||
|       return; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     tcpRouter.get(opts.localAddress || conn.localAddress, conn.localPort)(conn, firstChunk, opts); |     // Connection is not TLS, check for HTTP next.
 | ||||||
|  |     if (firstChunk[0] > 32 && firstChunk[0] < 127) { | ||||||
|  |       var firstStr = firstChunk.toString(); | ||||||
|  |       if (/HTTP\//i.test(firstStr)) { | ||||||
|  |         if (!modules.http) { | ||||||
|  |           modules.http = require('./modules/http.js').create(deps, config); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         conn.__opts = opts; | ||||||
|  |         modules.http.emit('connection', conn); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     console.warn('failed to identify protocol from first chunk', firstChunk); | ||||||
|  |     conn.close(); | ||||||
|   } |   } | ||||||
|   function netHandler(conn, opts) { |   function netHandler(conn, opts) { | ||||||
|     opts = opts || {}; |     opts = opts || {}; | ||||||
|  | |||||||
| @ -60,7 +60,5 @@ module.exports.create = function (deps, conf) { | |||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   /* device, addresses, cwd, http */ |   /* device, addresses, cwd, http */ | ||||||
|   var app = require('../app.js')(deps, conf, opts); |   return require('../app.js')(deps, conf, opts); | ||||||
|   var http = require('http'); |  | ||||||
|   return http.createServer(app); |  | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -2,8 +2,16 @@ | |||||||
| 
 | 
 | ||||||
| module.exports.create = function (deps, conf) { | module.exports.create = function (deps, conf) { | ||||||
|   var app = require('express')(); |   var app = require('express')(); | ||||||
|  |   var adminApp = require('./admin').create(deps, conf); | ||||||
|   var domainMatches = require('../match-domain').match; |   var domainMatches = require('../match-domain').match; | ||||||
| 
 | 
 | ||||||
|  |   var adminDomains = [ | ||||||
|  |     /\blocalhost\.admin\./ | ||||||
|  |   , /\blocalhost\.alpha\./ | ||||||
|  |   , /\badmin\.localhost\./ | ||||||
|  |   , /\balpha\.localhost\./ | ||||||
|  |   ]; | ||||||
|  | 
 | ||||||
|   // We handle both HTTPS and HTTP traffic on the same ports, and we want to redirect
 |   // We handle both HTTPS and HTTP traffic on the same ports, and we want to redirect
 | ||||||
|   // any unencrypted requests to the same port they came from unless it came in on
 |   // any unencrypted requests to the same port they came from unless it came in on
 | ||||||
|   // the default HTTP port, in which case there wont be a port specified in the host.
 |   // the default HTTP port, in which case there wont be a port specified in the host.
 | ||||||
| @ -17,6 +25,18 @@ module.exports.create = function (deps, conf) { | |||||||
|     redirecter(req, res, next); |     redirecter(req, res, next); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   function handleAdmin(req, res, next) { | ||||||
|  |     var admin = adminDomains.some(function (re) { | ||||||
|  |       return re.test(req.headers.host); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (admin) { | ||||||
|  |       adminApp(req, res); | ||||||
|  |     } else { | ||||||
|  |       next(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   function respond404(req, res) { |   function respond404(req, res) { | ||||||
|     res.writeHead(404); |     res.writeHead(404); | ||||||
|     res.end('Not Found'); |     res.end('Not Found'); | ||||||
| @ -36,6 +56,14 @@ module.exports.create = function (deps, conf) { | |||||||
|     , toProxy: true |     , toProxy: true | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     // We want to override the default value for some headers with the extra information we
 | ||||||
|  |     // have available to us in the opts object attached to the connection.
 | ||||||
|  |     proxy.on('proxyReq', function (proxyReq, req) { | ||||||
|  |       var conn = req.connection; | ||||||
|  |       var opts = conn.__opts; | ||||||
|  |       proxyReq.setHeader('X-Forwarded-For', opts.remoteAddress || conn.remoteAddress); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     return function (req, res, next) { |     return function (req, res, next) { | ||||||
|       var hostname = req.headers.host.split(':')[0]; |       var hostname = req.headers.host.split(':')[0]; | ||||||
|       var relevant = mod.domains.some(function (pattern) { |       var relevant = mod.domains.some(function (pattern) { | ||||||
| @ -51,6 +79,7 @@ module.exports.create = function (deps, conf) { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   app.use(redirectHttps); |   app.use(redirectHttps); | ||||||
|  |   app.use(handleAdmin); | ||||||
| 
 | 
 | ||||||
|   (conf.http.modules || []).forEach(function (mod) { |   (conf.http.modules || []).forEach(function (mod) { | ||||||
|     if (mod.name === 'proxy') { |     if (mod.name === 'proxy') { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user