macournoyer-thin 1.0.1

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.
Files changed (135) hide show
  1. data/CHANGELOG +233 -0
  2. data/COPYING +18 -0
  3. data/README +77 -0
  4. data/Rakefile +13 -0
  5. data/benchmark/abc +51 -0
  6. data/benchmark/benchmarker.rb +80 -0
  7. data/benchmark/runner +79 -0
  8. data/bin/thin +6 -0
  9. data/example/adapter.rb +32 -0
  10. data/example/config.ru +23 -0
  11. data/example/monit_sockets +20 -0
  12. data/example/monit_unixsock +20 -0
  13. data/example/myapp.rb +1 -0
  14. data/example/ramaze.ru +12 -0
  15. data/example/thin.god +80 -0
  16. data/example/thin_solaris_smf.erb +36 -0
  17. data/example/thin_solaris_smf.readme.txt +150 -0
  18. data/example/vlad.rake +64 -0
  19. data/ext/thin_parser/common.rl +55 -0
  20. data/ext/thin_parser/ext_help.h +14 -0
  21. data/ext/thin_parser/extconf.rb +6 -0
  22. data/ext/thin_parser/parser.c +452 -0
  23. data/ext/thin_parser/parser.h +49 -0
  24. data/ext/thin_parser/parser.rl +157 -0
  25. data/ext/thin_parser/thin.c +433 -0
  26. data/lib/rack/adapter/loader.rb +79 -0
  27. data/lib/rack/adapter/rails.rb +173 -0
  28. data/lib/rack/handler/thin.rb +18 -0
  29. data/lib/thin.rb +50 -0
  30. data/lib/thin/backends/base.rb +141 -0
  31. data/lib/thin/backends/swiftiply_client.rb +56 -0
  32. data/lib/thin/backends/tcp_server.rb +29 -0
  33. data/lib/thin/backends/unix_server.rb +51 -0
  34. data/lib/thin/command.rb +52 -0
  35. data/lib/thin/connection.rb +186 -0
  36. data/lib/thin/controllers/cluster.rb +127 -0
  37. data/lib/thin/controllers/controller.rb +183 -0
  38. data/lib/thin/controllers/service.rb +75 -0
  39. data/lib/thin/controllers/service.sh.erb +39 -0
  40. data/lib/thin/daemonizing.rb +163 -0
  41. data/lib/thin/headers.rb +39 -0
  42. data/lib/thin/logging.rb +54 -0
  43. data/lib/thin/request.rb +147 -0
  44. data/lib/thin/response.rb +99 -0
  45. data/lib/thin/runner.rb +208 -0
  46. data/lib/thin/server.rb +241 -0
  47. data/lib/thin/stats.html.erb +216 -0
  48. data/lib/thin/stats.rb +52 -0
  49. data/lib/thin/statuses.rb +43 -0
  50. data/lib/thin/version.rb +32 -0
  51. data/spec/backends/swiftiply_client_spec.rb +66 -0
  52. data/spec/backends/tcp_server_spec.rb +33 -0
  53. data/spec/backends/unix_server_spec.rb +37 -0
  54. data/spec/command_spec.rb +20 -0
  55. data/spec/configs/cluster.yml +9 -0
  56. data/spec/configs/single.yml +9 -0
  57. data/spec/connection_spec.rb +105 -0
  58. data/spec/controllers/cluster_spec.rb +212 -0
  59. data/spec/controllers/controller_spec.rb +129 -0
  60. data/spec/controllers/service_spec.rb +50 -0
  61. data/spec/daemonizing_spec.rb +192 -0
  62. data/spec/headers_spec.rb +40 -0
  63. data/spec/logging_spec.rb +46 -0
  64. data/spec/perf/request_perf_spec.rb +50 -0
  65. data/spec/perf/response_perf_spec.rb +19 -0
  66. data/spec/perf/server_perf_spec.rb +39 -0
  67. data/spec/rack/loader_spec.rb +29 -0
  68. data/spec/rack/rails_adapter_spec.rb +106 -0
  69. data/spec/rails_app/app/controllers/application.rb +10 -0
  70. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  71. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  72. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  73. data/spec/rails_app/config/boot.rb +109 -0
  74. data/spec/rails_app/config/environment.rb +64 -0
  75. data/spec/rails_app/config/environments/development.rb +18 -0
  76. data/spec/rails_app/config/environments/production.rb +19 -0
  77. data/spec/rails_app/config/environments/test.rb +22 -0
  78. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  79. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  80. data/spec/rails_app/config/routes.rb +35 -0
  81. data/spec/rails_app/public/404.html +30 -0
  82. data/spec/rails_app/public/422.html +30 -0
  83. data/spec/rails_app/public/500.html +30 -0
  84. data/spec/rails_app/public/dispatch.cgi +10 -0
  85. data/spec/rails_app/public/dispatch.fcgi +24 -0
  86. data/spec/rails_app/public/dispatch.rb +10 -0
  87. data/spec/rails_app/public/favicon.ico +0 -0
  88. data/spec/rails_app/public/images/rails.png +0 -0
  89. data/spec/rails_app/public/index.html +277 -0
  90. data/spec/rails_app/public/javascripts/application.js +2 -0
  91. data/spec/rails_app/public/javascripts/controls.js +963 -0
  92. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  93. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  94. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  95. data/spec/rails_app/public/robots.txt +5 -0
  96. data/spec/rails_app/script/about +3 -0
  97. data/spec/rails_app/script/console +3 -0
  98. data/spec/rails_app/script/destroy +3 -0
  99. data/spec/rails_app/script/generate +3 -0
  100. data/spec/rails_app/script/performance/benchmarker +3 -0
  101. data/spec/rails_app/script/performance/profiler +3 -0
  102. data/spec/rails_app/script/performance/request +3 -0
  103. data/spec/rails_app/script/plugin +3 -0
  104. data/spec/rails_app/script/process/inspector +3 -0
  105. data/spec/rails_app/script/process/reaper +3 -0
  106. data/spec/rails_app/script/process/spawner +3 -0
  107. data/spec/rails_app/script/runner +3 -0
  108. data/spec/rails_app/script/server +3 -0
  109. data/spec/request/mongrel_spec.rb +39 -0
  110. data/spec/request/parser_spec.rb +215 -0
  111. data/spec/request/persistent_spec.rb +35 -0
  112. data/spec/request/processing_spec.rb +45 -0
  113. data/spec/response_spec.rb +83 -0
  114. data/spec/runner_spec.rb +167 -0
  115. data/spec/server/builder_spec.rb +44 -0
  116. data/spec/server/pipelining_spec.rb +109 -0
  117. data/spec/server/robustness_spec.rb +34 -0
  118. data/spec/server/stopping_spec.rb +45 -0
  119. data/spec/server/swiftiply.yml +6 -0
  120. data/spec/server/swiftiply_spec.rb +32 -0
  121. data/spec/server/tcp_spec.rb +57 -0
  122. data/spec/server/threaded_spec.rb +27 -0
  123. data/spec/server/unix_socket_spec.rb +26 -0
  124. data/spec/server_spec.rb +96 -0
  125. data/spec/spec_helper.rb +219 -0
  126. data/tasks/announce.rake +22 -0
  127. data/tasks/deploy.rake +16 -0
  128. data/tasks/email.erb +30 -0
  129. data/tasks/ext.rake +42 -0
  130. data/tasks/gem.rake +108 -0
  131. data/tasks/rdoc.rake +25 -0
  132. data/tasks/site.rake +15 -0
  133. data/tasks/spec.rake +48 -0
  134. data/tasks/stats.rake +28 -0
  135. metadata +248 -0
data/lib/thin/stats.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'erb'
2
+
3
+ module Thin
4
+ module Stats
5
+ # Rack adapter to log stats about a Rack application.
6
+ class Adapter
7
+ include ERB::Util
8
+
9
+ def initialize(app, path='/stats')
10
+ @app = app
11
+ @path = path
12
+
13
+ @template = ERB.new(File.read(File.dirname(__FILE__) + '/stats.html.erb'))
14
+
15
+ @requests = 0
16
+ @requests_finished = 0
17
+ @start_time = Time.now
18
+ end
19
+
20
+ def call(env)
21
+ if env['PATH_INFO'].index(@path) == 0
22
+ serve(env)
23
+ else
24
+ log(env) { @app.call(env) }
25
+ end
26
+ end
27
+
28
+ def log(env)
29
+ @requests += 1
30
+ @last_request = Rack::Request.new(env)
31
+ request_started_at = Time.now
32
+
33
+ response = yield
34
+
35
+ @requests_finished += 1
36
+ @last_request_time = Time.now - request_started_at
37
+
38
+ response
39
+ end
40
+
41
+ def serve(env)
42
+ body = @template.result(binding)
43
+
44
+ [
45
+ 200,
46
+ { 'Content-Type' => 'text/html' },
47
+ [body]
48
+ ]
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,43 @@
1
+ module Thin
2
+ # Every standard HTTP code mapped to the appropriate message.
3
+ # Stolent from Mongrel.
4
+ HTTP_STATUS_CODES = {
5
+ 100 => 'Continue',
6
+ 101 => 'Switching Protocols',
7
+ 200 => 'OK',
8
+ 201 => 'Created',
9
+ 202 => 'Accepted',
10
+ 203 => 'Non-Authoritative Information',
11
+ 204 => 'No Content',
12
+ 205 => 'Reset Content',
13
+ 206 => 'Partial Content',
14
+ 300 => 'Multiple Choices',
15
+ 301 => 'Moved Permanently',
16
+ 302 => 'Moved Temporarily',
17
+ 303 => 'See Other',
18
+ 304 => 'Not Modified',
19
+ 305 => 'Use Proxy',
20
+ 400 => 'Bad Request',
21
+ 401 => 'Unauthorized',
22
+ 402 => 'Payment Required',
23
+ 403 => 'Forbidden',
24
+ 404 => 'Not Found',
25
+ 405 => 'Method Not Allowed',
26
+ 406 => 'Not Acceptable',
27
+ 407 => 'Proxy Authentication Required',
28
+ 408 => 'Request Time-out',
29
+ 409 => 'Conflict',
30
+ 410 => 'Gone',
31
+ 411 => 'Length Required',
32
+ 412 => 'Precondition Failed',
33
+ 413 => 'Request Entity Too Large',
34
+ 414 => 'Request-URI Too Large',
35
+ 415 => 'Unsupported Media Type',
36
+ 500 => 'Internal Server Error',
37
+ 501 => 'Not Implemented',
38
+ 502 => 'Bad Gateway',
39
+ 503 => 'Service Unavailable',
40
+ 504 => 'Gateway Time-out',
41
+ 505 => 'HTTP Version not supported'
42
+ }
43
+ end
@@ -0,0 +1,32 @@
1
+ module Thin
2
+ # Raised when a feature is not supported on the
3
+ # current platform.
4
+ class PlatformNotSupported < RuntimeError; end
5
+
6
+ module VERSION #:nodoc:
7
+ MAJOR = 1
8
+ MINOR = 0
9
+ TINY = 1
10
+
11
+ STRING = [MAJOR, MINOR, TINY].join('.')
12
+
13
+ CODENAME = "?".freeze
14
+
15
+ RACK = [0, 3].freeze # Latest Rack version that was tested
16
+ end
17
+
18
+ NAME = 'thin'.freeze
19
+ SERVER = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze
20
+
21
+ def self.win?
22
+ RUBY_PLATFORM =~ /mswin/
23
+ end
24
+
25
+ def self.linux?
26
+ RUBY_PLATFORM =~ /linux/
27
+ end
28
+
29
+ def self.ruby_18?
30
+ RUBY_VERSION =~ /^1\.8/
31
+ end
32
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Backends::SwiftiplyClient do
4
+ before do
5
+ @backend = Backends::SwiftiplyClient.new('0.0.0.0', 3333)
6
+ @backend.server = mock('server', :null_object => true)
7
+ end
8
+
9
+ it "should connect" do
10
+ EventMachine.run do
11
+ @backend.connect
12
+ EventMachine.stop
13
+ end
14
+ end
15
+
16
+ it "should disconnect" do
17
+ EventMachine.run do
18
+ @backend.connect
19
+ @backend.disconnect
20
+ EventMachine.stop
21
+ end
22
+ end
23
+ end
24
+
25
+ describe SwiftiplyConnection do
26
+ before do
27
+ @connection = SwiftiplyConnection.new(nil)
28
+ @connection.backend = Backends::SwiftiplyClient.new('0.0.0.0', 3333)
29
+ @connection.backend.server = mock('server', :null_object => true)
30
+ end
31
+
32
+ it do
33
+ @connection.should be_persistent
34
+ end
35
+
36
+ it "should send handshake on connection_completed" do
37
+ @connection.should_receive(:send_data).with('swiftclient000000000d0500')
38
+ @connection.connection_completed
39
+ end
40
+
41
+ it "should reconnect on unbind" do
42
+ @connection.backend.stub!(:running?).and_return(true)
43
+ @connection.stub!(:rand).and_return(0) # Make sure we don't wait
44
+
45
+ @connection.should_receive(:reconnect).with('0.0.0.0', 3333)
46
+
47
+ EventMachine.run do
48
+ @connection.unbind
49
+ EventMachine.add_timer(0) { EventMachine.stop }
50
+ end
51
+ end
52
+
53
+ it "should not reconnect when not running" do
54
+ @connection.backend.stub!(:running?).and_return(false)
55
+ EventMachine.should_not_receive(:add_timer)
56
+ @connection.unbind
57
+ end
58
+
59
+ it "should have a host_ip" do
60
+ @connection.send(:host_ip).should == [0, 0, 0, 0]
61
+ end
62
+
63
+ it "should generate swiftiply_handshake based on key" do
64
+ @connection.send(:swiftiply_handshake, 'key').should == 'swiftclient000000000d0503key'
65
+ end
66
+ end
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Backends::TcpServer do
4
+ before do
5
+ @backend = Backends::TcpServer.new('0.0.0.0', 3333)
6
+ end
7
+
8
+ it "should not use epoll" do
9
+ @backend.no_epoll = true
10
+ EventMachine.should_not_receive(:epoll)
11
+ @backend.config
12
+ end
13
+
14
+ it "should use epoll" do
15
+ EventMachine.should_receive(:epoll)
16
+ @backend.config
17
+ end
18
+
19
+ it "should connect" do
20
+ EventMachine.run do
21
+ @backend.connect
22
+ EventMachine.stop
23
+ end
24
+ end
25
+
26
+ it "should disconnect" do
27
+ EventMachine.run do
28
+ @backend.connect
29
+ @backend.disconnect
30
+ EventMachine.stop
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Backends::UnixServer do
4
+ before do
5
+ @backend = Backends::UnixServer.new('/tmp/thin-test.sock')
6
+ end
7
+
8
+ it "should connect" do
9
+ EventMachine.run do
10
+ @backend.connect
11
+ EventMachine.stop
12
+ end
13
+ end
14
+
15
+ it "should disconnect" do
16
+ EventMachine.run do
17
+ @backend.connect
18
+ @backend.disconnect
19
+ EventMachine.stop
20
+ end
21
+ end
22
+
23
+ it "should remove socket file on close" do
24
+ @backend.close
25
+ File.exist?('/tmp/thin-test.sock').should be_false
26
+ end
27
+ end
28
+
29
+ describe UnixConnection do
30
+ before do
31
+ @connection = UnixConnection.new(nil)
32
+ end
33
+
34
+ it "should return 127.0.0.1 as remote_address" do
35
+ @connection.remote_address.should == '127.0.0.1'
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Command do
4
+ before do
5
+ Command.script = 'thin'
6
+ @command = Command.new(:start, :port => 3000, :daemonize => true, :log => 'hi.log',
7
+ :require => %w(rubygems thin))
8
+ end
9
+
10
+ it 'should shellify command' do
11
+ out = @command.shellify
12
+ out.should include('--port=3000', '--daemonize', '--log="hi.log"', 'thin start --')
13
+ out.should_not include('--pid')
14
+ end
15
+
16
+ it 'should shellify Array argument to multiple parameters' do
17
+ out = @command.shellify
18
+ out.should include('--require="rubygems"', '--require="thin"')
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ ---
2
+ pid: tmp/pids/thin.pid
3
+ log: log/thin.log
4
+ timeout: 60
5
+ port: 5000
6
+ chdir: spec/rails_app
7
+ environment: production
8
+ servers: 3
9
+ address: 127.0.0.1
@@ -0,0 +1,9 @@
1
+ ---
2
+ pid: tmp/pids/thin.pid
3
+ log: log/thin.log
4
+ timeout: 60
5
+ port: 6000
6
+ chdir: spec/rails_app
7
+ environment: production
8
+ daemonize: true
9
+ address: 127.0.0.1
@@ -0,0 +1,105 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Connection do
4
+ before do
5
+ @connection = Connection.new(mock('EM', :null_object => true))
6
+ @connection.post_init
7
+ @connection.app = proc do |env|
8
+ [200, {}, ['']]
9
+ end
10
+ end
11
+
12
+ it "should parse on receive_data" do
13
+ @connection.request.should_receive(:parse).with('GET')
14
+ @connection.receive_data('GET')
15
+ end
16
+
17
+ it "should close connection on InvalidRequest error in receive_data" do
18
+ @connection.request.stub!(:parse).and_raise(InvalidRequest)
19
+ @connection.should_receive(:close_connection)
20
+ @connection.receive_data('')
21
+ end
22
+
23
+ it "should process when parsing complete" do
24
+ @connection.request.should_receive(:parse).and_return(true)
25
+ @connection.should_receive(:process)
26
+ @connection.receive_data('GET')
27
+ end
28
+
29
+ it "should process" do
30
+ @connection.process
31
+ end
32
+
33
+ it "should rescue error in process" do
34
+ @connection.app.should_receive(:call).and_raise(StandardError)
35
+ @connection.process
36
+ end
37
+
38
+ it "should rescue Timeout error in process" do
39
+ @connection.app.should_receive(:call).and_raise(Timeout::Error.new("timeout error not rescued"))
40
+ @connection.process
41
+ end
42
+
43
+ it "should return HTTP_X_FORWARDED_FOR as remote_address" do
44
+ @connection.request.env['HTTP_X_FORWARDED_FOR'] = '1.2.3.4'
45
+ @connection.remote_address.should == '1.2.3.4'
46
+ end
47
+
48
+ it "should return nil on error retreiving remote_address" do
49
+ @connection.stub!(:get_peername).and_raise(RuntimeError)
50
+ @connection.remote_address.should be_nil
51
+ end
52
+
53
+ it "should return nil on nil get_peername" do
54
+ @connection.stub!(:get_peername).and_return(nil)
55
+ @connection.remote_address.should be_nil
56
+ end
57
+
58
+ it "should return nil on empty get_peername" do
59
+ @connection.stub!(:get_peername).and_return('')
60
+ @connection.remote_address.should be_nil
61
+ end
62
+
63
+ it "should return remote_address" do
64
+ @connection.stub!(:get_peername).and_return(Socket.pack_sockaddr_in(3000, '127.0.0.1'))
65
+ @connection.remote_address.should == '127.0.0.1'
66
+ end
67
+
68
+ it "should not be persistent" do
69
+ @connection.should_not be_persistent
70
+ end
71
+
72
+ it "should be persistent when response is and allowed" do
73
+ @connection.response.stub!(:persistent?).and_return(true)
74
+ @connection.can_persist!
75
+ @connection.should be_persistent
76
+ end
77
+
78
+ it "should not be persistent when response is but not allowed" do
79
+ @connection.response.persistent!
80
+ @connection.should_not be_persistent
81
+ end
82
+
83
+ it "should set request env as rack.multithread" do
84
+ EventMachine.should_receive(:defer)
85
+
86
+ @connection.threaded = true
87
+ @connection.process
88
+
89
+ @connection.request.env["rack.multithread"].should == true
90
+ end
91
+
92
+ it "should set as threaded when app.deferred? is true" do
93
+ @connection.app.should_receive(:deferred?).and_return(true)
94
+ @connection.should be_threaded
95
+ end
96
+
97
+ it "should not set as threaded when app.deferred? is false" do
98
+ @connection.app.should_receive(:deferred?).and_return(false)
99
+ @connection.should_not be_threaded
100
+ end
101
+
102
+ it "should not set as threaded when app do not respond to deferred?" do
103
+ @connection.should_not be_threaded
104
+ end
105
+ end
@@ -0,0 +1,212 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ include Controllers
3
+
4
+ describe Cluster, "with host and port" do
5
+ before do
6
+ @cluster = Cluster.new(:chdir => '/rails_app',
7
+ :address => '0.0.0.0',
8
+ :port => 3000,
9
+ :servers => 3,
10
+ :timeout => 10,
11
+ :log => 'thin.log',
12
+ :pid => 'thin.pid'
13
+ )
14
+ end
15
+
16
+ it 'should include port number in file names' do
17
+ @cluster.send(:include_server_number, 'thin.log', 3000).should == 'thin.3000.log'
18
+ @cluster.send(:include_server_number, 'thin.pid', 3000).should == 'thin.3000.pid'
19
+ end
20
+
21
+ it 'should call each server' do
22
+ calls = []
23
+ @cluster.send(:with_each_server) do |port|
24
+ calls << port
25
+ end
26
+ calls.should == [3000, 3001, 3002]
27
+ end
28
+
29
+ it 'should start on each port' do
30
+ Command.should_receive(:run).with(:start, options_for_port(3000))
31
+ Command.should_receive(:run).with(:start, options_for_port(3001))
32
+ Command.should_receive(:run).with(:start, options_for_port(3002))
33
+
34
+ @cluster.start
35
+ end
36
+
37
+ it 'should stop on each port' do
38
+ Command.should_receive(:run).with(:stop, options_for_port(3000))
39
+ Command.should_receive(:run).with(:stop, options_for_port(3001))
40
+ Command.should_receive(:run).with(:stop, options_for_port(3002))
41
+
42
+ @cluster.stop
43
+ end
44
+
45
+ private
46
+ def options_for_port(port)
47
+ { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
48
+ end
49
+ end
50
+
51
+ describe Cluster, "with UNIX socket" do
52
+ before do
53
+ @cluster = Cluster.new(:chdir => '/rails_app',
54
+ :socket => '/tmp/thin.sock',
55
+ :address => '0.0.0.0',
56
+ :port => 3000,
57
+ :servers => 3,
58
+ :timeout => 10,
59
+ :log => 'thin.log',
60
+ :pid => 'thin.pid'
61
+ )
62
+ end
63
+
64
+ it 'should include socket number in file names' do
65
+ @cluster.send(:include_server_number, 'thin.sock', 0).should == 'thin.0.sock'
66
+ @cluster.send(:include_server_number, 'thin', 0).should == 'thin.0'
67
+ end
68
+
69
+ it "should exclude :address and :port options" do
70
+ @cluster.options.should_not have_key(:address)
71
+ @cluster.options.should_not have_key(:port)
72
+ end
73
+
74
+ it 'should call each server' do
75
+ calls = []
76
+ @cluster.send(:with_each_server) do |n|
77
+ calls << n
78
+ end
79
+ calls.should == [0, 1, 2]
80
+ end
81
+
82
+ it 'should start each server' do
83
+ Command.should_receive(:run).with(:start, options_for_socket(0))
84
+ Command.should_receive(:run).with(:start, options_for_socket(1))
85
+ Command.should_receive(:run).with(:start, options_for_socket(2))
86
+
87
+ @cluster.start
88
+ end
89
+
90
+ it 'should stop each server' do
91
+ Command.should_receive(:run).with(:stop, options_for_socket(0))
92
+ Command.should_receive(:run).with(:stop, options_for_socket(1))
93
+ Command.should_receive(:run).with(:stop, options_for_socket(2))
94
+
95
+ @cluster.stop
96
+ end
97
+
98
+
99
+ private
100
+ def options_for_socket(number)
101
+ { :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :socket => "/tmp/thin.#{number}.sock", :pid => "thin.#{number}.pid", :chdir => "/rails_app" }
102
+ end
103
+ end
104
+
105
+ describe Cluster, "controlling only one server" do
106
+ before do
107
+ @cluster = Cluster.new(:chdir => '/rails_app',
108
+ :address => '0.0.0.0',
109
+ :port => 3000,
110
+ :servers => 3,
111
+ :timeout => 10,
112
+ :log => 'thin.log',
113
+ :pid => 'thin.pid',
114
+ :only => 3001
115
+ )
116
+ end
117
+
118
+ it 'should call only specified server' do
119
+ calls = []
120
+ @cluster.send(:with_each_server) do |n|
121
+ calls << n
122
+ end
123
+ calls.should == [3001]
124
+ end
125
+
126
+ it "should start only specified server" do
127
+ Command.should_receive(:run).with(:start, options_for_port(3001))
128
+
129
+ @cluster.start
130
+ end
131
+
132
+ private
133
+ def options_for_port(port)
134
+ { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
135
+ end
136
+ end
137
+
138
+ describe Cluster, "controlling only one server, by sequence number" do
139
+ before do
140
+ @cluster = Cluster.new(:chdir => '/rails_app',
141
+ :address => '0.0.0.0',
142
+ :port => 3000,
143
+ :servers => 3,
144
+ :timeout => 10,
145
+ :log => 'thin.log',
146
+ :pid => 'thin.pid',
147
+ :only => 1
148
+ )
149
+ end
150
+
151
+ it 'should call only specified server' do
152
+ calls = []
153
+ @cluster.send(:with_each_server) do |n|
154
+ calls << n
155
+ end
156
+ calls.should == [3001]
157
+ end
158
+
159
+ it "should start only specified server" do
160
+ Command.should_receive(:run).with(:start, options_for_port(3001))
161
+
162
+ @cluster.start
163
+ end
164
+
165
+ private
166
+ def options_for_port(port)
167
+ { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
168
+ end
169
+ end
170
+
171
+ describe Cluster, "with Swiftiply" do
172
+ before do
173
+ @cluster = Cluster.new(:chdir => '/rails_app',
174
+ :address => '0.0.0.0',
175
+ :port => 3000,
176
+ :servers => 3,
177
+ :timeout => 10,
178
+ :log => 'thin.log',
179
+ :pid => 'thin.pid',
180
+ :swiftiply => true
181
+ )
182
+ end
183
+
184
+ it 'should call each server' do
185
+ calls = []
186
+ @cluster.send(:with_each_server) do |n|
187
+ calls << n
188
+ end
189
+ calls.should == [0, 1, 2]
190
+ end
191
+
192
+ it 'should start each server' do
193
+ Command.should_receive(:run).with(:start, options_for_swiftiply(0))
194
+ Command.should_receive(:run).with(:start, options_for_swiftiply(1))
195
+ Command.should_receive(:run).with(:start, options_for_swiftiply(2))
196
+
197
+ @cluster.start
198
+ end
199
+
200
+ it 'should stop each server' do
201
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(0))
202
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(1))
203
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(2))
204
+
205
+ @cluster.stop
206
+ end
207
+
208
+ private
209
+ def options_for_swiftiply(number)
210
+ { :address => '0.0.0.0', :port => 3000, :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :pid => "thin.#{number}.pid", :chdir => "/rails_app", :swiftiply => true }
211
+ end
212
+ end