steamcannon-thin 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +288 -0
  2. data/COPYING +18 -0
  3. data/README +69 -0
  4. data/Rakefile +44 -0
  5. data/benchmark/abc +51 -0
  6. data/benchmark/benchmarker.rb +80 -0
  7. data/benchmark/runner +82 -0
  8. data/bin/thin +6 -0
  9. data/example/adapter.rb +32 -0
  10. data/example/async_app.ru +126 -0
  11. data/example/async_chat.ru +247 -0
  12. data/example/async_tailer.ru +100 -0
  13. data/example/config.ru +22 -0
  14. data/example/monit_sockets +20 -0
  15. data/example/monit_unixsock +20 -0
  16. data/example/myapp.rb +1 -0
  17. data/example/ramaze.ru +12 -0
  18. data/example/thin.god +80 -0
  19. data/example/thin_solaris_smf.erb +36 -0
  20. data/example/thin_solaris_smf.readme.txt +150 -0
  21. data/example/vlad.rake +64 -0
  22. data/ext/thin_parser/common.rl +55 -0
  23. data/ext/thin_parser/ext_help.h +14 -0
  24. data/ext/thin_parser/extconf.rb +6 -0
  25. data/ext/thin_parser/parser.c +1249 -0
  26. data/ext/thin_parser/parser.h +49 -0
  27. data/ext/thin_parser/parser.rl +157 -0
  28. data/ext/thin_parser/thin.c +436 -0
  29. data/lib/rack/adapter/loader.rb +91 -0
  30. data/lib/rack/adapter/rails.rb +183 -0
  31. data/lib/thin.rb +56 -0
  32. data/lib/thin/backends/base.rb +149 -0
  33. data/lib/thin/backends/swiftiply_client.rb +56 -0
  34. data/lib/thin/backends/tcp_server.rb +29 -0
  35. data/lib/thin/backends/unix_server.rb +51 -0
  36. data/lib/thin/command.rb +53 -0
  37. data/lib/thin/connection.rb +224 -0
  38. data/lib/thin/controllers/cluster.rb +178 -0
  39. data/lib/thin/controllers/controller.rb +188 -0
  40. data/lib/thin/controllers/service.rb +75 -0
  41. data/lib/thin/controllers/service.sh.erb +39 -0
  42. data/lib/thin/daemonizing.rb +180 -0
  43. data/lib/thin/headers.rb +39 -0
  44. data/lib/thin/logging.rb +54 -0
  45. data/lib/thin/request.rb +156 -0
  46. data/lib/thin/response.rb +101 -0
  47. data/lib/thin/runner.rb +220 -0
  48. data/lib/thin/server.rb +253 -0
  49. data/lib/thin/stats.html.erb +216 -0
  50. data/lib/thin/stats.rb +52 -0
  51. data/lib/thin/statuses.rb +43 -0
  52. data/lib/thin/version.rb +32 -0
  53. data/lib/thin_parser.so +0 -0
  54. data/spec/backends/swiftiply_client_spec.rb +66 -0
  55. data/spec/backends/tcp_server_spec.rb +33 -0
  56. data/spec/backends/unix_server_spec.rb +37 -0
  57. data/spec/command_spec.rb +25 -0
  58. data/spec/configs/cluster.yml +9 -0
  59. data/spec/configs/single.yml +9 -0
  60. data/spec/connection_spec.rb +106 -0
  61. data/spec/controllers/cluster_spec.rb +267 -0
  62. data/spec/controllers/controller_spec.rb +129 -0
  63. data/spec/controllers/service_spec.rb +50 -0
  64. data/spec/daemonizing_spec.rb +196 -0
  65. data/spec/headers_spec.rb +40 -0
  66. data/spec/logging_spec.rb +46 -0
  67. data/spec/perf/request_perf_spec.rb +50 -0
  68. data/spec/perf/response_perf_spec.rb +19 -0
  69. data/spec/perf/server_perf_spec.rb +39 -0
  70. data/spec/rack/loader_spec.rb +42 -0
  71. data/spec/rack/rails_adapter_spec.rb +173 -0
  72. data/spec/rails_app/app/controllers/application.rb +10 -0
  73. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  74. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  75. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  76. data/spec/rails_app/config/boot.rb +109 -0
  77. data/spec/rails_app/config/environment.rb +64 -0
  78. data/spec/rails_app/config/environments/development.rb +18 -0
  79. data/spec/rails_app/config/environments/production.rb +19 -0
  80. data/spec/rails_app/config/environments/test.rb +22 -0
  81. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  82. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  83. data/spec/rails_app/config/routes.rb +35 -0
  84. data/spec/rails_app/public/404.html +30 -0
  85. data/spec/rails_app/public/422.html +30 -0
  86. data/spec/rails_app/public/500.html +30 -0
  87. data/spec/rails_app/public/dispatch.cgi +10 -0
  88. data/spec/rails_app/public/dispatch.fcgi +24 -0
  89. data/spec/rails_app/public/dispatch.rb +10 -0
  90. data/spec/rails_app/public/favicon.ico +0 -0
  91. data/spec/rails_app/public/images/rails.png +0 -0
  92. data/spec/rails_app/public/index.html +277 -0
  93. data/spec/rails_app/public/javascripts/application.js +2 -0
  94. data/spec/rails_app/public/javascripts/controls.js +963 -0
  95. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  96. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  97. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  98. data/spec/rails_app/public/robots.txt +5 -0
  99. data/spec/rails_app/script/about +3 -0
  100. data/spec/rails_app/script/console +3 -0
  101. data/spec/rails_app/script/destroy +3 -0
  102. data/spec/rails_app/script/generate +3 -0
  103. data/spec/rails_app/script/performance/benchmarker +3 -0
  104. data/spec/rails_app/script/performance/profiler +3 -0
  105. data/spec/rails_app/script/performance/request +3 -0
  106. data/spec/rails_app/script/plugin +3 -0
  107. data/spec/rails_app/script/process/inspector +3 -0
  108. data/spec/rails_app/script/process/reaper +3 -0
  109. data/spec/rails_app/script/process/spawner +3 -0
  110. data/spec/rails_app/script/runner +3 -0
  111. data/spec/rails_app/script/server +3 -0
  112. data/spec/request/mongrel_spec.rb +39 -0
  113. data/spec/request/parser_spec.rb +254 -0
  114. data/spec/request/persistent_spec.rb +35 -0
  115. data/spec/request/processing_spec.rb +50 -0
  116. data/spec/response_spec.rb +91 -0
  117. data/spec/runner_spec.rb +168 -0
  118. data/spec/server/builder_spec.rb +44 -0
  119. data/spec/server/pipelining_spec.rb +110 -0
  120. data/spec/server/robustness_spec.rb +34 -0
  121. data/spec/server/stopping_spec.rb +55 -0
  122. data/spec/server/swiftiply.yml +6 -0
  123. data/spec/server/swiftiply_spec.rb +32 -0
  124. data/spec/server/tcp_spec.rb +57 -0
  125. data/spec/server/threaded_spec.rb +27 -0
  126. data/spec/server/unix_socket_spec.rb +26 -0
  127. data/spec/server_spec.rb +100 -0
  128. data/spec/spec_helper.rb +220 -0
  129. data/tasks/announce.rake +22 -0
  130. data/tasks/deploy.rake +13 -0
  131. data/tasks/email.erb +30 -0
  132. data/tasks/gem.rake +66 -0
  133. data/tasks/rdoc.rake +25 -0
  134. data/tasks/site.rake +15 -0
  135. data/tasks/spec.rake +43 -0
  136. data/tasks/stats.rake +28 -0
  137. metadata +251 -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 = 2
9
+ TINY = 8
10
+
11
+ STRING = [MAJOR, MINOR, TINY].join('.')
12
+
13
+ CODENAME = "Does It Offend You, Yeah?".freeze
14
+
15
+ RACK = [1, 0].freeze # Rack protocol version
16
+ end
17
+
18
+ NAME = 'steamcannon-thin'.freeze
19
+ SERVER = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze
20
+
21
+ def self.win?
22
+ RUBY_PLATFORM =~ /mswin|mingw/
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
Binary file
@@ -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,25 @@
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), :no_epoll => true)
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
+
21
+ it 'should convert _ to - in option name' do
22
+ out = @command.shellify
23
+ out.should include('--no-epoll')
24
+ end
25
+ 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,106 @@
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 not return HTTP_X_FORWARDED_FOR as remote_address" do
44
+ @connection.request.env['HTTP_X_FORWARDED_FOR'] = '1.2.3.4'
45
+ @connection.stub!(:socket_address).and_return("127.0.0.1")
46
+ @connection.remote_address.should == "127.0.0.1"
47
+ end
48
+
49
+ it "should return nil on error retreiving remote_address" do
50
+ @connection.stub!(:get_peername).and_raise(RuntimeError)
51
+ @connection.remote_address.should be_nil
52
+ end
53
+
54
+ it "should return nil on nil get_peername" do
55
+ @connection.stub!(:get_peername).and_return(nil)
56
+ @connection.remote_address.should be_nil
57
+ end
58
+
59
+ it "should return nil on empty get_peername" do
60
+ @connection.stub!(:get_peername).and_return('')
61
+ @connection.remote_address.should be_nil
62
+ end
63
+
64
+ it "should return remote_address" do
65
+ @connection.stub!(:get_peername).and_return(Socket.pack_sockaddr_in(3000, '127.0.0.1'))
66
+ @connection.remote_address.should == '127.0.0.1'
67
+ end
68
+
69
+ it "should not be persistent" do
70
+ @connection.should_not be_persistent
71
+ end
72
+
73
+ it "should be persistent when response is and allowed" do
74
+ @connection.response.stub!(:persistent?).and_return(true)
75
+ @connection.can_persist!
76
+ @connection.should be_persistent
77
+ end
78
+
79
+ it "should not be persistent when response is but not allowed" do
80
+ @connection.response.persistent!
81
+ @connection.should_not be_persistent
82
+ end
83
+
84
+ it "should set request env as rack.multithread" do
85
+ EventMachine.should_receive(:defer)
86
+
87
+ @connection.threaded = true
88
+ @connection.process
89
+
90
+ @connection.request.env["rack.multithread"].should == true
91
+ end
92
+
93
+ it "should set as threaded when app.deferred? is true" do
94
+ @connection.app.should_receive(:deferred?).and_return(true)
95
+ @connection.should be_threaded
96
+ end
97
+
98
+ it "should not set as threaded when app.deferred? is false" do
99
+ @connection.app.should_receive(:deferred?).and_return(false)
100
+ @connection.should_not be_threaded
101
+ end
102
+
103
+ it "should not set as threaded when app do not respond to deferred?" do
104
+ @connection.should_not be_threaded
105
+ end
106
+ end
@@ -0,0 +1,267 @@
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 with UNIX socket" do
139
+ before do
140
+ @cluster = Cluster.new(:chdir => '/rails_app',
141
+ :socket => '/tmp/thin.sock',
142
+ :address => '0.0.0.0',
143
+ :port => 3000,
144
+ :servers => 3,
145
+ :timeout => 10,
146
+ :log => 'thin.log',
147
+ :pid => 'thin.pid',
148
+ :only => 1
149
+ )
150
+ end
151
+
152
+ it 'should call only specified server' do
153
+ calls = []
154
+ @cluster.send(:with_each_server) do |n|
155
+ calls << n
156
+ end
157
+ calls.should == [1]
158
+ end
159
+ end
160
+
161
+ describe Cluster, "controlling only one server, by sequence number" do
162
+ before do
163
+ @cluster = Cluster.new(:chdir => '/rails_app',
164
+ :address => '0.0.0.0',
165
+ :port => 3000,
166
+ :servers => 3,
167
+ :timeout => 10,
168
+ :log => 'thin.log',
169
+ :pid => 'thin.pid',
170
+ :only => 1
171
+ )
172
+ end
173
+
174
+ it 'should call only specified server' do
175
+ calls = []
176
+ @cluster.send(:with_each_server) do |n|
177
+ calls << n
178
+ end
179
+ calls.should == [3001]
180
+ end
181
+
182
+ it "should start only specified server" do
183
+ Command.should_receive(:run).with(:start, options_for_port(3001))
184
+
185
+ @cluster.start
186
+ end
187
+
188
+ private
189
+ def options_for_port(port)
190
+ { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
191
+ end
192
+ end
193
+
194
+ describe Cluster, "with Swiftiply" do
195
+ before do
196
+ @cluster = Cluster.new(:chdir => '/rails_app',
197
+ :address => '0.0.0.0',
198
+ :port => 3000,
199
+ :servers => 3,
200
+ :timeout => 10,
201
+ :log => 'thin.log',
202
+ :pid => 'thin.pid',
203
+ :swiftiply => true
204
+ )
205
+ end
206
+
207
+ it 'should call each server' do
208
+ calls = []
209
+ @cluster.send(:with_each_server) do |n|
210
+ calls << n
211
+ end
212
+ calls.should == [0, 1, 2]
213
+ end
214
+
215
+ it 'should start each server' do
216
+ Command.should_receive(:run).with(:start, options_for_swiftiply(0))
217
+ Command.should_receive(:run).with(:start, options_for_swiftiply(1))
218
+ Command.should_receive(:run).with(:start, options_for_swiftiply(2))
219
+
220
+ @cluster.start
221
+ end
222
+
223
+ it 'should stop each server' do
224
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(0))
225
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(1))
226
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(2))
227
+
228
+ @cluster.stop
229
+ end
230
+
231
+ private
232
+ def options_for_swiftiply(number)
233
+ { :address => '0.0.0.0', :port => 3000, :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :pid => "thin.#{number}.pid", :chdir => "/rails_app", :swiftiply => true }
234
+ end
235
+ end
236
+
237
+ describe Cluster, "rolling restart" do
238
+ before do
239
+ @cluster = Cluster.new(:chdir => '/rails_app',
240
+ :address => '0.0.0.0',
241
+ :port => 3000,
242
+ :servers => 2,
243
+ :timeout => 10,
244
+ :log => 'thin.log',
245
+ :pid => 'thin.pid',
246
+ :onebyone => true,
247
+ :wait => 30
248
+ )
249
+ end
250
+
251
+ it "should restart servers one by one" do
252
+ Command.should_receive(:run).with(:stop, options_for_port(3000))
253
+ Command.should_receive(:run).with(:start, options_for_port(3000))
254
+ @cluster.should_receive(:wait_until_server_started).with(3000)
255
+
256
+ Command.should_receive(:run).with(:stop, options_for_port(3001))
257
+ Command.should_receive(:run).with(:start, options_for_port(3001))
258
+ @cluster.should_receive(:wait_until_server_started).with(3001)
259
+
260
+ @cluster.restart
261
+ end
262
+
263
+ private
264
+ def options_for_port(port)
265
+ { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
266
+ end
267
+ end