thin 1.2.6-x86-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +273 -0
 - data/COPYING +18 -0
 - data/README +69 -0
 - data/Rakefile +39 -0
 - data/benchmark/abc +51 -0
 - data/benchmark/benchmarker.rb +80 -0
 - data/benchmark/runner +82 -0
 - data/bin/thin +6 -0
 - data/example/adapter.rb +32 -0
 - data/example/async_app.ru +126 -0
 - data/example/async_chat.ru +247 -0
 - data/example/async_tailer.ru +100 -0
 - data/example/config.ru +22 -0
 - data/example/monit_sockets +20 -0
 - data/example/monit_unixsock +20 -0
 - data/example/myapp.rb +1 -0
 - data/example/ramaze.ru +12 -0
 - data/example/thin.god +80 -0
 - data/example/thin_solaris_smf.erb +36 -0
 - data/example/thin_solaris_smf.readme.txt +150 -0
 - data/example/vlad.rake +64 -0
 - data/ext/thin_parser/common.rl +55 -0
 - data/ext/thin_parser/ext_help.h +14 -0
 - data/ext/thin_parser/extconf.rb +6 -0
 - data/ext/thin_parser/parser.c +1185 -0
 - data/ext/thin_parser/parser.h +49 -0
 - data/ext/thin_parser/parser.rl +157 -0
 - data/ext/thin_parser/thin.c +436 -0
 - data/lib/rack/adapter/loader.rb +91 -0
 - data/lib/rack/adapter/rails.rb +180 -0
 - data/lib/thin.rb +46 -0
 - data/lib/thin/backends/base.rb +141 -0
 - data/lib/thin/backends/swiftiply_client.rb +56 -0
 - data/lib/thin/backends/tcp_server.rb +29 -0
 - data/lib/thin/backends/unix_server.rb +51 -0
 - data/lib/thin/command.rb +53 -0
 - data/lib/thin/connection.rb +222 -0
 - data/lib/thin/controllers/cluster.rb +178 -0
 - data/lib/thin/controllers/controller.rb +182 -0
 - data/lib/thin/controllers/service.rb +75 -0
 - data/lib/thin/controllers/service.sh.erb +39 -0
 - data/lib/thin/daemonizing.rb +176 -0
 - data/lib/thin/headers.rb +39 -0
 - data/lib/thin/logging.rb +54 -0
 - data/lib/thin/request.rb +157 -0
 - data/lib/thin/response.rb +101 -0
 - data/lib/thin/runner.rb +212 -0
 - data/lib/thin/server.rb +248 -0
 - data/lib/thin/stats.html.erb +216 -0
 - data/lib/thin/stats.rb +52 -0
 - data/lib/thin/statuses.rb +43 -0
 - data/lib/thin/version.rb +32 -0
 - data/lib/thin_parser.so +0 -0
 - data/spec/backends/swiftiply_client_spec.rb +66 -0
 - data/spec/backends/tcp_server_spec.rb +33 -0
 - data/spec/backends/unix_server_spec.rb +37 -0
 - data/spec/command_spec.rb +25 -0
 - data/spec/configs/cluster.yml +9 -0
 - data/spec/configs/single.yml +9 -0
 - data/spec/connection_spec.rb +106 -0
 - data/spec/controllers/cluster_spec.rb +267 -0
 - data/spec/controllers/controller_spec.rb +129 -0
 - data/spec/controllers/service_spec.rb +50 -0
 - data/spec/daemonizing_spec.rb +192 -0
 - data/spec/headers_spec.rb +40 -0
 - data/spec/logging_spec.rb +46 -0
 - data/spec/perf/request_perf_spec.rb +50 -0
 - data/spec/perf/response_perf_spec.rb +19 -0
 - data/spec/perf/server_perf_spec.rb +39 -0
 - data/spec/rack/loader_spec.rb +42 -0
 - data/spec/rack/rails_adapter_spec.rb +106 -0
 - data/spec/rails_app/app/controllers/application.rb +10 -0
 - data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
 - data/spec/rails_app/app/helpers/application_helper.rb +3 -0
 - data/spec/rails_app/app/views/simple/index.html.erb +15 -0
 - data/spec/rails_app/config/boot.rb +109 -0
 - data/spec/rails_app/config/environment.rb +64 -0
 - data/spec/rails_app/config/environments/development.rb +18 -0
 - data/spec/rails_app/config/environments/production.rb +19 -0
 - data/spec/rails_app/config/environments/test.rb +22 -0
 - data/spec/rails_app/config/initializers/inflections.rb +10 -0
 - data/spec/rails_app/config/initializers/mime_types.rb +5 -0
 - data/spec/rails_app/config/routes.rb +35 -0
 - data/spec/rails_app/public/404.html +30 -0
 - data/spec/rails_app/public/422.html +30 -0
 - data/spec/rails_app/public/500.html +30 -0
 - data/spec/rails_app/public/dispatch.cgi +10 -0
 - data/spec/rails_app/public/dispatch.fcgi +24 -0
 - data/spec/rails_app/public/dispatch.rb +10 -0
 - data/spec/rails_app/public/favicon.ico +0 -0
 - data/spec/rails_app/public/images/rails.png +0 -0
 - data/spec/rails_app/public/index.html +277 -0
 - data/spec/rails_app/public/javascripts/application.js +2 -0
 - data/spec/rails_app/public/javascripts/controls.js +963 -0
 - data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
 - data/spec/rails_app/public/javascripts/effects.js +1120 -0
 - data/spec/rails_app/public/javascripts/prototype.js +4225 -0
 - data/spec/rails_app/public/robots.txt +5 -0
 - data/spec/rails_app/script/about +3 -0
 - data/spec/rails_app/script/console +3 -0
 - data/spec/rails_app/script/destroy +3 -0
 - data/spec/rails_app/script/generate +3 -0
 - data/spec/rails_app/script/performance/benchmarker +3 -0
 - data/spec/rails_app/script/performance/profiler +3 -0
 - data/spec/rails_app/script/performance/request +3 -0
 - data/spec/rails_app/script/plugin +3 -0
 - data/spec/rails_app/script/process/inspector +3 -0
 - data/spec/rails_app/script/process/reaper +3 -0
 - data/spec/rails_app/script/process/spawner +3 -0
 - data/spec/rails_app/script/runner +3 -0
 - data/spec/rails_app/script/server +3 -0
 - data/spec/request/mongrel_spec.rb +39 -0
 - data/spec/request/parser_spec.rb +243 -0
 - data/spec/request/persistent_spec.rb +35 -0
 - data/spec/request/processing_spec.rb +50 -0
 - data/spec/response_spec.rb +91 -0
 - data/spec/runner_spec.rb +168 -0
 - data/spec/server/builder_spec.rb +44 -0
 - data/spec/server/pipelining_spec.rb +110 -0
 - data/spec/server/robustness_spec.rb +34 -0
 - data/spec/server/stopping_spec.rb +55 -0
 - data/spec/server/swiftiply.yml +6 -0
 - data/spec/server/swiftiply_spec.rb +32 -0
 - data/spec/server/tcp_spec.rb +57 -0
 - data/spec/server/threaded_spec.rb +27 -0
 - data/spec/server/unix_socket_spec.rb +26 -0
 - data/spec/server_spec.rb +100 -0
 - data/spec/spec_helper.rb +219 -0
 - data/tasks/announce.rake +22 -0
 - data/tasks/deploy.rake +13 -0
 - data/tasks/email.erb +30 -0
 - data/tasks/gem.rake +66 -0
 - data/tasks/rdoc.rake +25 -0
 - data/tasks/site.rake +15 -0
 - data/tasks/spec.rake +43 -0
 - data/tasks/stats.rake +28 -0
 - metadata +219 -0
 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Thin
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Backends
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Backend to act as a Swiftiply client (http://swiftiply.swiftcore.org).
         
     | 
| 
      
 4 
     | 
    
         
            +
                class SwiftiplyClient < Base
         
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_accessor :key
         
     | 
| 
      
 6 
     | 
    
         
            +
                  
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_accessor :host, :port
         
     | 
| 
      
 8 
     | 
    
         
            +
                  
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def initialize(host, port, options={})
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @host = host
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @port = port.to_i
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @key  = options[:swiftiply].to_s
         
     | 
| 
      
 13 
     | 
    
         
            +
                    super()
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  # Connect the server
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def connect
         
     | 
| 
      
 18 
     | 
    
         
            +
                    EventMachine.connect(@host, @port, SwiftiplyConnection, &method(:initialize_connection))
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  # Stops the server
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def disconnect
         
     | 
| 
      
 23 
     | 
    
         
            +
                    EventMachine.stop
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  def to_s
         
     | 
| 
      
 27 
     | 
    
         
            +
                    "#{@host}:#{@port} swiftiply"
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end    
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              class SwiftiplyConnection < Connection
         
     | 
| 
      
 33 
     | 
    
         
            +
                def connection_completed
         
     | 
| 
      
 34 
     | 
    
         
            +
                  send_data swiftiply_handshake(@backend.key)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
                
         
     | 
| 
      
 37 
     | 
    
         
            +
                def persistent?
         
     | 
| 
      
 38 
     | 
    
         
            +
                  true
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
                
         
     | 
| 
      
 41 
     | 
    
         
            +
                def unbind
         
     | 
| 
      
 42 
     | 
    
         
            +
                  super
         
     | 
| 
      
 43 
     | 
    
         
            +
                  EventMachine.add_timer(rand(2)) { reconnect(@backend.host, @backend.port) } if @backend.running?
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
                
         
     | 
| 
      
 46 
     | 
    
         
            +
                protected
         
     | 
| 
      
 47 
     | 
    
         
            +
                  def swiftiply_handshake(key)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    'swiftclient' << host_ip.collect { |x| sprintf('%02x', x.to_i)}.join << sprintf('%04x', @backend.port) << sprintf('%02x', key.length) << key
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # For some reason Swiftiply request the current host
         
     | 
| 
      
 52 
     | 
    
         
            +
                  def host_ip
         
     | 
| 
      
 53 
     | 
    
         
            +
                    Socket.gethostbyname(@backend.host)[3].unpack('CCCC') rescue [0,0,0,0]
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Thin
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Backends
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Backend to act as a TCP socket server.
         
     | 
| 
      
 4 
     | 
    
         
            +
                class TcpServer < Base
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # Address and port on which the server is listening for connections.
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_accessor :host, :port
         
     | 
| 
      
 7 
     | 
    
         
            +
                  
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def initialize(host, port)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @host = host
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @port = port
         
     | 
| 
      
 11 
     | 
    
         
            +
                    super()
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                  
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # Connect the server
         
     | 
| 
      
 15 
     | 
    
         
            +
                  def connect
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @signature = EventMachine.start_server(@host, @port, Connection, &method(:initialize_connection))
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # Stops the server
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def disconnect
         
     | 
| 
      
 21 
     | 
    
         
            +
                    EventMachine.stop_server(@signature)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                        
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def to_s
         
     | 
| 
      
 25 
     | 
    
         
            +
                    "#{@host}:#{@port}"
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,51 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Thin
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Backends
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Backend to act as a UNIX domain socket server.
         
     | 
| 
      
 4 
     | 
    
         
            +
                class UnixServer < Base
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # UNIX domain socket on which the server is listening for connections.
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_accessor :socket
         
     | 
| 
      
 7 
     | 
    
         
            +
                  
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def initialize(socket)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    raise PlatformNotSupported, 'UNIX domain sockets not available on Windows' if Thin.win?
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @socket = socket
         
     | 
| 
      
 11 
     | 
    
         
            +
                    super()
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                  
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # Connect the server
         
     | 
| 
      
 15 
     | 
    
         
            +
                  def connect
         
     | 
| 
      
 16 
     | 
    
         
            +
                    at_exit { remove_socket_file } # In case it crashes
         
     | 
| 
      
 17 
     | 
    
         
            +
                    EventMachine.start_unix_domain_server(@socket, UnixConnection, &method(:initialize_connection))
         
     | 
| 
      
 18 
     | 
    
         
            +
                    # HACK EventMachine.start_unix_domain_server doesn't return the connection signature
         
     | 
| 
      
 19 
     | 
    
         
            +
                    #      so we have to go in the internal stuff to find it.
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @signature = EventMachine.instance_eval{@acceptors.keys.first}
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # Stops the server
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def disconnect
         
     | 
| 
      
 25 
     | 
    
         
            +
                    EventMachine.stop_server(@signature)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # Free up resources used by the backend.
         
     | 
| 
      
 29 
     | 
    
         
            +
                  def close
         
     | 
| 
      
 30 
     | 
    
         
            +
                    remove_socket_file
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def to_s
         
     | 
| 
      
 34 
     | 
    
         
            +
                    @socket
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  
         
     | 
| 
      
 37 
     | 
    
         
            +
                  protected
         
     | 
| 
      
 38 
     | 
    
         
            +
                    def remove_socket_file
         
     | 
| 
      
 39 
     | 
    
         
            +
                      File.delete(@socket) if @socket && File.exist?(@socket)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
                end    
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              # Connection through a UNIX domain socket.
         
     | 
| 
      
 45 
     | 
    
         
            +
              class UnixConnection < Connection
         
     | 
| 
      
 46 
     | 
    
         
            +
                protected
         
     | 
| 
      
 47 
     | 
    
         
            +
                  def socket_address        
         
     | 
| 
      
 48 
     | 
    
         
            +
                    '127.0.0.1' # Unix domain sockets can only be local
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/thin/command.rb
    ADDED
    
    | 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'open3'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Thin
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Run a command through the +thin+ command-line script.
         
     | 
| 
      
 5 
     | 
    
         
            +
              class Command
         
     | 
| 
      
 6 
     | 
    
         
            +
                include Logging
         
     | 
| 
      
 7 
     | 
    
         
            +
                
         
     | 
| 
      
 8 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # Path to the +thin+ script used to control the servers.
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # Leave this to default to use the one in the path.
         
     | 
| 
      
 11 
     | 
    
         
            +
                  attr_accessor :script
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
                
         
     | 
| 
      
 14 
     | 
    
         
            +
                def initialize(name, options={})
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @name    = name
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
                
         
     | 
| 
      
 19 
     | 
    
         
            +
                def self.run(*args)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  new(*args).run
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
                
         
     | 
| 
      
 23 
     | 
    
         
            +
                # Send the command to the +thin+ script
         
     | 
| 
      
 24 
     | 
    
         
            +
                def run
         
     | 
| 
      
 25 
     | 
    
         
            +
                  shell_cmd = shellify
         
     | 
| 
      
 26 
     | 
    
         
            +
                  trace shell_cmd
         
     | 
| 
      
 27 
     | 
    
         
            +
                  trap('INT') {} # Ignore INT signal to pass CTRL+C to subprocess
         
     | 
| 
      
 28 
     | 
    
         
            +
                  Open3.popen3(shell_cmd) do |stdin, stdout, stderr|
         
     | 
| 
      
 29 
     | 
    
         
            +
                    log stdout.gets until stdout.eof?
         
     | 
| 
      
 30 
     | 
    
         
            +
                    log stderr.gets until stderr.eof?
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                
         
     | 
| 
      
 34 
     | 
    
         
            +
                # Turn into a runnable shell command
         
     | 
| 
      
 35 
     | 
    
         
            +
                def shellify
         
     | 
| 
      
 36 
     | 
    
         
            +
                  shellified_options = @options.inject([]) do |args, (name, value)|
         
     | 
| 
      
 37 
     | 
    
         
            +
                    option_name = name.to_s.tr("_", "-")
         
     | 
| 
      
 38 
     | 
    
         
            +
                    case value
         
     | 
| 
      
 39 
     | 
    
         
            +
                    when NilClass,
         
     | 
| 
      
 40 
     | 
    
         
            +
                         TrueClass then args << "--#{option_name}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                    when FalseClass
         
     | 
| 
      
 42 
     | 
    
         
            +
                    when Array     then value.each { |v| args << "--#{option_name}=#{v.inspect}" }
         
     | 
| 
      
 43 
     | 
    
         
            +
                    else                args << "--#{option_name}=#{value.inspect}"
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
                    args
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  
         
     | 
| 
      
 48 
     | 
    
         
            +
                  raise ArgumentError, "Path to thin script can't be found, set Command.script" unless self.class.script
         
     | 
| 
      
 49 
     | 
    
         
            +
                  
         
     | 
| 
      
 50 
     | 
    
         
            +
                  "#{self.class.script} #{@name} #{shellified_options.compact.join(' ')}"
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,222 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'socket'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Thin
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Connection between the server and client.
         
     | 
| 
      
 5 
     | 
    
         
            +
              # This class is instanciated by EventMachine on each new connection
         
     | 
| 
      
 6 
     | 
    
         
            +
              # that is opened.
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Connection < EventMachine::Connection
         
     | 
| 
      
 8 
     | 
    
         
            +
                CONTENT_LENGTH    = 'Content-Length'.freeze
         
     | 
| 
      
 9 
     | 
    
         
            +
                TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
                CHUNKED_REGEXP    = /\bchunked\b/i.freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                include Logging
         
     | 
| 
      
 13 
     | 
    
         
            +
                
         
     | 
| 
      
 14 
     | 
    
         
            +
                # This is a template async response. N.B. Can't use string for body on 1.9
         
     | 
| 
      
 15 
     | 
    
         
            +
                AsyncResponse = [-1, {}, []].freeze
         
     | 
| 
      
 16 
     | 
    
         
            +
                
         
     | 
| 
      
 17 
     | 
    
         
            +
                # Rack application (adapter) served by this connection.
         
     | 
| 
      
 18 
     | 
    
         
            +
                attr_accessor :app
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                # Backend to the server
         
     | 
| 
      
 21 
     | 
    
         
            +
                attr_accessor :backend
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                # Current request served by the connection
         
     | 
| 
      
 24 
     | 
    
         
            +
                attr_accessor :request
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                # Next response sent through the connection
         
     | 
| 
      
 27 
     | 
    
         
            +
                attr_accessor :response
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                # Calling the application in a threaded allowing
         
     | 
| 
      
 30 
     | 
    
         
            +
                # concurrent processing of requests.
         
     | 
| 
      
 31 
     | 
    
         
            +
                attr_writer :threaded
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                # Get the connection ready to process a request.
         
     | 
| 
      
 34 
     | 
    
         
            +
                def post_init
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @request  = Request.new
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @response = Response.new
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                # Called when data is received from the client.
         
     | 
| 
      
 40 
     | 
    
         
            +
                def receive_data(data)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  trace { data }
         
     | 
| 
      
 42 
     | 
    
         
            +
                  process if @request.parse(data)
         
     | 
| 
      
 43 
     | 
    
         
            +
                rescue InvalidRequest => e
         
     | 
| 
      
 44 
     | 
    
         
            +
                  log "!! Invalid request"
         
     | 
| 
      
 45 
     | 
    
         
            +
                  log_error e
         
     | 
| 
      
 46 
     | 
    
         
            +
                  close_connection
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                # Called when all data was received and the request
         
     | 
| 
      
 50 
     | 
    
         
            +
                # is ready to be processed.
         
     | 
| 
      
 51 
     | 
    
         
            +
                def process
         
     | 
| 
      
 52 
     | 
    
         
            +
                  if threaded?
         
     | 
| 
      
 53 
     | 
    
         
            +
                    @request.threaded = true
         
     | 
| 
      
 54 
     | 
    
         
            +
                    EventMachine.defer(method(:pre_process), method(:post_process))
         
     | 
| 
      
 55 
     | 
    
         
            +
                  else
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @request.threaded = false
         
     | 
| 
      
 57 
     | 
    
         
            +
                    post_process(pre_process)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                def pre_process
         
     | 
| 
      
 62 
     | 
    
         
            +
                  # Add client info to the request env
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @request.remote_address = remote_address
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  # Connection may be closed unless the App#call response was a [-1, ...]
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # It should be noted that connection objects will linger until this 
         
     | 
| 
      
 67 
     | 
    
         
            +
                  # callback is no longer referenced, so be tidy!
         
     | 
| 
      
 68 
     | 
    
         
            +
                  @request.async_callback = method(:post_process)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # When we're under a non-async framework like rails, we can still spawn
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # off async responses using the callback info, so there's little point
         
     | 
| 
      
 72 
     | 
    
         
            +
                  # in removing this.
         
     | 
| 
      
 73 
     | 
    
         
            +
                  response = AsyncResponse
         
     | 
| 
      
 74 
     | 
    
         
            +
                  catch(:async) do
         
     | 
| 
      
 75 
     | 
    
         
            +
                    # Process the request calling the Rack adapter
         
     | 
| 
      
 76 
     | 
    
         
            +
                    response = @app.call(@request.env)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                  response
         
     | 
| 
      
 79 
     | 
    
         
            +
                rescue Exception
         
     | 
| 
      
 80 
     | 
    
         
            +
                  handle_error
         
     | 
| 
      
 81 
     | 
    
         
            +
                  terminate_request
         
     | 
| 
      
 82 
     | 
    
         
            +
                  nil # Signal to post_process that the request could not be processed
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                def post_process(result)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  return unless result
         
     | 
| 
      
 87 
     | 
    
         
            +
                  result = result.to_a
         
     | 
| 
      
 88 
     | 
    
         
            +
                  
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # Status code -1 indicates that we're going to respond later (async).
         
     | 
| 
      
 90 
     | 
    
         
            +
                  return if result.first == AsyncResponse.first
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  # Set the Content-Length header if possible
         
     | 
| 
      
 93 
     | 
    
         
            +
                  set_content_length(result) if need_content_length?(result)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @response.status, @response.headers, @response.body = *result
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  log "!! Rack application returned nil body. Probably you wanted it to be an empty string?" if @response.body.nil?
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  # Make the response persistent if requested by the client
         
     | 
| 
      
 100 
     | 
    
         
            +
                  @response.persistent! if @request.persistent?
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                  # Send the response
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @response.each do |chunk|
         
     | 
| 
      
 104 
     | 
    
         
            +
                    trace { chunk }
         
     | 
| 
      
 105 
     | 
    
         
            +
                    send_data chunk
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                rescue Exception
         
     | 
| 
      
 109 
     | 
    
         
            +
                  handle_error
         
     | 
| 
      
 110 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 111 
     | 
    
         
            +
                  # If the body is being deferred, then terminate afterward.
         
     | 
| 
      
 112 
     | 
    
         
            +
                  if @response.body.respond_to?(:callback) && @response.body.respond_to?(:errback)
         
     | 
| 
      
 113 
     | 
    
         
            +
                    @response.body.callback { terminate_request }
         
     | 
| 
      
 114 
     | 
    
         
            +
                    @response.body.errback  { terminate_request }
         
     | 
| 
      
 115 
     | 
    
         
            +
                  else
         
     | 
| 
      
 116 
     | 
    
         
            +
                    # Don't terminate the response if we're going async.
         
     | 
| 
      
 117 
     | 
    
         
            +
                    terminate_request unless result && result.first == AsyncResponse.first
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                # Logs catched exception and closes the connection.
         
     | 
| 
      
 122 
     | 
    
         
            +
                def handle_error
         
     | 
| 
      
 123 
     | 
    
         
            +
                  log "!! Unexpected error while processing request: #{$!.message}"
         
     | 
| 
      
 124 
     | 
    
         
            +
                  log_error
         
     | 
| 
      
 125 
     | 
    
         
            +
                  close_connection rescue nil
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                def close_request_response
         
     | 
| 
      
 129 
     | 
    
         
            +
                  @request.async_close.succeed if @request.async_close
         
     | 
| 
      
 130 
     | 
    
         
            +
                  @request.close  rescue nil
         
     | 
| 
      
 131 
     | 
    
         
            +
                  @response.close rescue nil
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                # Does request and response cleanup (closes open IO streams and
         
     | 
| 
      
 135 
     | 
    
         
            +
                # deletes created temporary files).
         
     | 
| 
      
 136 
     | 
    
         
            +
                # Re-initializes response and request if client supports persistent
         
     | 
| 
      
 137 
     | 
    
         
            +
                # connection.
         
     | 
| 
      
 138 
     | 
    
         
            +
                def terminate_request
         
     | 
| 
      
 139 
     | 
    
         
            +
                  unless persistent?
         
     | 
| 
      
 140 
     | 
    
         
            +
                    close_connection_after_writing rescue nil
         
     | 
| 
      
 141 
     | 
    
         
            +
                    close_request_response
         
     | 
| 
      
 142 
     | 
    
         
            +
                  else
         
     | 
| 
      
 143 
     | 
    
         
            +
                    close_request_response
         
     | 
| 
      
 144 
     | 
    
         
            +
                    # Prepare the connection for another request if the client
         
     | 
| 
      
 145 
     | 
    
         
            +
                    # supports HTTP pipelining (persistent connection).
         
     | 
| 
      
 146 
     | 
    
         
            +
                    post_init
         
     | 
| 
      
 147 
     | 
    
         
            +
                  end
         
     | 
| 
      
 148 
     | 
    
         
            +
                end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                # Called when the connection is unbinded from the socket
         
     | 
| 
      
 151 
     | 
    
         
            +
                # and can no longer be used to process requests.
         
     | 
| 
      
 152 
     | 
    
         
            +
                def unbind
         
     | 
| 
      
 153 
     | 
    
         
            +
                  @request.async_close.succeed if @request.async_close
         
     | 
| 
      
 154 
     | 
    
         
            +
                  @response.body.fail if @response.body.respond_to?(:fail)
         
     | 
| 
      
 155 
     | 
    
         
            +
                  @backend.connection_finished(self)
         
     | 
| 
      
 156 
     | 
    
         
            +
                end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                # Allows this connection to be persistent.
         
     | 
| 
      
 159 
     | 
    
         
            +
                def can_persist!
         
     | 
| 
      
 160 
     | 
    
         
            +
                  @can_persist = true
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                # Return +true+ if this connection is allowed to stay open and be persistent.
         
     | 
| 
      
 164 
     | 
    
         
            +
                def can_persist?
         
     | 
| 
      
 165 
     | 
    
         
            +
                  @can_persist
         
     | 
| 
      
 166 
     | 
    
         
            +
                end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                # Return +true+ if the connection must be left open
         
     | 
| 
      
 169 
     | 
    
         
            +
                # and ready to be reused for another request.
         
     | 
| 
      
 170 
     | 
    
         
            +
                def persistent?
         
     | 
| 
      
 171 
     | 
    
         
            +
                  @can_persist && @response.persistent?
         
     | 
| 
      
 172 
     | 
    
         
            +
                end
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                # +true+ if <tt>app.call</tt> will be called inside a thread.
         
     | 
| 
      
 175 
     | 
    
         
            +
                # You can set all requests as threaded setting <tt>Connection#threaded=true</tt>
         
     | 
| 
      
 176 
     | 
    
         
            +
                # or on a per-request case returning +true+ in <tt>app.deferred?</tt>.
         
     | 
| 
      
 177 
     | 
    
         
            +
                def threaded?
         
     | 
| 
      
 178 
     | 
    
         
            +
                  @threaded || (@app.respond_to?(:deferred?) && @app.deferred?(@request.env))
         
     | 
| 
      
 179 
     | 
    
         
            +
                end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                # IP Address of the remote client.
         
     | 
| 
      
 182 
     | 
    
         
            +
                def remote_address
         
     | 
| 
      
 183 
     | 
    
         
            +
                  socket_address
         
     | 
| 
      
 184 
     | 
    
         
            +
                rescue Exception
         
     | 
| 
      
 185 
     | 
    
         
            +
                  log_error
         
     | 
| 
      
 186 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 187 
     | 
    
         
            +
                end
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                protected
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                  # Returns IP address of peer as a string.
         
     | 
| 
      
 192 
     | 
    
         
            +
                  def socket_address
         
     | 
| 
      
 193 
     | 
    
         
            +
                    Socket.unpack_sockaddr_in(get_peername)[1]
         
     | 
| 
      
 194 
     | 
    
         
            +
                  end
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                private
         
     | 
| 
      
 197 
     | 
    
         
            +
                  def need_content_length?(result)
         
     | 
| 
      
 198 
     | 
    
         
            +
                    status, headers, body = result
         
     | 
| 
      
 199 
     | 
    
         
            +
                    return false if status == -1
         
     | 
| 
      
 200 
     | 
    
         
            +
                    return false if headers.has_key?(CONTENT_LENGTH)
         
     | 
| 
      
 201 
     | 
    
         
            +
                    return false if (100..199).include?(status) || status == 204 || status == 304
         
     | 
| 
      
 202 
     | 
    
         
            +
                    return false if headers.has_key?(TRANSFER_ENCODING) && headers[TRANSFER_ENCODING] =~ CHUNKED_REGEXP
         
     | 
| 
      
 203 
     | 
    
         
            +
                    return false unless body.kind_of?(String) || body.kind_of?(Array)
         
     | 
| 
      
 204 
     | 
    
         
            +
                    true
         
     | 
| 
      
 205 
     | 
    
         
            +
                  end
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
                  def set_content_length(result)
         
     | 
| 
      
 208 
     | 
    
         
            +
                    headers, body = result[1..2]
         
     | 
| 
      
 209 
     | 
    
         
            +
                    case body
         
     | 
| 
      
 210 
     | 
    
         
            +
                    when String
         
     | 
| 
      
 211 
     | 
    
         
            +
                      # See http://redmine.ruby-lang.org/issues/show/203
         
     | 
| 
      
 212 
     | 
    
         
            +
                      headers[CONTENT_LENGTH] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
         
     | 
| 
      
 213 
     | 
    
         
            +
                    when Array
         
     | 
| 
      
 214 
     | 
    
         
            +
                       bytes = 0
         
     | 
| 
      
 215 
     | 
    
         
            +
                       body.each do |p|
         
     | 
| 
      
 216 
     | 
    
         
            +
                         bytes += p.respond_to?(:bytesize) ? p.bytesize : p.size
         
     | 
| 
      
 217 
     | 
    
         
            +
                       end
         
     | 
| 
      
 218 
     | 
    
         
            +
                       headers[CONTENT_LENGTH] = bytes.to_s
         
     | 
| 
      
 219 
     | 
    
         
            +
                    end
         
     | 
| 
      
 220 
     | 
    
         
            +
                  end
         
     | 
| 
      
 221 
     | 
    
         
            +
              end
         
     | 
| 
      
 222 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,178 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'socket'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Thin
         
     | 
| 
      
 4 
     | 
    
         
            +
              # An exception class to handle the event that server didn't start on time
         
     | 
| 
      
 5 
     | 
    
         
            +
              class RestartTimeout < RuntimeError; end
         
     | 
| 
      
 6 
     | 
    
         
            +
              
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Controllers
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Control a set of servers.
         
     | 
| 
      
 9 
     | 
    
         
            +
                # * Generate start and stop commands and run them.
         
     | 
| 
      
 10 
     | 
    
         
            +
                # * Inject the port or socket number in the pid and log filenames.
         
     | 
| 
      
 11 
     | 
    
         
            +
                # Servers are started throught the +thin+ command-line script.
         
     | 
| 
      
 12 
     | 
    
         
            +
                class Cluster < Controller
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # Cluster only options that should not be passed in the command sent
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # to the indiviual servers.
         
     | 
| 
      
 15 
     | 
    
         
            +
                  CLUSTER_OPTIONS = [:servers, :only, :onebyone, :wait]
         
     | 
| 
      
 16 
     | 
    
         
            +
                  
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # Maximum wait time for the server to be restarted
         
     | 
| 
      
 18 
     | 
    
         
            +
                  DEFAULT_WAIT_TIME = 30    # seconds
         
     | 
| 
      
 19 
     | 
    
         
            +
                  
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Create a new cluster of servers launched using +options+.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def initialize(options)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    super
         
     | 
| 
      
 23 
     | 
    
         
            +
                    # Cluster can only contain daemonized servers
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @options.merge!(:daemonize => true)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def first_port; @options[:port]     end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  def address;    @options[:address]  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                  def socket;     @options[:socket]   end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def pid_file;   @options[:pid]      end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def log_file;   @options[:log]      end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  def size;       @options[:servers]  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def only;       @options[:only]     end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  def onebyone;   @options[:onebyone] end
         
     | 
| 
      
 35 
     | 
    
         
            +
                  def wait;       @options[:wait]     end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  
         
     | 
| 
      
 37 
     | 
    
         
            +
                  def swiftiply?
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @options.has_key?(:swiftiply)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # Start the servers
         
     | 
| 
      
 42 
     | 
    
         
            +
                  def start
         
     | 
| 
      
 43 
     | 
    
         
            +
                    with_each_server { |n| start_server n }
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # Start a single server
         
     | 
| 
      
 47 
     | 
    
         
            +
                  def start_server(number)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    log "Starting server on #{server_id(number)} ... "
         
     | 
| 
      
 49 
     | 
    
         
            +
                  
         
     | 
| 
      
 50 
     | 
    
         
            +
                    run :start, number
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
              
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # Stop the servers
         
     | 
| 
      
 54 
     | 
    
         
            +
                  def stop
         
     | 
| 
      
 55 
     | 
    
         
            +
                    with_each_server { |n| stop_server n }
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # Stop a single server
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def stop_server(number)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    log "Stopping server on #{server_id(number)} ... "
         
     | 
| 
      
 61 
     | 
    
         
            +
                  
         
     | 
| 
      
 62 
     | 
    
         
            +
                    run :stop, number
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                
         
     | 
| 
      
 65 
     | 
    
         
            +
                  # Stop and start the servers.
         
     | 
| 
      
 66 
     | 
    
         
            +
                  def restart
         
     | 
| 
      
 67 
     | 
    
         
            +
                    unless onebyone
         
     | 
| 
      
 68 
     | 
    
         
            +
                      # Let's do a normal restart by defaults
         
     | 
| 
      
 69 
     | 
    
         
            +
                      stop
         
     | 
| 
      
 70 
     | 
    
         
            +
                      sleep 0.1 # Let's breath a bit shall we ?
         
     | 
| 
      
 71 
     | 
    
         
            +
                      start
         
     | 
| 
      
 72 
     | 
    
         
            +
                    else
         
     | 
| 
      
 73 
     | 
    
         
            +
                      with_each_server do |n| 
         
     | 
| 
      
 74 
     | 
    
         
            +
                        stop_server(n)
         
     | 
| 
      
 75 
     | 
    
         
            +
                        sleep 0.1 # Let's breath a bit shall we ?
         
     | 
| 
      
 76 
     | 
    
         
            +
                        start_server(n)
         
     | 
| 
      
 77 
     | 
    
         
            +
                        wait_until_server_started(n)
         
     | 
| 
      
 78 
     | 
    
         
            +
                      end
         
     | 
| 
      
 79 
     | 
    
         
            +
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                  
         
     | 
| 
      
 82 
     | 
    
         
            +
                  def test_socket(number)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    if socket
         
     | 
| 
      
 84 
     | 
    
         
            +
                      UNIXSocket.new(socket_for(number))
         
     | 
| 
      
 85 
     | 
    
         
            +
                    else
         
     | 
| 
      
 86 
     | 
    
         
            +
                      TCPSocket.new(address, number)
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 89 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
                  
         
     | 
| 
      
 92 
     | 
    
         
            +
                  # Make sure the server is running before moving on to the next one.
         
     | 
| 
      
 93 
     | 
    
         
            +
                  def wait_until_server_started(number)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    log "Waiting for server to start ..."
         
     | 
| 
      
 95 
     | 
    
         
            +
                    STDOUT.flush # Need this to make sure user got the message
         
     | 
| 
      
 96 
     | 
    
         
            +
                    
         
     | 
| 
      
 97 
     | 
    
         
            +
                    tries = 0
         
     | 
| 
      
 98 
     | 
    
         
            +
                    loop do
         
     | 
| 
      
 99 
     | 
    
         
            +
                      if test_socket = test_socket(number)
         
     | 
| 
      
 100 
     | 
    
         
            +
                        test_socket.close
         
     | 
| 
      
 101 
     | 
    
         
            +
                        break
         
     | 
| 
      
 102 
     | 
    
         
            +
                      elsif tries < wait
         
     | 
| 
      
 103 
     | 
    
         
            +
                        sleep 1
         
     | 
| 
      
 104 
     | 
    
         
            +
                        tries += 1
         
     | 
| 
      
 105 
     | 
    
         
            +
                      else
         
     | 
| 
      
 106 
     | 
    
         
            +
                        raise RestartTimeout, "The server didn't start in time. Please look at server's log file " +
         
     | 
| 
      
 107 
     | 
    
         
            +
                                              "for more information, or set the value of 'wait' in your config " +
         
     | 
| 
      
 108 
     | 
    
         
            +
                                              "file to be higher (defaults: 30)."
         
     | 
| 
      
 109 
     | 
    
         
            +
                      end
         
     | 
| 
      
 110 
     | 
    
         
            +
                    end
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
                
         
     | 
| 
      
 113 
     | 
    
         
            +
                  def server_id(number)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    if socket
         
     | 
| 
      
 115 
     | 
    
         
            +
                      socket_for(number)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    elsif swiftiply?
         
     | 
| 
      
 117 
     | 
    
         
            +
                      [address, first_port, number].join(':')
         
     | 
| 
      
 118 
     | 
    
         
            +
                    else
         
     | 
| 
      
 119 
     | 
    
         
            +
                      [address, number].join(':')
         
     | 
| 
      
 120 
     | 
    
         
            +
                    end
         
     | 
| 
      
 121 
     | 
    
         
            +
                  end
         
     | 
| 
      
 122 
     | 
    
         
            +
                
         
     | 
| 
      
 123 
     | 
    
         
            +
                  def log_file_for(number)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    include_server_number log_file, number
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
                
         
     | 
| 
      
 127 
     | 
    
         
            +
                  def pid_file_for(number)
         
     | 
| 
      
 128 
     | 
    
         
            +
                    include_server_number pid_file, number
         
     | 
| 
      
 129 
     | 
    
         
            +
                  end
         
     | 
| 
      
 130 
     | 
    
         
            +
                
         
     | 
| 
      
 131 
     | 
    
         
            +
                  def socket_for(number)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    include_server_number socket, number
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
      
 134 
     | 
    
         
            +
                
         
     | 
| 
      
 135 
     | 
    
         
            +
                  def pid_for(number)
         
     | 
| 
      
 136 
     | 
    
         
            +
                    File.read(pid_file_for(number)).chomp.to_i
         
     | 
| 
      
 137 
     | 
    
         
            +
                  end
         
     | 
| 
      
 138 
     | 
    
         
            +
                  
         
     | 
| 
      
 139 
     | 
    
         
            +
                  private
         
     | 
| 
      
 140 
     | 
    
         
            +
                    # Send the command to the +thin+ script
         
     | 
| 
      
 141 
     | 
    
         
            +
                    def run(cmd, number)
         
     | 
| 
      
 142 
     | 
    
         
            +
                      cmd_options = @options.reject { |option, value| CLUSTER_OPTIONS.include?(option) }
         
     | 
| 
      
 143 
     | 
    
         
            +
                      cmd_options.merge!(:pid => pid_file_for(number), :log => log_file_for(number))
         
     | 
| 
      
 144 
     | 
    
         
            +
                      if socket
         
     | 
| 
      
 145 
     | 
    
         
            +
                        cmd_options.merge!(:socket => socket_for(number))
         
     | 
| 
      
 146 
     | 
    
         
            +
                      elsif swiftiply?
         
     | 
| 
      
 147 
     | 
    
         
            +
                        cmd_options.merge!(:port => first_port)
         
     | 
| 
      
 148 
     | 
    
         
            +
                      else
         
     | 
| 
      
 149 
     | 
    
         
            +
                        cmd_options.merge!(:port => number)
         
     | 
| 
      
 150 
     | 
    
         
            +
                      end
         
     | 
| 
      
 151 
     | 
    
         
            +
                      Command.run(cmd, cmd_options)
         
     | 
| 
      
 152 
     | 
    
         
            +
                    end
         
     | 
| 
      
 153 
     | 
    
         
            +
                  
         
     | 
| 
      
 154 
     | 
    
         
            +
                    def with_each_server
         
     | 
| 
      
 155 
     | 
    
         
            +
                      if only
         
     | 
| 
      
 156 
     | 
    
         
            +
                        if first_port && only < 80
         
     | 
| 
      
 157 
     | 
    
         
            +
                          # interpret +only+ as a sequence number
         
     | 
| 
      
 158 
     | 
    
         
            +
                          yield first_port + only
         
     | 
| 
      
 159 
     | 
    
         
            +
                        else
         
     | 
| 
      
 160 
     | 
    
         
            +
                          # interpret +only+ as an absolute port number
         
     | 
| 
      
 161 
     | 
    
         
            +
                          yield only
         
     | 
| 
      
 162 
     | 
    
         
            +
                        end
         
     | 
| 
      
 163 
     | 
    
         
            +
                      elsif socket || swiftiply?
         
     | 
| 
      
 164 
     | 
    
         
            +
                        size.times { |n| yield n }
         
     | 
| 
      
 165 
     | 
    
         
            +
                      else
         
     | 
| 
      
 166 
     | 
    
         
            +
                        size.times { |n| yield first_port + n }
         
     | 
| 
      
 167 
     | 
    
         
            +
                      end
         
     | 
| 
      
 168 
     | 
    
         
            +
                    end
         
     | 
| 
      
 169 
     | 
    
         
            +
                  
         
     | 
| 
      
 170 
     | 
    
         
            +
                    # Add the server port or number in the filename
         
     | 
| 
      
 171 
     | 
    
         
            +
                    # so each instance get its own file
         
     | 
| 
      
 172 
     | 
    
         
            +
                    def include_server_number(path, number)
         
     | 
| 
      
 173 
     | 
    
         
            +
                      ext = File.extname(path)
         
     | 
| 
      
 174 
     | 
    
         
            +
                      path.gsub(/#{ext}$/, ".#{number}#{ext}")
         
     | 
| 
      
 175 
     | 
    
         
            +
                    end
         
     | 
| 
      
 176 
     | 
    
         
            +
                end
         
     | 
| 
      
 177 
     | 
    
         
            +
              end
         
     | 
| 
      
 178 
     | 
    
         
            +
            end
         
     |