thin 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of thin might be problematic. Click here for more details.

Files changed (49) hide show
  1. data/CHANGELOG +34 -0
  2. data/bin/thin +2 -164
  3. data/example/config.ru +4 -1
  4. data/example/ramaze.ru +12 -0
  5. data/example/thin.god +70 -66
  6. data/example/vlad.rake +61 -0
  7. data/lib/rack/adapter/rails.rb +0 -3
  8. data/lib/rack/handler/thin.rb +6 -1
  9. data/lib/thin.rb +13 -4
  10. data/lib/thin/command.rb +9 -5
  11. data/lib/thin/connection.rb +5 -14
  12. data/lib/thin/connectors/connector.rb +61 -0
  13. data/lib/thin/connectors/tcp_server.rb +29 -0
  14. data/lib/thin/connectors/unix_server.rb +48 -0
  15. data/lib/thin/controllers/cluster.rb +115 -0
  16. data/lib/thin/controllers/controller.rb +85 -0
  17. data/lib/thin/controllers/service.rb +73 -0
  18. data/lib/thin/controllers/service.sh.erb +39 -0
  19. data/lib/thin/daemonizing.rb +9 -4
  20. data/lib/thin/headers.rb +2 -2
  21. data/lib/thin/runner.rb +166 -0
  22. data/lib/thin/server.rb +109 -89
  23. data/lib/thin/stats.html.erb +216 -0
  24. data/lib/thin/stats.rb +1 -249
  25. data/lib/thin/version.rb +10 -3
  26. data/spec/command_spec.rb +0 -1
  27. data/spec/configs/cluster.yml +9 -0
  28. data/spec/configs/single.yml +9 -0
  29. data/spec/{cluster_spec.rb → controllers/cluster_spec.rb} +22 -10
  30. data/spec/controllers/controller_spec.rb +85 -0
  31. data/spec/controllers/service_spec.rb +51 -0
  32. data/spec/daemonizing_spec.rb +73 -9
  33. data/spec/request/mongrel_spec.rb +39 -0
  34. data/spec/{request_spec.rb → request/parser_spec.rb} +11 -143
  35. data/spec/request/perf_spec.rb +50 -0
  36. data/spec/request/processing_spec.rb +46 -0
  37. data/spec/runner_spec.rb +135 -0
  38. data/spec/server/builder_spec.rb +38 -0
  39. data/spec/server/stopping_spec.rb +45 -0
  40. data/spec/server/tcp_spec.rb +54 -0
  41. data/spec/server/unix_socket_spec.rb +30 -0
  42. data/spec/spec_helper.rb +49 -16
  43. data/tasks/announce.rake +7 -3
  44. data/tasks/email.erb +8 -18
  45. data/tasks/gem.rake +8 -1
  46. data/tasks/stats.rake +21 -8
  47. metadata +33 -6
  48. data/lib/thin/cluster.rb +0 -123
  49. data/spec/server_spec.rb +0 -200
data/lib/thin/version.rb CHANGED
@@ -1,4 +1,4 @@
1
- module Thin
1
+ module Thin
2
2
  # Raised when a feature is not supported on the
3
3
  # current platform.
4
4
  class PlatformNotSupported < RuntimeError; end
@@ -6,17 +6,24 @@ module Thin
6
6
  module VERSION #:nodoc:
7
7
  MAJOR = 0
8
8
  MINOR = 6
9
- TINY = 2
9
+ TINY = 3
10
10
 
11
11
  STRING = [MAJOR, MINOR, TINY].join('.')
12
12
 
13
- CODENAME = 'Rambo'
13
+ CODENAME = 'Ninja Cookie'
14
14
  end
15
15
 
16
+ NAME = 'thin'.freeze
17
+ SERVER = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze
18
+
16
19
  def self.win?
17
20
  RUBY_PLATFORM =~ /mswin/
18
21
  end
19
22
 
23
+ def self.linux?
24
+ RUBY_PLATFORM =~ /linux/
25
+ end
26
+
20
27
  def self.ruby_18?
21
28
  RUBY_VERSION =~ /^1\.8/
22
29
  end
data/spec/command_spec.rb CHANGED
@@ -3,7 +3,6 @@ require File.dirname(__FILE__) + '/spec_helper'
3
3
  describe Command do
4
4
  before do
5
5
  @command = Command.new(:start, :port => 3000, :daemonize => true, :log => 'hi.log', :pid => nil)
6
- @command.script = File.dirname(__FILE__) + '/../bin/thin'
7
6
  @command.silent = true
8
7
  end
9
8
 
@@ -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
@@ -1,8 +1,9 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ include Controllers
2
3
 
3
4
  describe Cluster, "with host and port" do
4
5
  before do
5
- @cluster = Cluster.new(:chdir => File.dirname(__FILE__) + '/rails_app',
6
+ @cluster = Cluster.new(:chdir => '/rails_app',
6
7
  :address => '0.0.0.0',
7
8
  :port => 3000,
8
9
  :servers => 3,
@@ -10,7 +11,6 @@ describe Cluster, "with host and port" do
10
11
  :log => 'thin.log',
11
12
  :pid => 'thin.pid'
12
13
  )
13
- @cluster.script = File.dirname(__FILE__) + '/../bin/thin'
14
14
  @cluster.silent = true
15
15
  end
16
16
 
@@ -18,6 +18,10 @@ describe Cluster, "with host and port" do
18
18
  @cluster.send(:include_server_number, 'thin.log', 3000).should == 'thin.3000.log'
19
19
  @cluster.send(:include_server_number, 'thin.pid', 3000).should == 'thin.3000.pid'
20
20
  end
21
+
22
+ it "should exclude :servers option" do
23
+ @cluster.options.should_not have_key(:servers)
24
+ end
21
25
 
22
26
  it 'should call each server' do
23
27
  calls = []
@@ -45,13 +49,13 @@ describe Cluster, "with host and port" do
45
49
 
46
50
  private
47
51
  def options_for_port(port)
48
- { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "./spec/rails_app" }
52
+ { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
49
53
  end
50
54
  end
51
55
 
52
56
  describe Cluster, "with UNIX socket" do
53
57
  before do
54
- @cluster = Cluster.new(:chdir => File.dirname(__FILE__) + '/rails_app',
58
+ @cluster = Cluster.new(:chdir => '/rails_app',
55
59
  :socket => '/tmp/thin.sock',
56
60
  :address => '0.0.0.0',
57
61
  :port => 3000,
@@ -60,7 +64,6 @@ describe Cluster, "with UNIX socket" do
60
64
  :log => 'thin.log',
61
65
  :pid => 'thin.pid'
62
66
  )
63
- @cluster.script = File.dirname(__FILE__) + '/../bin/thin'
64
67
  @cluster.silent = true
65
68
  end
66
69
 
@@ -69,6 +72,11 @@ describe Cluster, "with UNIX socket" do
69
72
  @cluster.send(:include_server_number, 'thin', 0).should == 'thin.0'
70
73
  end
71
74
 
75
+ it "should exclude :address and :port options" do
76
+ @cluster.options.should_not have_key(:address)
77
+ @cluster.options.should_not have_key(:port)
78
+ end
79
+
72
80
  it 'should call each server' do
73
81
  calls = []
74
82
  @cluster.send(:with_each_server) do |n|
@@ -96,13 +104,13 @@ describe Cluster, "with UNIX socket" do
96
104
 
97
105
  private
98
106
  def options_for_socket(number)
99
- { :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :socket => "/tmp/thin.#{number}.sock", :pid => "thin.#{number}.pid", :chdir => "./spec/rails_app" }
107
+ { :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :socket => "/tmp/thin.#{number}.sock", :pid => "thin.#{number}.pid", :chdir => "/rails_app" }
100
108
  end
101
109
  end
102
110
 
103
111
  describe Cluster, "controlling only one server" do
104
112
  before do
105
- @cluster = Cluster.new(:chdir => File.dirname(__FILE__) + '/rails_app',
113
+ @cluster = Cluster.new(:chdir => '/rails_app',
106
114
  :address => '0.0.0.0',
107
115
  :port => 3000,
108
116
  :servers => 3,
@@ -111,10 +119,14 @@ describe Cluster, "controlling only one server" do
111
119
  :pid => 'thin.pid',
112
120
  :only => 3001
113
121
  )
114
- @cluster.script = File.dirname(__FILE__) + '/../bin/thin'
115
122
  @cluster.silent = true
116
123
  end
117
124
 
125
+ it "should exclude :servers and :only options" do
126
+ @cluster.options.should_not have_key(:servers)
127
+ @cluster.options.should_not have_key(:only)
128
+ end
129
+
118
130
  it 'should call only specified server' do
119
131
  calls = []
120
132
  @cluster.send(:with_each_server) do |n|
@@ -131,6 +143,6 @@ describe Cluster, "controlling only one server" do
131
143
 
132
144
  private
133
145
  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 => "./spec/rails_app" }
146
+ { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
135
147
  end
136
148
  end
@@ -0,0 +1,85 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'ostruct'
3
+ include Controllers
4
+
5
+ describe Controller, 'start' do
6
+ before do
7
+ @controller = Controller.new(:address => '0.0.0.0', :port => 3000, :pid => 'thin.pid', :log => 'thin.log', :timeout => 60)
8
+
9
+ @server = OpenStruct.new
10
+ @adapter = OpenStruct.new
11
+
12
+ Server.should_receive(:new).with('0.0.0.0', 3000).and_return(@server)
13
+ Rack::Adapter::Rails.stub!(:new).and_return(@adapter)
14
+ end
15
+
16
+ it "should configure server" do
17
+ @controller.start
18
+
19
+ @server.app.should == @adapter
20
+ @server.pid_file.should == 'thin.pid'
21
+ @server.log_file.should == 'thin.log'
22
+ @server.timeout.should == 60
23
+ end
24
+
25
+ it "should start as daemon" do
26
+ @controller.options[:daemonize] = true
27
+ @controller.options[:user] = true
28
+ @controller.options[:group] = true
29
+
30
+ @server.should_receive(:daemonize)
31
+ @server.should_receive(:change_privilege)
32
+
33
+ @controller.start
34
+ end
35
+
36
+ it "should configure Rails adapter" do
37
+ Rack::Adapter::Rails.should_receive(:new).with(@controller.options.merge(:root => nil))
38
+
39
+ @controller.start
40
+ end
41
+
42
+ it "should mount app under :prefix" do
43
+ @controller.options[:prefix] = '/app'
44
+ @controller.start
45
+
46
+ @server.app.class.should == Rack::URLMap
47
+ end
48
+
49
+ it "should mount Stats adapter under :stats" do
50
+ @controller.options[:stats] = '/stats'
51
+ @controller.start
52
+
53
+ @server.app.class.should == Stats::Adapter
54
+ end
55
+
56
+ it "should load app from Rack config" do
57
+ @controller.options[:rackup] = 'example/config.ru'
58
+ @controller.start
59
+
60
+ @server.app.class.should == Proc
61
+ end
62
+ end
63
+
64
+ describe Controller do
65
+ it "should stop" do
66
+ Server.should_receive(:kill).with('thin.pid', 10)
67
+ Controller.new(:pid => 'thin.pid', :timeout => 10).stop
68
+ end
69
+
70
+ it "should restart" do
71
+ Server.should_receive(:restart).with('thin.pid')
72
+ Controller.new(:pid => 'thin.pid').restart
73
+ end
74
+
75
+ it "should write configuration file" do
76
+ silence_stream(STDOUT) do
77
+ Controller.new(:config => 'test.yml', :port => 5000, :address => '127.0.0.1').config
78
+ end
79
+
80
+ File.read('test.yml').should include('port: 5000', 'address: 127.0.0.1')
81
+ File.read('test.yml').should_not include('config: ')
82
+
83
+ File.delete('test.yml')
84
+ end
85
+ end
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ include Controllers
3
+
4
+ describe Service do
5
+ before(:all) do
6
+ silence_stream(STDERR) do
7
+ Service::INITD_PATH = 'tmp/sandbox' + Service::INITD_PATH
8
+ Service::DEFAULT_CONFIG_PATH = 'tmp/sandbox' + Service::DEFAULT_CONFIG_PATH
9
+ end
10
+ end
11
+
12
+ before do
13
+ Thin.stub!(:linux?).and_return(true)
14
+ FileUtils.mkdir_p 'tmp/sandbox'
15
+
16
+ @service = Service.new(:all => 'spec/configs')
17
+ @service.silent = true
18
+ end
19
+
20
+ it "should call command for each config file" do
21
+ Command.should_receive(:run).with(:start, :config => 'spec/configs/cluster.yml', :daemonize => true)
22
+ Command.should_receive(:run).with(:start, :config => 'spec/configs/single.yml', :daemonize => true)
23
+
24
+ @service.start
25
+ end
26
+
27
+ it "should create /etc/init.d/thin file when calling install" do
28
+ @service.install
29
+
30
+ File.exist?(Service::INITD_PATH).should be_true
31
+ File.read(Service::INITD_PATH).should include('CONFIG_PATH=tmp/sandbox/etc/thin',
32
+ 'SCRIPT_NAME=tmp/sandbox/etc/init.d/thin',
33
+ 'DAEMON=' + Command.script)
34
+ end
35
+
36
+ it "should create /etc/thin dir when calling install" do
37
+ @service.install
38
+
39
+ File.directory?(Service::DEFAULT_CONFIG_PATH).should be_true
40
+ end
41
+
42
+ it "should include specified path in /etc/init.d/thin script" do
43
+ @service.install('tmp/sandbox/usr/thin')
44
+
45
+ File.read(Service::INITD_PATH).should include('CONFIG_PATH=tmp/sandbox/usr/thin')
46
+ end
47
+
48
+ after do
49
+ FileUtils.rm_rf 'tmp/sandbox'
50
+ end
51
+ end
@@ -1,9 +1,28 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
+ class TestServer
4
+ include Logging # Daemonizable should include this?
5
+ include Daemonizable
6
+
7
+ def stop
8
+ end
9
+
10
+ def name
11
+ 'thin'
12
+ end
13
+ end
14
+
3
15
  describe 'Daemonizing' do
4
- before do
5
- @server = Server.new('0.0.0.0', 3000, nil)
6
- @server.log_file = File.dirname(__FILE__) + '/../log/daemonizing_test.log'
16
+
17
+ before(:all) do
18
+ @logfile = File.dirname(__FILE__) + '/../log/daemonizing_test.log'
19
+ File.delete(@logfile) if File.exist?(@logfile)
20
+ @child_processes = []
21
+ end
22
+
23
+ before(:each) do
24
+ @server = TestServer.new
25
+ @server.log_file = @logfile
7
26
  @server.pid_file = 'test.pid'
8
27
  @pid = nil
9
28
  end
@@ -15,6 +34,7 @@ describe 'Daemonizing' do
15
34
 
16
35
  it 'should create a pid file' do
17
36
  @pid = fork do
37
+ @child_processes << Process.pid
18
38
  @server.daemonize
19
39
  sleep 1
20
40
  end
@@ -29,6 +49,7 @@ describe 'Daemonizing' do
29
49
 
30
50
  it 'should redirect stdio to a log file' do
31
51
  @pid = fork do
52
+ @child_processes << Process.pid
32
53
  @server.log_file = 'daemon_test.log'
33
54
  @server.daemonize
34
55
 
@@ -49,6 +70,7 @@ describe 'Daemonizing' do
49
70
 
50
71
  it 'should change privilege' do
51
72
  @pid = fork do
73
+ @child_processes << Process.pid
52
74
  @server.daemonize
53
75
  @server.change_privilege('root', 'admin')
54
76
  end
@@ -58,14 +80,15 @@ describe 'Daemonizing' do
58
80
 
59
81
  it 'should kill process in pid file' do
60
82
  @pid = fork do
83
+ @child_processes << Process.pid
61
84
  @server.daemonize
62
85
  loop { sleep 1 }
63
86
  end
64
87
 
65
- proc { sleep 0.1 until File.exist?(@server.pid_file) }.should take_less_then(3)
88
+ server_should_start_in_less_then 3
66
89
 
67
90
  silence_stream STDOUT do
68
- Server.kill(@server.pid_file, 1)
91
+ TestServer.kill(@server.pid_file, 1)
69
92
  end
70
93
 
71
94
  File.exist?(@server.pid_file).should_not be_true
@@ -73,25 +96,66 @@ describe 'Daemonizing' do
73
96
 
74
97
  it 'should send kill signal if timeout' do
75
98
  @pid = fork do
99
+ @child_processes << Process.pid
76
100
  @server.should_receive(:stop) # pretend we cannot handle the INT signal
77
101
  @server.daemonize
78
102
  sleep 5
79
103
  end
80
104
 
81
- proc { sleep 0.1 until File.exist?(@server.pid_file) }.should take_less_then(10)
105
+ server_should_start_in_less_then 10
82
106
 
83
107
  silence_stream STDOUT do
84
- Server.kill(@server.pid_file, 1)
108
+ TestServer.kill(@server.pid_file, 1)
85
109
  end
86
110
 
87
111
  File.exist?(@server.pid_file).should be_false
88
112
  Process.running?(@pid).should be_false
89
113
  end
90
114
 
91
- it "should restart"
115
+ it "should restart" do
116
+ @pid = fork do
117
+ @child_processes << Process.pid
118
+ @server.on_restart {}
119
+ @server.daemonize
120
+ sleep 5
121
+ end
122
+
123
+ server_should_start_in_less_then 10
124
+
125
+ silence_stream STDOUT do
126
+ TestServer.restart(@server.pid_file)
127
+ end
128
+
129
+ proc { sleep 0.1 while File.exist?(@server.pid_file) }.should take_less_then(10)
130
+ end
131
+
132
+ it "should exit if pid file already exist" do
133
+ @pid = fork do
134
+ @child_processes << Process.pid
135
+ @server.daemonize
136
+ sleep 5
137
+ end
138
+ server_should_start_in_less_then 10
139
+
140
+ proc { @server.daemonize }.should raise_error(PidFileExist)
141
+
142
+ File.exist?(@server.pid_file).should be_true
143
+ end
92
144
 
93
- after do
145
+ after(:each) do
94
146
  Process.kill(9, @pid.to_i) if @pid && Process.running?(@pid.to_i)
95
147
  Process.kill(9, @server.pid) if @server.pid && Process.running?(@server.pid)
148
+ File.delete(@server.pid_file) rescue nil
149
+ end
150
+
151
+ after(:all) do
152
+ @child_processes.each do |pid|
153
+ Process.kill(9, pid) rescue nil
154
+ end
96
155
  end
156
+
157
+ private
158
+ def server_should_start_in_less_then(sec=10)
159
+ proc { sleep 0.1 until File.exist?(@server.pid_file) }.should take_less_then(10)
160
+ end
97
161
  end