thin 0.6.4-x86-mswin32-60 → 0.7.0-x86-mswin32-60

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 (46) hide show
  1. data/CHANGELOG +20 -0
  2. data/README +11 -12
  3. data/benchmark/abc +51 -0
  4. data/benchmark/benchmarker.rb +80 -0
  5. data/benchmark/runner +79 -0
  6. data/example/adapter.rb +3 -3
  7. data/example/thin.god +11 -7
  8. data/lib/thin.rb +17 -16
  9. data/lib/thin/command.rb +10 -4
  10. data/lib/thin/connection.rb +51 -9
  11. data/lib/thin/connectors/connector.rb +22 -10
  12. data/lib/thin/connectors/swiftiply_client.rb +55 -0
  13. data/lib/thin/connectors/unix_server.rb +5 -7
  14. data/lib/thin/controllers/cluster.rb +28 -22
  15. data/lib/thin/controllers/controller.rb +74 -14
  16. data/lib/thin/controllers/service.rb +1 -1
  17. data/lib/thin/daemonizing.rb +6 -4
  18. data/lib/thin/headers.rb +4 -0
  19. data/lib/thin/logging.rb +34 -9
  20. data/lib/thin/request.rb +31 -2
  21. data/lib/thin/response.rb +22 -7
  22. data/lib/thin/runner.rb +27 -14
  23. data/lib/thin/server.rb +55 -7
  24. data/lib/thin/version.rb +3 -3
  25. data/lib/thin_parser.so +0 -0
  26. data/spec/command_spec.rb +2 -3
  27. data/spec/connection_spec.rb +25 -1
  28. data/spec/connectors/swiftiply_client_spec.rb +66 -0
  29. data/spec/controllers/cluster_spec.rb +43 -12
  30. data/spec/controllers/controller_spec.rb +16 -4
  31. data/spec/controllers/service_spec.rb +0 -1
  32. data/spec/logging_spec.rb +42 -0
  33. data/spec/request/persistent_spec.rb +35 -0
  34. data/spec/response_spec.rb +18 -0
  35. data/spec/server/pipelining_spec.rb +108 -0
  36. data/spec/server/swiftiply.yml +6 -0
  37. data/spec/server/swiftiply_spec.rb +27 -0
  38. data/spec/server/tcp_spec.rb +3 -3
  39. data/spec/server_spec.rb +22 -0
  40. data/spec/spec_helper.rb +3 -3
  41. data/tasks/gem.rake +1 -1
  42. data/tasks/spec.rake +9 -0
  43. metadata +14 -6
  44. data/benchmark/previous.rb +0 -14
  45. data/benchmark/simple.rb +0 -15
  46. data/benchmark/utils.rb +0 -75
@@ -11,7 +11,6 @@ describe Cluster, "with host and port" do
11
11
  :log => 'thin.log',
12
12
  :pid => 'thin.pid'
13
13
  )
14
- @cluster.silent = true
15
14
  end
16
15
 
17
16
  it 'should include port number in file names' do
@@ -19,10 +18,6 @@ describe Cluster, "with host and port" do
19
18
  @cluster.send(:include_server_number, 'thin.pid', 3000).should == 'thin.3000.pid'
20
19
  end
21
20
 
22
- it "should exclude :servers option" do
23
- @cluster.options.should_not have_key(:servers)
24
- end
25
-
26
21
  it 'should call each server' do
27
22
  calls = []
28
23
  @cluster.send(:with_each_server) do |port|
@@ -64,7 +59,6 @@ describe Cluster, "with UNIX socket" do
64
59
  :log => 'thin.log',
65
60
  :pid => 'thin.pid'
66
61
  )
67
- @cluster.silent = true
68
62
  end
69
63
 
70
64
  it 'should include socket number in file names' do
@@ -119,12 +113,6 @@ describe Cluster, "controlling only one server" do
119
113
  :pid => 'thin.pid',
120
114
  :only => 3001
121
115
  )
122
- @cluster.silent = true
123
- end
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
116
  end
129
117
 
130
118
  it 'should call only specified server' do
@@ -145,4 +133,47 @@ describe Cluster, "controlling only one server" do
145
133
  def options_for_port(port)
146
134
  { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
147
135
  end
136
+ end
137
+
138
+ describe Cluster, "with Swiftiply" 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
+ :swiftiply => true
148
+ )
149
+ end
150
+
151
+ it 'should call each server' do
152
+ calls = []
153
+ @cluster.send(:with_each_server) do |n|
154
+ calls << n
155
+ end
156
+ calls.should == [0, 1, 2]
157
+ end
158
+
159
+ it 'should start each server' do
160
+ Command.should_receive(:run).with(:start, options_for_swiftiply(0))
161
+ Command.should_receive(:run).with(:start, options_for_swiftiply(1))
162
+ Command.should_receive(:run).with(:start, options_for_swiftiply(2))
163
+
164
+ @cluster.start
165
+ end
166
+
167
+ it 'should stop each server' do
168
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(0))
169
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(1))
170
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(2))
171
+
172
+ @cluster.stop
173
+ end
174
+
175
+ private
176
+ def options_for_swiftiply(number)
177
+ { :address => '0.0.0.0', :port => 3000, :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :pid => "thin.#{number}.pid", :chdir => "/rails_app", :swiftiply => true }
178
+ end
148
179
  end
@@ -4,7 +4,13 @@ include Controllers
4
4
 
5
5
  describe Controller, 'start' do
6
6
  before do
7
- @controller = Controller.new(:address => '0.0.0.0', :port => 3000, :pid => 'thin.pid', :log => 'thin.log', :timeout => 60)
7
+ @controller = Controller.new(:address => '0.0.0.0',
8
+ :port => 3000,
9
+ :pid => 'thin.pid',
10
+ :log => 'thin.log',
11
+ :timeout => 60,
12
+ :max_conns => 2000,
13
+ :max_persistent_conns => 1000)
8
14
 
9
15
  @server = OpenStruct.new
10
16
  @adapter = OpenStruct.new
@@ -19,7 +25,8 @@ describe Controller, 'start' do
19
25
  @server.app.should == @adapter
20
26
  @server.pid_file.should == 'thin.pid'
21
27
  @server.log_file.should == 'thin.log'
22
- @server.timeout.should == 60
28
+ @server.maximum_connections.should == 2000
29
+ @server.maximum_persistent_connections.should == 1000
23
30
  end
24
31
 
25
32
  it "should start as daemon" do
@@ -62,14 +69,19 @@ describe Controller, 'start' do
62
69
  end
63
70
 
64
71
  describe Controller do
72
+ before do
73
+ @controller = Controller.new(:pid => 'thin.pid', :timeout => 10)
74
+ @controller.stub!(:wait_for_file)
75
+ end
76
+
65
77
  it "should stop" do
66
78
  Server.should_receive(:kill).with('thin.pid', 10)
67
- Controller.new(:pid => 'thin.pid', :timeout => 10).stop
79
+ @controller.stop
68
80
  end
69
81
 
70
82
  it "should restart" do
71
83
  Server.should_receive(:restart).with('thin.pid')
72
- Controller.new(:pid => 'thin.pid').restart
84
+ @controller.restart
73
85
  end
74
86
 
75
87
  it "should write configuration file" do
@@ -14,7 +14,6 @@ describe Service do
14
14
  FileUtils.mkdir_p 'tmp/sandbox'
15
15
 
16
16
  @service = Service.new(:all => 'spec/configs')
17
- @service.silent = true
18
17
  end
19
18
 
20
19
  it "should call command for each config file" do
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class TestLogging
4
+ include Logging
5
+ end
6
+
7
+ describe Logging do
8
+ before do
9
+ Logging.silent = false
10
+ @object = TestLogging.new
11
+ end
12
+
13
+ it "should output debug when set to true" do
14
+ Logging.debug = true
15
+ @object.should_receive(:puts)
16
+ @object.debug 'hi'
17
+ end
18
+
19
+ it "should output trace when set to true" do
20
+ Logging.trace = true
21
+ @object.should_receive(:puts)
22
+ @object.trace 'hi'
23
+ end
24
+
25
+ it "should not output when silenced" do
26
+ Logging.silent = true
27
+ @object.should_not_receive(:puts)
28
+ @object.log 'hi'
29
+ end
30
+
31
+ it "should not output when silenced [deprecated]" do
32
+ @object.should_receive(:warn)
33
+ @object.silent = true
34
+
35
+ @object.should_not_receive(:puts)
36
+ @object.log 'hi'
37
+ end
38
+
39
+ after do
40
+ Logging.silent = true
41
+ end
42
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Request, 'persistent' do
4
+ before do
5
+ @request = Request.new
6
+ end
7
+
8
+ it "should not assume that a persistent connection is maintained for HTTP version 1.0" do
9
+ @request.env['HTTP_VERSION'] = 'HTTP/1.0'
10
+ @request.should_not be_persistent
11
+ end
12
+
13
+ it "should assume that a persistent connection is maintained for HTTP version 1.0 when specified" do
14
+ @request.env['HTTP_VERSION'] = 'HTTP/1.0'
15
+ @request.env['HTTP_CONNECTION'] = 'Keep-Alive'
16
+ @request.should be_persistent
17
+ end
18
+
19
+ it "should maintain a persistent connection for HTTP/1.1 client" do
20
+ @request.env['HTTP_VERSION'] = 'HTTP/1.1'
21
+ @request.env['HTTP_CONNECTION'] = 'Keep-Alive'
22
+ @request.should be_persistent
23
+ end
24
+
25
+ it "should maintain a persistent connection for HTTP/1.1 client by default" do
26
+ @request.env['HTTP_VERSION'] = 'HTTP/1.1'
27
+ @request.should be_persistent
28
+ end
29
+
30
+ it "should not maintain a persistent connection for HTTP/1.1 client when Connection header include close" do
31
+ @request.env['HTTP_VERSION'] = 'HTTP/1.1'
32
+ @request.env['HTTP_CONNECTION'] = 'close'
33
+ @request.should_not be_persistent
34
+ end
35
+ end
@@ -63,6 +63,24 @@ EOS
63
63
  proc { @response.each { |l| l } }.should be_faster_then(0.00011)
64
64
  end
65
65
 
66
+ it "should not be persistent by default" do
67
+ @response.should_not be_persistent
68
+ end
69
+
70
+ it "should not be persistent when no Content-Length" do
71
+ @response = Response.new
72
+ @response.headers['Content-Type'] = 'text/html'
73
+ @response.body = ''
74
+
75
+ @response.persistent!
76
+ @response.should_not be_persistent
77
+ end
78
+
79
+ it "should be persistent when specified" do
80
+ @response.persistent!
81
+ @response.should be_persistent
82
+ end
83
+
66
84
  it "should be closeable" do
67
85
  @response.close
68
86
  end
@@ -0,0 +1,108 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Server, "HTTP pipelining" do
4
+ before do
5
+ calls = 0
6
+ start_server do |env|
7
+ calls += 1
8
+ body = env['PATH_INFO'] + '-' + calls.to_s
9
+ [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s }, body]
10
+ end
11
+ @server.maximum_persistent_connections = 1024
12
+ end
13
+
14
+ it "should pipeline request on same socket" do
15
+ socket = TCPSocket.new('0.0.0.0', 3333)
16
+ socket.write "GET /first HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
17
+ socket.flush
18
+ socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
19
+ response = socket.read
20
+ socket.close
21
+
22
+ wait_for_requests_to_complete!
23
+
24
+ response.should include('/first-1', '/second-2')
25
+ end
26
+
27
+ it "should pipeline requests by default on HTTP 1.1" do
28
+ socket = TCPSocket.new('0.0.0.0', 3333)
29
+ socket.write "GET /first HTTP/1.1\r\n\r\n"
30
+ socket.flush
31
+ socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
32
+ response = socket.read
33
+ socket.close
34
+
35
+ wait_for_requests_to_complete!
36
+
37
+ response.should include('/first-1', '/second-2')
38
+ end
39
+
40
+ it "should not pipeline request by default on HTTP 1.0" do
41
+ socket = TCPSocket.new('0.0.0.0', 3333)
42
+ socket.write "GET /first HTTP/1.0\r\n\r\n"
43
+ socket.flush
44
+ socket.write "GET /second HTTP/1.0\r\nConnection: close\r\n\r\n"
45
+ response = socket.read
46
+ socket.close
47
+
48
+ wait_for_requests_to_complete!
49
+
50
+ response.should include('/first-1')
51
+ response.should_not include('/second-2')
52
+ end
53
+
54
+ it "should not pipeline request on same socket when connection is closed" do
55
+ socket = TCPSocket.new('0.0.0.0', 3333)
56
+ socket.write "GET /first HTTP/1.1\r\nConnection: close\r\n\r\n"
57
+ socket.flush
58
+ socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
59
+ response = socket.read
60
+ socket.close
61
+
62
+ wait_for_requests_to_complete!
63
+
64
+ response.should include('/first-1')
65
+ response.should_not include('/second-2')
66
+ end
67
+
68
+ it "should not allow more persistent connection then maximum" do
69
+ @server.maximum_persistent_connections = 1
70
+
71
+ socket1 = TCPSocket.new('0.0.0.0', 3333)
72
+ socket1.write "GET / HTTP/1.1\r\n\r\n"
73
+ socket1.flush
74
+ socket2 = TCPSocket.new('0.0.0.0', 3333)
75
+ socket2.write "GET / HTTP/1.1\r\n\r\n"
76
+ socket2.flush
77
+
78
+ @server.connector.persistent_connection_count.should == 1
79
+ @server.connector.size.should == 2
80
+
81
+ socket1.close
82
+ socket2.close
83
+ end
84
+
85
+ it "should decrement persistent connection on close" do
86
+ socket = TCPSocket.new('0.0.0.0', 3333)
87
+ socket.write "GET / HTTP/1.1\r\n\r\n"
88
+ socket.flush
89
+
90
+ @server.connector.persistent_connection_count.should == 1
91
+
92
+ socket.write "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
93
+ socket.close
94
+
95
+ wait_for_requests_to_complete!
96
+
97
+ @server.connector.persistent_connection_count.should == 0
98
+ end
99
+
100
+ after do
101
+ stop_server
102
+ end
103
+
104
+ private
105
+ def wait_for_requests_to_complete!
106
+ sleep 0.1 until @server.connector.size == 0
107
+ end
108
+ end
@@ -0,0 +1,6 @@
1
+ cluster_address: 0.0.0.0
2
+ cluster_port: 3333
3
+ map:
4
+ - incoming: 127.0.0.1
5
+ outgoing: 127.0.0.1:5555
6
+ default: true
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Server, 'on Swiftiply' do
4
+ before do
5
+ @swiftiply = fork do
6
+ exec "swiftiply -c #{File.dirname(__FILE__)}/swiftiply.yml"
7
+ end
8
+ sleep 0.5
9
+ start_server(Connectors::SwiftiplyClient.new('0.0.0.0', 5555, nil)) do |env|
10
+ body = env.inspect + env['rack.input'].read
11
+ [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s }, body]
12
+ end
13
+ end
14
+
15
+ it 'should GET from Net::HTTP' do
16
+ Net::HTTP.get(URI.parse("http://0.0.0.0:3333/?cthis")).should include('cthis')
17
+ end
18
+
19
+ it 'should POST from Net::HTTP' do
20
+ Net::HTTP.post_form(URI.parse("http://0.0.0.0:3333/"), :arg => 'pirate').body.should include('arg=pirate')
21
+ end
22
+
23
+ after do
24
+ stop_server
25
+ Process.kill(9, @swiftiply)
26
+ end
27
+ end
@@ -13,7 +13,7 @@ describe Server, 'on TCP socket' do
13
13
  end
14
14
 
15
15
  it 'should GET from TCPSocket' do
16
- send_data("GET /?this HTTP/1.1\r\n\r\n").
16
+ send_data("GET /?this HTTP/1.1\r\nConnection: close\r\n\r\n").
17
17
  should include("HTTP/1.1 200 OK",
18
18
  "Content-Type: text/html", "Content-Length: ",
19
19
  "Connection: close", "this")
@@ -24,7 +24,7 @@ describe Server, 'on TCP socket' do
24
24
  end
25
25
 
26
26
  it 'should return empty string on incorrect Content-Length' do
27
- send_data("POST / HTTP/1.1\r\nContent-Length: 300\r\n\r\naye").should be_empty
27
+ send_data("POST / HTTP/1.1\r\nContent-Length: 300\r\nConnection: close\r\n\r\naye").should be_empty
28
28
  end
29
29
 
30
30
  it 'should POST from Net::HTTP' do
@@ -36,7 +36,7 @@ describe Server, 'on TCP socket' do
36
36
  post('/', :big => big).should include(big)
37
37
  end
38
38
 
39
- it "should handle GET in less then #{get_request_time = 0.004} RubySecond" do
39
+ it "should handle GET in less then #{get_request_time = 0.0045} RubySecond" do
40
40
  proc { get('/') }.should be_faster_then(get_request_time)
41
41
  end
42
42
 
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Server do
4
+ before do
5
+ @server = Server.new('0.0.0.0', 3000)
6
+ end
7
+
8
+ it "should set descriptor table size" do
9
+ @server.should_receive(:log).once
10
+ @server.maximum_connections = 100
11
+ @server.set_descriptor_table_size!
12
+ @server.maximum_connections.should == 100
13
+ end
14
+
15
+ it "should warn when descriptor table size too large" do
16
+ @server.stub!(:log)
17
+ @server.should_receive(:log).with(/^!!/)
18
+ @server.maximum_connections = 100_000
19
+ @server.set_descriptor_table_size!
20
+ @server.maximum_connections.should < 100_000
21
+ end
22
+ end
@@ -12,6 +12,7 @@ include Thin
12
12
 
13
13
  FileUtils.mkdir_p File.dirname(__FILE__) + '/../log'
14
14
  Command.script = File.dirname(__FILE__) + '/../bin/thin'
15
+ Logging.silent = true
15
16
 
16
17
  module Matchers
17
18
  class BeFasterThen
@@ -138,10 +139,9 @@ module Helpers
138
139
  def start_server(*args, &app)
139
140
  @server = Thin::Server.new(args[0] || '0.0.0.0', args[1] || 3333, app)
140
141
  @server.timeout = 3
141
- @server.silent = true
142
142
 
143
143
  @thread = Thread.new { @server.start }
144
- sleep 0.1 until @thread.status == 'sleep'
144
+ sleep 0.1 until @server.running? && EventMachine.reactor_running?
145
145
  end
146
146
 
147
147
  def stop_server
@@ -163,7 +163,7 @@ module Helpers
163
163
 
164
164
  def get(url)
165
165
  if @server.connector.class == Connectors::UnixServer
166
- send_data("GET #{url} HTTP/1.1\r\n\r\n")
166
+ send_data("GET #{url} HTTP/1.1\r\nConnection: close\r\n\r\n")
167
167
  else
168
168
  Net::HTTP.get(URI.parse("http://#{@server.host}:#{@server.port}" + url))
169
169
  end