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.
- data/CHANGELOG +20 -0
- data/README +11 -12
- data/benchmark/abc +51 -0
- data/benchmark/benchmarker.rb +80 -0
- data/benchmark/runner +79 -0
- data/example/adapter.rb +3 -3
- data/example/thin.god +11 -7
- data/lib/thin.rb +17 -16
- data/lib/thin/command.rb +10 -4
- data/lib/thin/connection.rb +51 -9
- data/lib/thin/connectors/connector.rb +22 -10
- data/lib/thin/connectors/swiftiply_client.rb +55 -0
- data/lib/thin/connectors/unix_server.rb +5 -7
- data/lib/thin/controllers/cluster.rb +28 -22
- data/lib/thin/controllers/controller.rb +74 -14
- data/lib/thin/controllers/service.rb +1 -1
- data/lib/thin/daemonizing.rb +6 -4
- data/lib/thin/headers.rb +4 -0
- data/lib/thin/logging.rb +34 -9
- data/lib/thin/request.rb +31 -2
- data/lib/thin/response.rb +22 -7
- data/lib/thin/runner.rb +27 -14
- data/lib/thin/server.rb +55 -7
- data/lib/thin/version.rb +3 -3
- data/lib/thin_parser.so +0 -0
- data/spec/command_spec.rb +2 -3
- data/spec/connection_spec.rb +25 -1
- data/spec/connectors/swiftiply_client_spec.rb +66 -0
- data/spec/controllers/cluster_spec.rb +43 -12
- data/spec/controllers/controller_spec.rb +16 -4
- data/spec/controllers/service_spec.rb +0 -1
- data/spec/logging_spec.rb +42 -0
- data/spec/request/persistent_spec.rb +35 -0
- data/spec/response_spec.rb +18 -0
- data/spec/server/pipelining_spec.rb +108 -0
- data/spec/server/swiftiply.yml +6 -0
- data/spec/server/swiftiply_spec.rb +27 -0
- data/spec/server/tcp_spec.rb +3 -3
- data/spec/server_spec.rb +22 -0
- data/spec/spec_helper.rb +3 -3
- data/tasks/gem.rake +1 -1
- data/tasks/spec.rake +9 -0
- metadata +14 -6
- data/benchmark/previous.rb +0 -14
- data/benchmark/simple.rb +0 -15
- 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
|
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.
|
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
|
-
|
79
|
+
@controller.stop
|
68
80
|
end
|
69
81
|
|
70
82
|
it "should restart" do
|
71
83
|
Server.should_receive(:restart).with('thin.pid')
|
72
|
-
|
84
|
+
@controller.restart
|
73
85
|
end
|
74
86
|
|
75
87
|
it "should write configuration 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
|
data/spec/response_spec.rb
CHANGED
@@ -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,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
|
data/spec/server/tcp_spec.rb
CHANGED
@@ -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.
|
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
|
|
data/spec/server_spec.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
CHANGED
@@ -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 @
|
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
|