Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit d1723b9d29cdd8e574a38caaa11287dfbe320330 @mattinsler committed Dec 12, 2012
Showing with 317 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. 0 README.md
  3. +64 −0 bin/axle
  4. +25 −0 index.coffee
  5. +55 −0 lib/axle_client.coffee
  6. +75 −0 lib/axle_server.coffee
  7. +24 −0 lib/net_client.coffee
  8. +28 −0 lib/net_server.coffee
  9. +35 −0 package.json
  10. +2 −0 test.coffee
  11. +2 −0 test.js
  12. +6 −0 test_client.coffee
@@ -0,0 +1 @@
+node_modules
No changes.
@@ -0,0 +1,64 @@
+#!/usr/bin/env node
+
+function start_node(args) {
+ var script = args[0];
+ if (script.indexOf('/') !== 0) { script = process.cwd() + '/' + script; }
+
+ process.argv = process.argv.slice(2);
+ require(script);
+}
+
+function start_coffee(args) {
+ var script = args[0];
+ if (script.indexOf('/') !== 0) { script = process.cwd() + '/' + script; }
+
+ process.env.NODE_PATH = (process.env.NODE_PATH || '') + ':/usr/local/lib/node_modules';
+ require('module').Module._cache = {};
+ require('module').Module._initPaths();
+
+ require('coffee-script');
+ process.argv = process.argv.slice(2);
+ require(script);
+}
+
+function start_script(original_script, args) {
+ var script = original_script;
+ if (script.indexOf('/') !== 0) { script = process.cwd() + '/' + script; }
+ try { script = fs.readlinkSync(script); } catch(e) {}
+
+ var content = fs.readFileSync(script, 'utf8');
+ content = content.slice(content.indexOf('\n') + 1);
+ var filename = require('path').dirname(script) + '/' + require('crypto').randomBytes(8).toString('hex') + '.js';
+ fs.writeFileSync(filename, content, 'utf8');
+
+ process.on('uncaughtException', function() {
+ try { fs.unlinkSync(filename); } catch(e) {}
+ });
+ process.on('exit', function() {
+ try { fs.unlinkSync(filename); } catch(e) {}
+ });
+
+ process.env.NODE_PATH = (process.env.NODE_PATH || '') + ':/usr/local/lib/node_modules';
+ require('module').Module._cache = {};
+ require('module').Module._initPaths();
+
+ process.argv = ['node', original_script].concat(process.argv.slice(3));
+ require(filename);
+}
+
+var cmd = process.argv[2]
+ , args = process.argv.slice(3);
+
+require('coffee-script');
+require('../lib/axle_client.coffee');
+
+if (cmd === 'node') { return start_node(args); }
+if (cmd === 'coffee') { return start_coffee(args); }
+
+var exec = require('child_process').exec
+ , fs = require('fs');
+
+exec('which ' + cmd, function(err, stdout, stderr) {
+ if (err) { return console.error(err.stack); }
+ return start_script(stdout.replace(/(^[\r\n\t ]+|[\r\n\t ]+$)/g, ''), args);
+});
@@ -0,0 +1,25 @@
+Axle = require './lib/axle_server'
+NetServer = require './lib/net_server'
+NetClient = require './lib/net_client'
+
+class AxleProtocol
+ constructor: (@axle, socket) ->
+ @client = new NetClient(socket)
+ @client.on 'message', (command, data) =>
+ @['on_' + command](data) if @['on_' + command]?
+
+ on_connected: ->
+
+
+ on_disconnected: ->
+ if @routes?
+ @axle.remove(r) for r in @routes
+
+ on_register: (data) ->
+ @routes = if Array.isArray(data) then data else [data]
+
+ for r in @routes
+ @axle.serve(r.host, r.endpoint)
+
+axle = new Axle(process.env.PORT || 3000)
+tcp_server = new NetServer(client_factory: (socket) -> new AxleProtocol(axle, socket)).listen(1313)
@@ -0,0 +1,55 @@
+require 'colors'
+
+net = require 'net'
+http = require 'http'
+
+DOMAINS = []
+if process.env.HUB_DOMAINS?
+ Array::push.apply(DOMAINS, process.env.HUB_DOMAINS.split(','))
+try
+ name = require(process.cwd() + '/package').name
+ DOMAINS.push("#{name}.localhost.dev") if name?
+
+class AxleClient
+ constructor: (@server) ->
+ @log = -> console.log '[' + 'axle'.cyan + '] ' + arguments[0]
+
+ @server.once('listening', => @on_server_listening())
+
+ send: (command, data) ->
+ @client.write(JSON.stringify($c: command, $d: data))
+
+ connect: ->
+ @client = net.connect(port: 1313)
+ ['connect', 'end', 'close', 'data', 'error'].forEach (event) =>
+ @client.on(event, => @['on_' + event](arguments...))
+
+ on_server_listening: ->
+ @connect()
+
+ on_connect: ->
+ @send('register', DOMAINS.map (d) => {host: d, endpoint: @server.address().port})
+ @log 'Listening on ' + DOMAINS.map((d) -> d.green).join(', ')
+
+ on_end: ->
+
+
+ on_close: ->
+ setTimeout (=> @connect()), 1000
+
+ on_error: (err) ->
+ console.log '[ERROR] ' + err.code
+
+ # if err.code is 'ECONNREFUSED'
+ # setTimeout (=> @connect()), 1000
+
+ on_data: ->
+ console.log 'data'
+ console.log arguments
+
+
+_createServer = http.createServer
+http.createServer = ->
+ server = _createServer.apply(http, arguments)
+ new AxleClient(server)
+ server
@@ -0,0 +1,75 @@
+require 'colors'
+betturl = require 'betturl'
+
+parse_endpoint = (endpoint) ->
+ if parseInt(endpoint).toString() is endpoint.toString()
+ target_host = 'localhost'
+ target_port = parseInt(endpoint)
+ else
+ [target_host, target_port] = endpoint.split(':')
+ target_port = if target_port? then parseInt(target_port) else 80
+
+ {host: target_host, port: target_port}
+
+class RoutePredicate
+ constructor: (@host, @endpoint) ->
+ @target = parse_endpoint(@endpoint)
+
+ matches: (host) ->
+ @host is host
+
+class WildcardRoutePredicate extends RoutePredicate
+ constructor: ->
+ super
+ @rx = new RegExp('^' + @host.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$')
+
+ matches: (host) ->
+ @rx.test(host)
+
+class Axle
+ constructor: (port) ->
+ throw new Error('Axle must take a port that is a number') unless port? and parseInt(port).toString() is port.toString()
+
+ @log = -> console.log '[' + 'axle'.cyan + '] ' + arguments[0]
+
+ @server = require('http').createServer().listen(port)
+
+ @routes = []
+ @distribute = require('distribute')(@server)
+ # @interval_id = setInterval (=> @reap()), 10 * 1000
+
+ @distribute.use (req, res, next) =>
+ [host, _x] = req.headers.host.split(':')
+
+ for e in @routes
+ if e.matches(host)
+ console.log '[' + 'axle'.cyan + '] Routing ' + host.yellow + ' to ' + "#{e.target.host}:#{e.target.port}".green
+ return next(e.target.port, e.target.host)
+
+ @log 'No route for ' + host.red
+ next()
+
+ # reap: ->
+ # @routes.forEach (e) =>
+ # require('request') {
+ # url: "http://#{e.target.host}:#{e.target.port}/"
+ # method: 'HEAD'
+ # timeout: 5000
+ # }, (err, req, body) =>
+ # @remove(e) if err?
+
+ remove: (route) ->
+ @routes = @routes.filter (r) =>
+ if r.host is route.host and r.endpoint is route.endpoint
+ @log 'Removed'.red + ' route ' + r.host + ' => ' + r.endpoint
+ return false
+ true
+
+ serve: (host, endpoint) ->
+ if host.indexOf('*') isnt -1
+ @routes.push(new WildcardRoutePredicate(host, endpoint))
+ else
+ @routes.push(new RoutePredicate(host, endpoint))
+ @log 'Added'.green + ' route ' + host + ' => ' + endpoint
+
+module.exports = Axle
@@ -0,0 +1,24 @@
+class NetClient extends require('events').EventEmitter
+ constructor: (@socket) ->
+ @id = "#{@socket.remoteAddress}:#{@socket.remotePort}"
+ @log = => console.log '[' + @id.green + '] ' + arguments[0]
+
+ ['data', 'close', 'end', 'error'].forEach (event) =>
+ @socket.on(event, => @['on_' + event](arguments...))
+
+ process.nextTick => @emit('message', 'connected')
+
+ on_data: (data) ->
+ data = JSON.parse(data.toString())
+ @emit('message', data.$c, data.$d)
+
+ on_close: ->
+
+ on_end: (had_error) ->
+ @emit('message', 'disconnected')
+
+ on_error: ->
+ @log 'error'
+ console.log arguments
+
+module.exports = NetClient
@@ -0,0 +1,28 @@
+net = require 'net'
+
+class NetServer
+ constructor: (opts) ->
+ @clients = {}
+
+ if opts.client?
+ @client_factory = (socket) =>
+ new opts.client(socket)
+ else if opts.client_factory?
+ @client_factory = opts.client_factory
+ else
+ throw new Error('NetServer constructor takes either a client or client_factory')
+
+ @server = net.createServer (s) => @on_connection(s)
+
+ on_connection: (socket) ->
+ client = @client_factory(socket)
+ @clients[client.id] = client
+ socket.on 'close', => @on_close(client)
+
+ on_close: (client) ->
+ delete @clients[client.id]
+
+ listen: ->
+ @server.listen(arguments...)
+
+module.exports = NetServer
@@ -0,0 +1,35 @@
+{
+ "name": "axle",
+ "version": "0.0.1",
+ "private": true,
+ "engines": {
+ "node": "0.8.x",
+ "npm": "1.1.x"
+ },
+ "dependencies": {
+ "distribute": "latest",
+ "portfinder": "latest",
+ "request": "latest",
+ "colors": "latest"
+ }
+}
+
+
+{
+ "author": "Matt Insler <matt.insler@gmail.com> (www.mattinsler.com)",
+ "name": "axle",
+ "description": "",
+ "version": "0.0.1",
+ "repository": {
+ "type": "git",
+ "url": "git@github.com:mattinsler/axle.git"
+ },
+ "dependencies": {
+ "distribute": "latest",
+ "colors": "latest"
+ },
+ "bin": {
+ "axle": "./bin/axle"
+ },
+ "main": "index"
+}
@@ -0,0 +1,2 @@
+console.log 'Hello'
+console.log process.argv
@@ -0,0 +1,2 @@
+console.log('Hello');
+console.log(process.argv);
@@ -0,0 +1,6 @@
+http = require 'http'
+
+server = require('http').createServer (req, res) ->
+ res.writeHead(200, 'Content-Type': 'text/plain')
+ res.end('Hello World')
+.listen(process.env.PORT || 3000)

0 comments on commit d1723b9

Please sign in to comment.