Skip to content

Commit

Permalink
rolled back up
Browse files Browse the repository at this point in the history
  • Loading branch information
mattinsler committed May 19, 2013
1 parent 02047a8 commit e288ca2
Show file tree
Hide file tree
Showing 18 changed files with 600 additions and 111 deletions.
11 changes: 2 additions & 9 deletions bin/axle
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,13 @@ function start_script(original_script, args) {
require(script_file);
}

require('coffee-script');
var Axle = require('../index');

if (process.argv.length === 2) {
// Server
return Axle.start_server();
}

// Client

var cmd = process.argv[2]
, args = process.argv.slice(3);

Axle.start_client();
require('coffee-script');
require('../lib/cli').run_client();

if (cmd === 'node') { return start_node(args); }
if (cmd === 'coffee') { return start_coffee(args); }
Expand Down
14 changes: 14 additions & 0 deletions bin/axle-server
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env node

var program = require('commander');

program
.version(require('../package').version)

.command('run', 'Run the axle server')
.command('install', 'Install the axle services to run all the time')
.command('uninstall', 'Remove the axle services (you should be ashamed)')
// .command('status', 'Check on the ')
// .command('daemon', 'Run the axle-server daemon')

.parse(process.argv);
4 changes: 4 additions & 0 deletions bin/axle-server-daemon
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node

require('coffee-script');
require('../lib/cli').daemon();
4 changes: 4 additions & 0 deletions bin/axle-server-install
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node

require('coffee-script');
require('../lib/cli').install();
4 changes: 4 additions & 0 deletions bin/axle-server-run
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node

require('coffee-script');
require('../lib/cli').run_server();
4 changes: 4 additions & 0 deletions bin/axle-server-uninstall
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node

require('coffee-script');
require('../lib/cli').uninstall();
55 changes: 8 additions & 47 deletions index.coffee
Original file line number Diff line number Diff line change
@@ -1,50 +1,11 @@
exports.version = require('./package').version

exports.Configuration = require './lib/configuration'

exports.Axle = require './lib/axle'
exports.Logger = require './lib/logger'
exports.Client = require './lib/client'
exports.Server = require './lib/server'
exports.Service = require './lib/service'

exports.start_server = ->
require 'colors'
coupler = require 'coupler'

log = -> console.log '[' + 'axle'.cyan + '] ' + arguments[0]

axle = new exports.Axle(process.env.PORT || 3000)
coupler.accept(tcp: 1313).provide(axle: (connection) -> new exports.Service(axle, connection))

axle.on 'listening', (address) -> log 'Listening on port ' + address.port.toString().green
axle.on 'route:add', (route) -> log 'Added'.green + ' route ' + route.host + ' => ' + route.endpoint
axle.on 'route:remove', (route) -> log 'Removed'.red + ' route ' + route.host + ' => ' + route.endpoint
axle.on 'route:match', (from, to) -> log 'Routing ' + from.yellow + ' to ' + "#{to.host}:#{to.port}".green
axle.on 'route:miss', (host) -> log 'No route for ' + host.red

axle.start()
axle

exports.start_client = ->
require 'colors'
coupler = require 'coupler'

log = -> console.log '[' + 'axle'.cyan + '] ' + arguments[0]

if process.env.AXLE_DOMAINS?
domains = process.env.AXLE_DOMAINS.split(',')
else
try
pkg = require(process.cwd() + '/package')
domains ?= pkg.axle_domains
domains ?= "#{pkg.name}.localhost.dev"
domains ?= []
domains = [domains] unless Array.isArray(domains)

return null unless domains?

axle_service = coupler.connect(tcp: 1313).consume('axle')
client = new exports.Client(axle_service, domains)

client.on 'listening', (server) -> log 'Listening on port ' + server.address().port.toString().green
client.on 'connected', -> log 'Listening on ' + client.domains.map((d) -> d.green).join(', ')
client.on 'reconnected', -> log 'Reconnected'.green + ' to axle service'
client.on 'disconnected', -> log 'Lost Connection'.yellow + ' to axle service'

client.start()
client
exports.Dns = require './lib/dns'
exports.OsxResolverManager = require './lib/osx_resolver_manager'
59 changes: 21 additions & 38 deletions lib/axle.coffee
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
events = require 'events'
{EventEmitter} = require 'events'
Logger = require './logger'

parse_endpoint = (endpoint) ->
if parseInt(endpoint).toString() is endpoint.toString()
Expand All @@ -13,7 +14,7 @@ parse_endpoint = (endpoint) ->
class RoutePredicate
constructor: (@host, @endpoint) ->
@target = parse_endpoint(@endpoint)

matches: (host) ->
@host is host

Expand All @@ -25,56 +26,38 @@ class WildcardRoutePredicate extends RoutePredicate
matches: (host) ->
@rx.test(host)

class Axle extends events.EventEmitter
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]
class Axle extends EventEmitter
config: require './configuration'

initialize: ->
return if @server?

@server = require('http').createServer()
@server.on 'listening', => @emit('listening', @server.address())
@server.on 'error', (err) => @emit('error', err)

constructor: ->
@routes = []
@distribute = require('distribute')(@server)
@distribute.use (req, res, next) =>
try
[host, _x] = req.headers.host.split(':')

for e in @routes
if e.matches(host)
@emit('route:match', host, e.target)
return next(e.target.port, e.target.host)

@emit('route:miss', host)
next()
catch e
next(e)

start: ->
return if @server?
@initialize()
@server.listen(@port)

stop: ->
@server.close()
delete @server

@on 'route:add', (route) -> Logger.info Logger.green('Added') + ' route ' + route.host + ' => ' + route.endpoint
@on 'route:remove', (route) -> Logger.info Logger.red('Removed') + ' route ' + route.host + ' => ' + route.endpoint
@on 'route:match', (from, to) -> Logger.debug 'Matched route for ' + Logger.yellow(from) + ' to ' + Logger.green("#{to.host}:#{to.port}")
@on 'route:miss', (host) -> Logger.debug 'No route for ' + Logger.red(host)

remove: (route) ->
@routes = @routes.filter (r) =>
if r.host is route.host and r.endpoint is route.endpoint
@emit('route:remove', r)
return false
true

serve: (host, endpoint) ->
if host.indexOf('*') isnt -1
@routes.push(new WildcardRoutePredicate(host, endpoint))
else
@routes.push(new RoutePredicate(host, endpoint))
@emit('route:add', {host: host, endpoint: endpoint})

match: (host) ->
for e in @routes
if e.matches(host)
@emit('route:match', host, e.target)
return e.target

@emit('route:miss', host)
null

module.exports = Axle
120 changes: 120 additions & 0 deletions lib/cli.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# - axle registry
# - tcp service
# - dns server
# - proxy
# - web server for status

os = require 'os'
axle = require '../index'
walkabout = require 'walkabout'
exec = require('child_process').exec

Logger = axle.Logger

AXLE_RESOLVER = """
# Resolver for axle
nameserver 127.0.0.1
port #{axle.Configuration.dns.port}
"""

AXLE_PLIST_FILE = walkabout('/Library/LaunchAgents/com.mattinsler.axle.plist')
AXLE_DAEMON_PLIST = """
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.mattinsler.axle</string>
<key>ProgramArguments</key>
<array>
<string>#{process.execPath}</string>
<string>#{walkabout(__dirname).join('../bin/axle-server-run').absolute_path}</string>
</array>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>Debug</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/com.mattinsler.axle.log</string>
<key>StandardErrorPath</key>
<string>/var/log/com.mattinsler.axle.log</string>
</dict>
</plist>
"""

exports.install = ->
Logger.info 'install'

return Logger.error('Try running with sudo') unless process.getuid() is 0

Logger.info 'mkdirp /etc/resolver'
walkabout('/etc/resolver').mkdirp_sync()
# Logger.info 'write /etc/resolver/axle'
# walkabout('/etc/resolver/axle').write_file_sync(AXLE_RESOLVER)

Logger.info 'write', AXLE_PLIST_FILE.absolute_path
walkabout(AXLE_PLIST_FILE.dirname).mkdirp_sync()
AXLE_PLIST_FILE.write_file_sync(AXLE_DAEMON_PLIST)

Logger.info 'launch axle server'
exec "launchctl unload #{AXLE_PLIST_FILE.absolute_path}", ->
exec "launchctl load #{AXLE_PLIST_FILE.absolute_path}", ->
Logger.info 'ok'

exports.uninstall = ->
Logger.info 'uninstall'

return Logger.error('Try running with sudo') unless process.getuid() is 0

# Logger.info 'rm /etc/resolver/axle'
# walkabout('/etc/resolver/axle').unlink_sync() if walkabout('/etc/resolver/axle').exists_sync()

Logger.info 'stop axle server'
exec "launchctl unload #{AXLE_PLIST_FILE.absolute_path}", ->
Logger.info 'rm', AXLE_PLIST_FILE.absolute_path
AXLE_PLIST_FILE.unlink_sync() if AXLE_PLIST_FILE.exists_sync()

Logger.info 'ok'

exports.run_client = ->
client = new axle.Client()

if process.env.AXLE_DOMAINS?
client.domains = process.env.AXLE_DOMAINS.split(',')
else
try
pkg = require(process.cwd() + '/package')
client.domains ?= pkg['axle-domains']
client.domains ?= ["#{pkg.name}.localhost.dev"]
client.domains ?= []
client.domains = [domains] unless Array.isArray(domains)

client.start()

exports.run_server = ->
Logger.info 'server'

return Logger.error('Try running with sudo') unless process.getuid() is 0

instance = new axle.Axle()

servers = []
servers.push(new axle.Server(instance)) if instance.config.server.enabled
servers.push(new axle.Service(instance)) if instance.config.service.enabled
servers.push(new axle.Dns(instance)) if instance.config.dns.enabled

servers.push(new axle.OsxResolverManager(instance)) if os.platform() is 'darwin'

servers.forEach (s) -> s.start()

process.on 'SIGTERM', ->
servers.forEach (s) -> s.stop()
Logger.info 'bye bye'
process.exit(0)

Logger.info 'started'

exports.daemon = ->

52 changes: 40 additions & 12 deletions lib/client.coffee
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
net = require 'net'
http = require 'http'
coupler = require 'coupler'
Logger = require './logger'
Configuration = require './configuration'
portfinder = require 'portfinder'
EventEmitter = require('events').EventEmitter
{EventEmitter} = require 'events'

class Client extends EventEmitter
constructor: (@axle_service, @domains) ->

start: ->
createServer = http.createServer
http.createServer = =>
server = createServer.apply(http, arguments)
return server if @intercepted
@intercepted = true
server.on 'error', => @on_server_error(server, arguments...)
server.on 'listening', => @on_server_listening(server, arguments...)
server
constructor: ->
@on 'listening', (server) -> Logger.info 'Listening on port ' + Logger.green(server.address().port)
@on 'connected', => Logger.info 'Listening on ' + @domains.map((d) -> Logger.green(d)).join(', ')
@on 'reconnected', -> Logger.info Logger.green('Reconnected') + ' to axle service'
@on 'disconnected', -> Logger.info Logger.yellow('Lost Connection') + ' to axle service'

@axle_service = coupler.connect(tcp: Configuration.service.port).consume('axle')

on_server_error: (server, err) ->
if err.code is 'EADDRINUSE'
Expand All @@ -35,4 +34,33 @@ class Client extends EventEmitter
@axle_service.on 'coupler:disconnected', =>
@emit('disconnected', server)

start: ->
createServer = http.createServer
http.createServer = =>
server_args = Array::slice.call(arguments)
server = createServer.apply(http, server_args)
return server if @intercepted
@intercepted = true
server.on 'error', => @on_server_error(server, server_args...)
server.on 'listening', => @on_server_listening(server, server_args...)

serverListen = server.listen
server.listen = ->
listen_args = Array::slice.call(arguments)
callback = listen_args[listen_args.length - 1] if listen_args.length > 0 and typeof listen_args[listen_args.length - 1] is 'function'

portfinder.getPort (err, port) =>
if err?
@emit('error', err)
callback?(err)
return
serverListen.call(server, port, callback)

server

server

stop: ->


module.exports = Client
Loading

0 comments on commit e288ca2

Please sign in to comment.