thin 0.6.2-x86-mswin32-60 → 0.6.3-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 +34 -1
- data/bin/thin +2 -164
- data/example/config.ru +4 -1
- data/example/ramaze.ru +12 -0
- data/example/thin.god +70 -66
- data/lib/rack/adapter/rails.rb +0 -3
- data/lib/rack/handler/thin.rb +6 -1
- data/lib/thin.rb +13 -4
- data/lib/thin/command.rb +9 -5
- data/lib/thin/connection.rb +5 -14
- data/lib/thin/connectors/connector.rb +61 -0
- data/lib/thin/connectors/tcp_server.rb +29 -0
- data/lib/thin/connectors/unix_server.rb +48 -0
- data/lib/thin/controllers/cluster.rb +115 -0
- data/lib/thin/controllers/controller.rb +85 -0
- data/lib/thin/controllers/service.rb +73 -0
- data/lib/thin/controllers/service.sh.erb +39 -0
- data/lib/thin/daemonizing.rb +9 -4
- data/lib/thin/headers.rb +2 -2
- data/lib/thin/runner.rb +166 -0
- data/lib/thin/server.rb +109 -89
- data/lib/thin/stats.html.erb +216 -0
- data/lib/thin/stats.rb +1 -249
- data/lib/thin/version.rb +10 -3
- data/lib/thin_parser.so +0 -0
- data/spec/command_spec.rb +0 -1
- data/spec/configs/cluster.yml +9 -0
- data/spec/configs/single.yml +9 -0
- data/spec/{cluster_spec.rb → controllers/cluster_spec.rb} +22 -10
- data/spec/controllers/controller_spec.rb +85 -0
- data/spec/controllers/service_spec.rb +51 -0
- data/spec/daemonizing_spec.rb +73 -9
- data/spec/request/mongrel_spec.rb +39 -0
- data/spec/{request_spec.rb → request/parser_spec.rb} +11 -143
- data/spec/request/perf_spec.rb +50 -0
- data/spec/request/processing_spec.rb +46 -0
- data/spec/runner_spec.rb +135 -0
- data/spec/server/builder_spec.rb +38 -0
- data/spec/server/stopping_spec.rb +45 -0
- data/spec/server/tcp_spec.rb +54 -0
- data/spec/server/unix_socket_spec.rb +30 -0
- data/spec/spec_helper.rb +49 -16
- data/tasks/announce.rake +7 -3
- data/tasks/email.erb +8 -18
- data/tasks/stats.rake +21 -8
- metadata +33 -7
- data/lib/thin/cluster.rb +0 -123
- data/spec/server_spec.rb +0 -200
data/lib/thin/cluster.rb
DELETED
@@ -1,123 +0,0 @@
|
|
1
|
-
module Thin
|
2
|
-
# Control a set of servers.
|
3
|
-
# * Generate start and stop commands and run them.
|
4
|
-
# * Inject the port or socket number in the pid and log filenames.
|
5
|
-
# Servers are started throught the +thin+ command-line script.
|
6
|
-
class Cluster
|
7
|
-
include Logging
|
8
|
-
|
9
|
-
# Path to the +thin+ script used to control the servers.
|
10
|
-
# Leave this to default to use the one in the path.
|
11
|
-
attr_accessor :script
|
12
|
-
|
13
|
-
# Number of servers in the cluster.
|
14
|
-
attr_accessor :size
|
15
|
-
|
16
|
-
# Command line options passed to the thin script
|
17
|
-
attr_accessor :options
|
18
|
-
|
19
|
-
# Create a new cluster of servers launched using +options+.
|
20
|
-
def initialize(options)
|
21
|
-
@options = options.merge(:daemonize => true)
|
22
|
-
@size = @options.delete(:servers)
|
23
|
-
@only = @options.delete(:only)
|
24
|
-
@script = $PROGRAM_NAME
|
25
|
-
|
26
|
-
if socket
|
27
|
-
@options.delete(:address)
|
28
|
-
@options.delete(:port)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def first_port; @options[:port] end
|
33
|
-
def address; @options[:address] end
|
34
|
-
def socket; @options[:socket] end
|
35
|
-
def pid_file; @options[:pid] end
|
36
|
-
def log_file; @options[:log] end
|
37
|
-
|
38
|
-
# Start the servers
|
39
|
-
def start
|
40
|
-
with_each_server { |port| start_server port }
|
41
|
-
end
|
42
|
-
|
43
|
-
# Start a single server
|
44
|
-
def start_server(number)
|
45
|
-
log "Starting server on #{server_id(number)} ... "
|
46
|
-
|
47
|
-
run :start, @options, number
|
48
|
-
end
|
49
|
-
|
50
|
-
# Stop the servers
|
51
|
-
def stop
|
52
|
-
with_each_server { |n| stop_server n }
|
53
|
-
end
|
54
|
-
|
55
|
-
# Stop a single server
|
56
|
-
def stop_server(number)
|
57
|
-
log "Stopping server on #{server_id(number)} ... "
|
58
|
-
|
59
|
-
run :stop, @options, number
|
60
|
-
end
|
61
|
-
|
62
|
-
# Stop and start the servers.
|
63
|
-
def restart
|
64
|
-
stop
|
65
|
-
sleep 0.1 # Let's breath a bit shall we ?
|
66
|
-
start
|
67
|
-
end
|
68
|
-
|
69
|
-
def server_id(number)
|
70
|
-
if socket
|
71
|
-
socket_for(number)
|
72
|
-
else
|
73
|
-
[address, number].join(':')
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def log_file_for(number)
|
78
|
-
include_server_number log_file, number
|
79
|
-
end
|
80
|
-
|
81
|
-
def pid_file_for(number)
|
82
|
-
include_server_number pid_file, number
|
83
|
-
end
|
84
|
-
|
85
|
-
def socket_for(number)
|
86
|
-
include_server_number socket, number
|
87
|
-
end
|
88
|
-
|
89
|
-
def pid_for(number)
|
90
|
-
File.read(pid_file_for(number)).chomp.to_i
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
# Send the command to the +thin+ script
|
95
|
-
def run(cmd, options, number)
|
96
|
-
cmd_options = options.dup
|
97
|
-
cmd_options.merge!(:pid => pid_file_for(number), :log => log_file_for(number))
|
98
|
-
if socket
|
99
|
-
cmd_options.merge!(:socket => socket_for(number))
|
100
|
-
else
|
101
|
-
cmd_options.merge!(:port => number)
|
102
|
-
end
|
103
|
-
Command.run(cmd, cmd_options)
|
104
|
-
end
|
105
|
-
|
106
|
-
def with_each_server
|
107
|
-
if @only
|
108
|
-
yield @only
|
109
|
-
else
|
110
|
-
@size.times do |n|
|
111
|
-
yield socket ? n : (first_port + n)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Add the server port or number in the filename
|
117
|
-
# so each instance get its own file
|
118
|
-
def include_server_number(path, number)
|
119
|
-
ext = File.extname(path)
|
120
|
-
path.gsub(/#{ext}$/, ".#{number}#{ext}")
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
data/spec/server_spec.rb
DELETED
@@ -1,200 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
-
require 'net/http'
|
3
|
-
require 'socket'
|
4
|
-
|
5
|
-
describe Server do
|
6
|
-
before do
|
7
|
-
app = proc do |env|
|
8
|
-
body = ''
|
9
|
-
body << env.inspect
|
10
|
-
body << env['rack.input'].read
|
11
|
-
[200, { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s }, body]
|
12
|
-
end
|
13
|
-
@server = Thin::Server.new('0.0.0.0', 3333, app)
|
14
|
-
@server.timeout = 3
|
15
|
-
@server.silent = true
|
16
|
-
|
17
|
-
@thread = Thread.new { @server.start }
|
18
|
-
sleep 0.1 until @thread.status == 'sleep'
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'should GET from Net::HTTP' do
|
22
|
-
get('/?cthis').should include('cthis')
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'should GET from TCPSocket' do
|
26
|
-
raw("GET /?this HTTP/1.1\r\n\r\n").
|
27
|
-
should include("HTTP/1.1 200 OK",
|
28
|
-
"Content-Type: text/html", "Content-Length: ",
|
29
|
-
"Connection: close", "this")
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'should return empty string on incomplete headers' do
|
33
|
-
raw("GET /?this HTTP/1.1\r\nHost:").should be_empty
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'should return empty string on incorrect Content-Length' do
|
37
|
-
raw("POST / HTTP/1.1\r\nContent-Length: 300\r\n\r\naye").should be_empty
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'should POST from Net::HTTP' do
|
41
|
-
post('/', :arg => 'pirate').should include('arg=pirate')
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'should handle big POST' do
|
45
|
-
big = 'X' * (20 * 1024)
|
46
|
-
post('/', :big => big).should include(big)
|
47
|
-
end
|
48
|
-
|
49
|
-
it "should handle GET in less then #{get_request_time = 0.004} RubySecond" do
|
50
|
-
proc { get('/') }.should be_faster_then(get_request_time)
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should handle POST in less then #{post_request_time = 0.007} RubySecond" do
|
54
|
-
proc { post('/', :file => 'X' * 1000) }.should be_faster_then(post_request_time)
|
55
|
-
end
|
56
|
-
|
57
|
-
it "should retreive remote address" do
|
58
|
-
get('/').should include('"REMOTE_ADDR"=>"127.0.0.1"')
|
59
|
-
end
|
60
|
-
|
61
|
-
it "should wait for current requests before soft stopping" do
|
62
|
-
socket = TCPSocket.new('0.0.0.0', 3333)
|
63
|
-
socket.write("GET / HTTP/1.1")
|
64
|
-
@server.stop # Stop the server in the middle of a request
|
65
|
-
socket.write("\r\n\r\n")
|
66
|
-
|
67
|
-
out = socket.read
|
68
|
-
socket.close
|
69
|
-
|
70
|
-
out.should_not be_empty
|
71
|
-
end
|
72
|
-
|
73
|
-
it "should not accept new requests when soft stopping" do
|
74
|
-
socket = TCPSocket.new('0.0.0.0', 3333)
|
75
|
-
socket.write("GET / HTTP/1.1")
|
76
|
-
@server.stop # Stop the server in the middle of a request
|
77
|
-
|
78
|
-
EventMachine.next_tick do
|
79
|
-
proc { get('/') }.should raise_error(Errno::ECONNRESET)
|
80
|
-
end
|
81
|
-
|
82
|
-
socket.close
|
83
|
-
end
|
84
|
-
|
85
|
-
it "should drop current requests when hard stopping" do
|
86
|
-
socket = TCPSocket.new('0.0.0.0', 3333)
|
87
|
-
socket.write("GET / HTTP/1.1")
|
88
|
-
@server.stop! # Force stop the server in the middle of a request
|
89
|
-
|
90
|
-
EventMachine.next_tick { socket.should be_closed }
|
91
|
-
end
|
92
|
-
|
93
|
-
after do
|
94
|
-
@server.stop!
|
95
|
-
@thread.kill
|
96
|
-
end
|
97
|
-
|
98
|
-
private
|
99
|
-
def get(url)
|
100
|
-
Net::HTTP.get(URI.parse('http://0.0.0.0:3333' + url))
|
101
|
-
end
|
102
|
-
|
103
|
-
def raw(data)
|
104
|
-
socket = TCPSocket.new('0.0.0.0', 3333)
|
105
|
-
socket.write data
|
106
|
-
out = socket.read
|
107
|
-
socket.close
|
108
|
-
out
|
109
|
-
end
|
110
|
-
|
111
|
-
def post(url, params={})
|
112
|
-
Net::HTTP.post_form(URI.parse('http://0.0.0.0:3333' + url), params).body
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
describe Server, 'app configuration' do
|
117
|
-
it "should build app from constructor" do
|
118
|
-
server = Server.new('0.0.0.0', 3000, :works)
|
119
|
-
|
120
|
-
server.app.should == :works
|
121
|
-
end
|
122
|
-
|
123
|
-
it "should build app from builder block" do
|
124
|
-
server = Server.new '0.0.0.0', 3000 do
|
125
|
-
run(proc { |env| :works })
|
126
|
-
end
|
127
|
-
|
128
|
-
server.app.call({}).should == :works
|
129
|
-
end
|
130
|
-
|
131
|
-
it "should use middlewares in builder block" do
|
132
|
-
server = Server.new '0.0.0.0', 3000 do
|
133
|
-
use Rack::ShowExceptions
|
134
|
-
run(proc { |env| :works })
|
135
|
-
end
|
136
|
-
|
137
|
-
server.app.class.should == Rack::ShowExceptions
|
138
|
-
server.app.call({}).should == :works
|
139
|
-
end
|
140
|
-
|
141
|
-
it "should work with Rack url mapper" do
|
142
|
-
server = Server.new '0.0.0.0', 3000 do
|
143
|
-
map '/test' do
|
144
|
-
run(proc { |env| :works })
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
server.app.call({})[0].should == 404
|
149
|
-
server.app.call({'PATH_INFO' => '/test'}).should == :works
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
describe Server, "on UNIX domain socket" do
|
154
|
-
before do
|
155
|
-
app = proc do |env|
|
156
|
-
[200, { 'Content-Type' => 'text/html' }, [env.inspect]]
|
157
|
-
end
|
158
|
-
@server = Thin::Server.new('/tmp/thin_test.sock', nil, app)
|
159
|
-
@server.timeout = 3
|
160
|
-
@server.silent = true
|
161
|
-
|
162
|
-
@thread = Thread.new { @server.start }
|
163
|
-
sleep 0.1 until @thread.status == 'sleep'
|
164
|
-
end
|
165
|
-
|
166
|
-
it "should accept GET request" do
|
167
|
-
get("/?this").should include('this')
|
168
|
-
end
|
169
|
-
|
170
|
-
it "should retreive remote address" do
|
171
|
-
get('/').should include('"REMOTE_ADDR"=>""') # Is that right?
|
172
|
-
end
|
173
|
-
|
174
|
-
it "should handle GET in less then #{get_request_time = 0.002} RubySecond" do
|
175
|
-
proc { get('/') }.should be_faster_then(get_request_time)
|
176
|
-
end
|
177
|
-
|
178
|
-
it "should remove socket file after server stops" do
|
179
|
-
@server.stop!
|
180
|
-
File.exist?('/tmp/thin_test.sock').should be_false
|
181
|
-
end
|
182
|
-
|
183
|
-
after do
|
184
|
-
@server.stop!
|
185
|
-
@thread.kill
|
186
|
-
end
|
187
|
-
|
188
|
-
private
|
189
|
-
def get(url)
|
190
|
-
send_data("GET #{url} HTTP/1.1\r\n\r\n")
|
191
|
-
end
|
192
|
-
|
193
|
-
def send_data(data)
|
194
|
-
socket = UNIXSocket.new('/tmp/thin_test.sock')
|
195
|
-
socket.write data
|
196
|
-
out = socket.read
|
197
|
-
socket.close
|
198
|
-
out
|
199
|
-
end
|
200
|
-
end
|