diff --git a/bin/thin b/bin/thin
index 444fdbc..b56eb34 100755
--- a/bin/thin
+++ b/bin/thin
@@ -32,6 +32,7 @@ opts = OptionParser.new do |opts|
opts.on("-a", "--address HOST", "bind to HOST address (default: 0.0.0.0)") { |host| options[:address] = host }
opts.on("-p", "--port PORT", "use PORT (default: 3000)") { |port| options[:port] = port.to_i }
opts.on("-S", "--socket PATH", "bind to unix domain socket") { |file| options[:socket] = file }
+ opts.on("-B", "--swiftiply", "Run using swiftiply") { |key| options[:swiftiply] = (key || true) }
opts.on("-e", "--environment ENV", "Rails environment (default: development)") { |env| options[:environment] = env }
opts.on("-c", "--chdir PATH", "Change to dir before starting") { |dir| options[:chdir] = File.expand_path(dir) }
opts.on("-t", "--timeout SEC", "Request or command timeout in sec",
@@ -95,9 +96,10 @@ def start(options)
server = Thin::Server.new(options[:address], options[:port])
end
- server.pid_file = options[:pid]
- server.log_file = options[:log]
- server.timeout = options[:timeout]
+ server.pid_file = options[:pid]
+ server.log_file = options[:log]
+ server.timeout = options[:timeout]
+ server.swiftiply = options[:swiftiply]
if options[:daemonize]
server.daemonize
diff --git a/lib/thin/cluster.rb b/lib/thin/cluster.rb
index 9d5e57c..cdc8921 100644
--- a/lib/thin/cluster.rb
+++ b/lib/thin/cluster.rb
@@ -29,11 +29,12 @@ module Thin
end
end
- def first_port; @options[:port] end
- def address; @options[:address] end
- def socket; @options[:socket] end
- def pid_file; @options[:pid] end
- def log_file; @options[:log] end
+ def first_port; @options[:port] end
+ def address; @options[:address] end
+ def socket; @options[:socket] end
+ def swiftiply; @options[:swiftiply] end
+ def pid_file; @options[:pid] end
+ def log_file; @options[:log] end
# Start the servers
def start
@@ -97,6 +98,8 @@ module Thin
cmd_options.merge!(:pid => pid_file_for(number), :log => log_file_for(number))
if socket
cmd_options.merge!(:socket => socket_for(number))
+ elsif swiftiply
+ cmd_options.merge!(:port => first_port)
else
cmd_options.merge!(:port => number)
end
@@ -108,7 +111,7 @@ module Thin
yield @only
else
@size.times do |n|
- yield socket ? n : (first_port + n)
+ yield((socket||swiftiply) ? n : (first_port + n))
end
end
end
diff --git a/lib/thin/connection.rb b/lib/thin/connection.rb
index 29bbb93..0d3a7fc 100644
--- a/lib/thin/connection.rb
+++ b/lib/thin/connection.rb
@@ -13,14 +13,24 @@ module Thin
# Server owning the connection
attr_accessor :server
+ # nil, true, or Swiftiply auth key
+ attr_accessor :swiftiply
+
def post_init
@request = Request.new
@response = Response.new
end
+ def connection_completed
+ return unless @swiftiply
+ key = (@swiftiply == true) ? '' : @swiftiply.to_s
+ send_data swiftiply_handshake(key)
+ end
+
def receive_data(data)
trace { data }
process if @request.parse(data)
+ post_init if @swiftiply
rescue InvalidRequest => e
log "Invalid request"
log_error e
@@ -40,7 +50,7 @@ module Thin
send_data chunk
end
- close_connection_after_writing
+ close_connection_after_writing unless @swiftiply
rescue Object => e
log "Unexpected error while processing request: #{e.message}"
@@ -53,6 +63,7 @@ module Thin
def unbind
@server.connection_finished(self)
+ ::EventMachine.add_timer(rand(2)) { reconnect(server.host, server.port) } if @swiftiply and !@server.stopping
end
protected
@@ -66,5 +77,14 @@ module Thin
Socket.unpack_sockaddr_in(get_peername)[1]
end
end
+
+ def swiftiply_handshake(key)
+ 'swiftclient' << host_ip.collect {|x| sprintf('%02x', x.to_i)}.join << sprintf('%04x', @server.port.to_i) << sprintf('%02x', key.length) << key
+ end
+
+ # For some reason Swiftiply request the current host
+ def host_ip
+ Socket.gethostbyname(@server.host)[3].unpack('CCCC') rescue [0,0,0,0]
+ end
end
end
\ No newline at end of file
diff --git a/lib/thin/server.rb b/lib/thin/server.rb
index a8bf756..ea03d8c 100644
--- a/lib/thin/server.rb
+++ b/lib/thin/server.rb
@@ -21,6 +21,12 @@ module Thin
# Maximum time for incoming data to arrive
attr_accessor :timeout
+ # nil, true, or Swiftiply auth key
+ attr_accessor :swiftiply
+
+ # True if server is stopping
+ attr_accessor :stopping
+
# Creates a new server bound to host:port
# or to +socket+ that will pass request to +app+.
# If +host_or_socket+ contains a / it is assumed
@@ -83,7 +89,11 @@ module Thin
@stopping = true
# Do not accept anymore connection
- EventMachine.stop_server(@signature)
+ if @swiftiply
+ EventMachine.stop
+ else
+ EventMachine.stop_server(@signature)
+ end
unless wait_for_connections_and_stop
# Still some connections running, schedule a check later
@@ -108,7 +118,9 @@ module Thin
protected
def start_server
- if @socket
+ if @swiftiply
+ start_client_for_swiftiply
+ elsif @socket
start_server_on_socket
else
start_server_on_host
@@ -127,12 +139,18 @@ module Thin
EventMachine.start_unix_domain_server(@socket, Connection, &method(:initialize_connection))
end
+ def start_client_for_swiftiply
+ log ">> Connecting to Swiftiply on #{@host}:#{@port}, CTRL+C to stop"
+ EventMachine.connect(@host, @port, Connection, &method(:initialize_connection))
+ end
+
def initialize_connection(connection)
connection.server = self
connection.comm_inactivity_timeout = @timeout
connection.app = @app
connection.silent = @silent
connection.unix_socket = !@socket.nil?
+ connection.swiftiply = @swiftiply
@connections << connection
end