thin 0.7.0-x86-mswin32-60 → 0.7.1-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 (47) hide show
  1. data/CHANGELOG +11 -1
  2. data/COMMITTERS +3 -0
  3. data/README +4 -0
  4. data/Rakefile +2 -0
  5. data/ext/thin_parser/thin.c +9 -0
  6. data/lib/thin.rb +5 -5
  7. data/lib/thin/backends/base.rb +114 -0
  8. data/lib/thin/{connectors → backends}/swiftiply_client.rb +6 -6
  9. data/lib/thin/{connectors → backends}/tcp_server.rb +2 -2
  10. data/lib/thin/{connectors → backends}/unix_server.rb +19 -8
  11. data/lib/thin/connection.rb +3 -3
  12. data/lib/thin/controllers/controller.rb +6 -5
  13. data/lib/thin/controllers/service.rb +3 -1
  14. data/lib/thin/daemonizing.rb +20 -7
  15. data/lib/thin/logging.rb +1 -1
  16. data/lib/thin/request.rb +5 -5
  17. data/lib/thin/server.rb +63 -78
  18. data/lib/thin/version.rb +4 -2
  19. data/lib/thin_parser.so +0 -0
  20. data/spec/{connectors → backends}/swiftiply_client_spec.rb +10 -10
  21. data/spec/{connectors → backends}/tcp_server_spec.rb +5 -5
  22. data/spec/backends/unix_server_spec.rb +37 -0
  23. data/spec/command_spec.rb +1 -0
  24. data/spec/connection_spec.rb +1 -1
  25. data/spec/controllers/controller_spec.rb +1 -0
  26. data/spec/daemonizing_spec.rb +31 -23
  27. data/spec/{request/perf_spec.rb → perf/request_perf_spec.rb} +0 -0
  28. data/spec/perf/response_perf_spec.rb +19 -0
  29. data/spec/perf/server_perf_spec.rb +39 -0
  30. data/spec/request/mongrel_spec.rb +1 -1
  31. data/spec/response_spec.rb +0 -11
  32. data/spec/runner_spec.rb +4 -4
  33. data/spec/server/builder_spec.rb +8 -3
  34. data/spec/server/pipelining_spec.rb +6 -5
  35. data/spec/server/swiftiply_spec.rb +25 -20
  36. data/spec/server/tcp_spec.rb +1 -9
  37. data/spec/server/unix_socket_spec.rb +1 -5
  38. data/spec/server_spec.rb +4 -7
  39. data/spec/spec_helper.rb +29 -5
  40. data/tasks/announce.rake +2 -2
  41. data/tasks/email.erb +13 -15
  42. data/tasks/ext.rake +28 -24
  43. data/tasks/gem.rake +8 -4
  44. data/tasks/spec.rake +14 -9
  45. metadata +19 -15
  46. data/lib/thin/connectors/connector.rb +0 -73
  47. data/spec/connectors/unix_server_spec.rb +0 -43
@@ -0,0 +1,19 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Response, 'performance' do
4
+ before do
5
+ @response = Response.new
6
+ @response.body = ''
7
+ end
8
+
9
+ it "should be fast" do
10
+ @response.body << <<-EOS
11
+ <html><head><title>Dir listing</title></head>
12
+ <body><h1>Listing stuff</h1><ul>
13
+ #{'<li>Hi!</li>' * 100}
14
+ </ul></body></html>
15
+ EOS
16
+
17
+ proc { @response.each { |l| l } }.should be_faster_then(0.00011)
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Server, 'performance' do
4
+ before do
5
+ start_server do |env|
6
+ body = env.inspect + env['rack.input'].read
7
+ [200, { 'Content-Length' => body.size.to_s }, body]
8
+ end
9
+ end
10
+
11
+ it "should handle GET in less then #{get_request_time = 0.0045} RubySecond" do
12
+ proc { get('/') }.should be_faster_then(get_request_time)
13
+ end
14
+
15
+ it "should handle POST in less then #{post_request_time = 0.007} RubySecond" do
16
+ proc { post('/', :file => 'X' * 1000) }.should be_faster_then(post_request_time)
17
+ end
18
+
19
+ after do
20
+ stop_server
21
+ end
22
+ end
23
+
24
+ describe Server, 'UNIX socket performance' do
25
+ before do
26
+ start_server('/tmp/thin_test.sock') do |env|
27
+ body = env.inspect + env['rack.input'].read
28
+ [200, { 'Content-Length' => body.size.to_s }, body]
29
+ end
30
+ end
31
+
32
+ it "should handle GET in less then #{get_request_time = 0.002} RubySecond" do
33
+ proc { get('/') }.should be_faster_then(get_request_time)
34
+ end
35
+
36
+ after do
37
+ stop_server
38
+ end
39
+ end
@@ -8,7 +8,7 @@ describe Request, 'legacy Mongrel tests' do
8
8
  end
9
9
 
10
10
  it 'should raise error on large mangled field values' do
11
- proc { R("GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 100*1024+(1024), false)}\r\n\r\n") }.
11
+ proc { R("GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024*1024, false)}\r\n\r\n") }.
12
12
  should raise_error(InvalidRequest)
13
13
  end
14
14
 
@@ -51,18 +51,7 @@ describe Response do
51
51
  @response.each { |l| out << l }
52
52
  out.should include("\r\n\r\n<html></html>")
53
53
  end
54
-
55
- it "should be fast" do
56
- @response.body << <<-EOS
57
- <html><head><title>Dir listing</title></head>
58
- <body><h1>Listing stuff</h1><ul>
59
- #{'<li>Hi!</li>' * 100}
60
- </ul></body></html>
61
- EOS
62
54
 
63
- proc { @response.each { |l| l } }.should be_faster_then(0.00011)
64
- end
65
-
66
55
  it "should not be persistent by default" do
67
56
  @response.should_not be_persistent
68
57
  end
@@ -36,7 +36,7 @@ describe Runner do
36
36
 
37
37
  controller = mock('controller')
38
38
  controller.should_receive(:start)
39
- Controller.should_receive(:new).and_return(controller)
39
+ Controllers::Controller.should_receive(:new).and_return(controller)
40
40
 
41
41
  runner.run!
42
42
  end
@@ -46,7 +46,7 @@ describe Runner do
46
46
 
47
47
  controller = mock('cluster')
48
48
  controller.should_receive(:start)
49
- Cluster.should_receive(:new).and_return(controller)
49
+ Controllers::Cluster.should_receive(:new).and_return(controller)
50
50
 
51
51
  runner.run!
52
52
  end
@@ -84,7 +84,7 @@ describe Runner, 'with config file' do
84
84
  controller = mock('controller')
85
85
  controller.should_receive(:respond_to?).with('start').and_return(true)
86
86
  controller.should_receive(:start)
87
- Cluster.should_receive(:new).and_return(controller)
87
+ Controllers::Cluster.should_receive(:new).and_return(controller)
88
88
  expected_dir = File.expand_path('spec/rails_app')
89
89
 
90
90
  begin
@@ -106,7 +106,7 @@ describe Runner, "service" do
106
106
  Thin.stub!(:linux?).and_return(true)
107
107
 
108
108
  @controller = mock('service')
109
- Service.stub!(:new).and_return(@controller)
109
+ Controllers::Service.stub!(:new).and_return(@controller)
110
110
  end
111
111
 
112
112
  it "should use Service controller when controlling all servers" do
@@ -28,11 +28,16 @@ describe Server, 'app builder' do
28
28
  it "should work with Rack url mapper" do
29
29
  server = Server.new '0.0.0.0', 3000 do
30
30
  map '/test' do
31
- run(proc { |env| :works })
31
+ run(proc { |env| [200, {}, 'Found /test'] })
32
32
  end
33
33
  end
34
34
 
35
- server.app.call({})[0].should == 404
36
- server.app.call({'PATH_INFO' => '/test'}).should == :works
35
+ default_env = { 'SCRIPT_NAME' => '' }
36
+
37
+ server.app.call(default_env.update('PATH_INFO' => '/'))[0].should == 404
38
+
39
+ status, headers, body = server.app.call(default_env.update('PATH_INFO' => '/test'))
40
+ status.should == 200
41
+ body.should == 'Found /test'
37
42
  end
38
43
  end
@@ -29,6 +29,7 @@ describe Server, "HTTP pipelining" do
29
29
  socket.write "GET /first HTTP/1.1\r\n\r\n"
30
30
  socket.flush
31
31
  socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
32
+ socket.flush
32
33
  response = socket.read
33
34
  socket.close
34
35
 
@@ -75,8 +76,8 @@ describe Server, "HTTP pipelining" do
75
76
  socket2.write "GET / HTTP/1.1\r\n\r\n"
76
77
  socket2.flush
77
78
 
78
- @server.connector.persistent_connection_count.should == 1
79
- @server.connector.size.should == 2
79
+ @server.backend.persistent_connection_count.should == 1
80
+ @server.backend.size.should == 2
80
81
 
81
82
  socket1.close
82
83
  socket2.close
@@ -87,14 +88,14 @@ describe Server, "HTTP pipelining" do
87
88
  socket.write "GET / HTTP/1.1\r\n\r\n"
88
89
  socket.flush
89
90
 
90
- @server.connector.persistent_connection_count.should == 1
91
+ @server.backend.persistent_connection_count.should == 1
91
92
 
92
93
  socket.write "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
93
94
  socket.close
94
95
 
95
96
  wait_for_requests_to_complete!
96
97
 
97
- @server.connector.persistent_connection_count.should == 0
98
+ @server.backend.persistent_connection_count.should == 0
98
99
  end
99
100
 
100
101
  after do
@@ -103,6 +104,6 @@ describe Server, "HTTP pipelining" do
103
104
 
104
105
  private
105
106
  def wait_for_requests_to_complete!
106
- sleep 0.1 until @server.connector.size == 0
107
+ sleep 0.1 until @server.backend.size == 0
107
108
  end
108
109
  end
@@ -1,27 +1,32 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
 
3
- describe Server, 'on Swiftiply' do
4
- before do
5
- @swiftiply = fork do
6
- exec "swiftiply -c #{File.dirname(__FILE__)}/swiftiply.yml"
3
+ if SWIFTIPLY_PATH.empty?
4
+ warn "Ignoring Server on Swiftiply specs, gem install swiftiply to run"
5
+ else
6
+ describe Server, 'on Swiftiply' do
7
+ before do
8
+ @swiftiply = fork do
9
+ exec "#{SWIFTIPLY_PATH} -c #{File.dirname(__FILE__)}/swiftiply.yml"
10
+ end
11
+ wait_for_socket('0.0.0.0', 3333)
12
+ sleep 2 # HACK ooh boy, I wish I knew how to make those specs more stable...
13
+ start_server(Backends::SwiftiplyClient.new('0.0.0.0', 5555, nil), nil, false) do |env|
14
+ body = env.inspect + env['rack.input'].read
15
+ [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s }, body]
16
+ end
7
17
  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
18
 
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
19
+ it 'should GET from Net::HTTP' do
20
+ Net::HTTP.get(URI.parse("http://0.0.0.0:3333/?cthis")).should include('cthis')
21
+ end
18
22
 
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
23
+ it 'should POST from Net::HTTP' do
24
+ Net::HTTP.post_form(URI.parse("http://0.0.0.0:3333/"), :arg => 'pirate').body.should include('arg=pirate')
25
+ end
22
26
 
23
- after do
24
- stop_server
25
- Process.kill(9, @swiftiply)
27
+ after do
28
+ stop_server
29
+ Process.kill(9, @swiftiply)
30
+ end
26
31
  end
27
- end
32
+ end
@@ -35,15 +35,7 @@ describe Server, 'on TCP socket' do
35
35
  big = 'X' * (20 * 1024)
36
36
  post('/', :big => big).should include(big)
37
37
  end
38
-
39
- it "should handle GET in less then #{get_request_time = 0.0045} RubySecond" do
40
- proc { get('/') }.should be_faster_then(get_request_time)
41
- end
42
-
43
- it "should handle POST in less then #{post_request_time = 0.007} RubySecond" do
44
- proc { post('/', :file => 'X' * 1000) }.should be_faster_then(post_request_time)
45
- end
46
-
38
+
47
39
  it "should retreive remote address" do
48
40
  get('/').should include('"REMOTE_ADDR"=>"127.0.0.1"')
49
41
  end
@@ -12,11 +12,7 @@ describe Server, "on UNIX domain socket" do
12
12
  end
13
13
 
14
14
  it "should retreive remote address" do
15
- get('/').should include('"REMOTE_ADDR"=>""') # Is that right?
16
- end
17
-
18
- it "should handle GET in less then #{get_request_time = 0.002} RubySecond" do
19
- proc { get('/') }.should be_faster_then(get_request_time)
15
+ get('/').should include('"REMOTE_ADDR"=>"127.0.0.1"')
20
16
  end
21
17
 
22
18
  it "should remove socket file after server stops" do
@@ -5,18 +5,15 @@ describe Server do
5
5
  @server = Server.new('0.0.0.0', 3000)
6
6
  end
7
7
 
8
- it "should set descriptor table size" do
9
- @server.should_receive(:log).once
8
+ it "should set maximum_connections size" do
10
9
  @server.maximum_connections = 100
11
- @server.set_descriptor_table_size!
10
+ @server.config
12
11
  @server.maximum_connections.should == 100
13
12
  end
14
13
 
15
- it "should warn when descriptor table size too large" do
16
- @server.stub!(:log)
17
- @server.should_receive(:log).with(/^!!/)
14
+ it "should set lower maximum_connections size when too large" do
18
15
  @server.maximum_connections = 100_000
19
- @server.set_descriptor_table_size!
16
+ @server.config
20
17
  @server.maximum_connections.should < 100_000
21
18
  end
22
19
  end
@@ -14,6 +14,8 @@ FileUtils.mkdir_p File.dirname(__FILE__) + '/../log'
14
14
  Command.script = File.dirname(__FILE__) + '/../bin/thin'
15
15
  Logging.silent = true
16
16
 
17
+ SWIFTIPLY_PATH = `which swiftiply`.chomp unless Object.const_defined?(:SWIFTIPLY_PATH)
18
+
17
19
  module Matchers
18
20
  class BeFasterThen
19
21
  def initialize(max_time)
@@ -136,21 +138,43 @@ module Helpers
136
138
  request
137
139
  end
138
140
 
139
- def start_server(*args, &app)
140
- @server = Thin::Server.new(args[0] || '0.0.0.0', args[1] || 3333, app)
141
+ def start_server(address='0.0.0.0', port=3333, wait_for_socket=true, &app)
142
+ @server = Thin::Server.new(address, port, app)
141
143
  @server.timeout = 3
142
144
 
143
145
  @thread = Thread.new { @server.start }
144
- sleep 0.1 until @server.running? && EventMachine.reactor_running?
146
+ if wait_for_socket
147
+ wait_for_socket(address, port)
148
+ else
149
+ # If we can't ping the address fallback to just wait for the server to run
150
+ sleep 1 until @server.running?
151
+ end
145
152
  end
146
153
 
147
154
  def stop_server
148
155
  @server.stop!
149
156
  @thread.kill
157
+ raise "Reactor still running, wtf?" if EventMachine.reactor_running?
158
+ end
159
+
160
+ def wait_for_socket(address='0.0.0.0', port=3333, timeout=5)
161
+ Timeout.timeout(timeout) do
162
+ loop do
163
+ begin
164
+ if address.include?('/')
165
+ UNIXSocket.new(address).close
166
+ else
167
+ TCPSocket.new(address, port).close
168
+ end
169
+ return true
170
+ rescue
171
+ end
172
+ end
173
+ end
150
174
  end
151
175
 
152
176
  def send_data(data)
153
- if @server.connector.class == Connectors::UnixServer
177
+ if @server.backend.class == Backends::UnixServer
154
178
  socket = UNIXSocket.new(@server.socket)
155
179
  else
156
180
  socket = TCPSocket.new(@server.host, @server.port)
@@ -162,7 +186,7 @@ module Helpers
162
186
  end
163
187
 
164
188
  def get(url)
165
- if @server.connector.class == Connectors::UnixServer
189
+ if @server.backend.class == Backends::UnixServer
166
190
  send_data("GET #{url} HTTP/1.1\r\nConnection: close\r\n\r\n")
167
191
  else
168
192
  Net::HTTP.get(URI.parse("http://#{@server.host}:#{@server.port}" + url))
@@ -1,7 +1,7 @@
1
1
  require 'erb'
2
2
 
3
3
  MSG_TEMPLATE = File.dirname(__FILE__) + '/email.erb'
4
- SEND_TO = %w(thin-ruby@googlegroups.com eventmachine-talk@rubyforge.org Rubymtl@lists.artengine.ca ruby-talk@ruby-lang.org montreal-on-rails@googlegroups.com)
4
+ SEND_TO = %w(thin-ruby@googlegroups.com eventmachine-talk@rubyforge.org ruby-talk@ruby-lang.org montreal-on-rails@googlegroups.com)
5
5
 
6
6
  desc 'Generate a template for the new version annoucement'
7
7
  task :ann do
@@ -14,7 +14,7 @@ Subject: [ANN] Thin #{Thin::VERSION::STRING} #{Thin::VERSION::CODENAME} release
14
14
  #{msg}
15
15
  END_OF_MESSAGE
16
16
 
17
- `echo "#{body}" | mate`
17
+ fork { `echo "#{body}" | mate` }
18
18
  end
19
19
 
20
20
  def changelog
@@ -1,34 +1,32 @@
1
1
  Hey all,
2
2
 
3
- Version <%= Thin::VERSION::STRING %> (codename <%= Thin::VERSION::CODENAME %>) of the fastest Ruby server is out!
4
-
5
- http://code.macournoyer.com/thin/
3
+ Thin version <%= Thin::VERSION::STRING %> (codename <%= Thin::VERSION::CODENAME %>) is out!
6
4
 
7
5
  == What's new?
8
6
 
9
7
  <%= changelog %>
10
8
 
11
9
  == Get it!
10
+
11
+ Install Thin from RubyForge:
12
12
 
13
- sudo gem install thin
13
+ gem install thin
14
14
 
15
15
  Or using my mirror:
16
16
 
17
- sudo gem install thin --source http://code.macournoyer.com
18
-
19
- WARNING:
20
- Thin is still alpha software, if you use it on your server you understand the
21
- risks that are involved.
17
+ gem install thin --source http://code.macournoyer.com
22
18
 
23
- == Contribute
19
+ To use Thin with UNIX domain sockets you need EventMachine 0.11.0 from my gem server:
24
20
 
25
- If you're using Thin, let me know and I'll put your site on http://code.macournoyer.com/thin/users/
21
+ gem install eventmachine --source http://code.macournoyer.com
26
22
 
27
- Thin is driven by an active community of passionate coders and benchmarkers.
28
- Please join us, contribute or share some ideas in Thin Google Group:
29
- http://groups.google.com/group/thin-ruby/topics
23
+ == Contribute
30
24
 
31
- Also on IRC: #thin on freenode
25
+ Site: http://code.macournoyer.com/thin/
26
+ Group: http://groups.google.com/group/thin-ruby/topics
27
+ Bugs: http://thin.lighthouseapp.com/projects/7212-thin
28
+ Code: http://github.com/macournoyer/thin
29
+ IRC: #thin on freenode
32
30
 
33
31
  Thanks to all the people who contributed to Thin, EventMachine, Rack and Mongrel.
34
32
 
@@ -1,19 +1,35 @@
1
1
  CLEAN.include %w(**/*.{o,bundle,jar,so,obj,pdb,lib,def,exp,log} ext/*/Makefile ext/*/conftest.dSYM)
2
2
 
3
- EXT_DIR = 'ext/thin_parser'
4
- EXT_BUNDLE = "#{EXT_DIR}/thin_parser.#{Config::CONFIG['DLEXT']}"
5
- EXT_FILES = FileList[
6
- "#{EXT_DIR}/*.c",
7
- "#{EXT_DIR}/*.h",
8
- "#{EXT_DIR}/*.rl",
9
- "#{EXT_DIR}/extconf.rb",
10
- "#{EXT_DIR}/Makefile",
11
- "lib"
12
- ]
3
+ def ext_task(name)
4
+ ext_dir = "ext/#{name}"
5
+ ext_bundle = "#{ext_dir}/#{name}.#{Config::CONFIG['DLEXT']}"
6
+ ext_files = FileList[
7
+ "#{ext_dir}/*.c",
8
+ "#{ext_dir}/*.h",
9
+ "#{ext_dir}/*.rl",
10
+ "#{ext_dir}/extconf.rb",
11
+ "#{ext_dir}/Makefile",
12
+ "lib"
13
+ ]
14
+
15
+ task "compile:#{name}" => ["#{ext_dir}/Makefile", ext_bundle]
16
+ task :compile => "compile:#{name}"
17
+
18
+ file "#{ext_dir}/Makefile" => ["#{ext_dir}/extconf.rb"] do
19
+ cd(ext_dir) { ruby "extconf.rb" }
20
+ end
21
+
22
+ file ext_bundle => ext_files do
23
+ cd ext_dir do
24
+ sh(WIN ? 'nmake' : 'make')
25
+ end
26
+ cp ext_bundle, 'lib/'
27
+ end
28
+ end
13
29
 
14
30
  desc "Compile the Ragel state machines"
15
31
  task :ragel do
16
- Dir.chdir EXT_DIR do
32
+ Dir.chdir 'ext/thin_parser' do
17
33
  target = "parser.c"
18
34
  File.unlink target if File.exist? target
19
35
  sh "ragel parser.rl | rlgen-cd -G2 -o #{target}"
@@ -22,17 +38,5 @@ task :ragel do
22
38
  end
23
39
 
24
40
  desc "Compile the extensions"
25
- task :compile => ["#{EXT_DIR}/Makefile", EXT_BUNDLE]
26
-
41
+ task :compile
27
42
  task :package => :compile
28
-
29
- file "#{EXT_DIR}/Makefile" => ["#{EXT_DIR}/extconf.rb"] do
30
- cd(EXT_DIR) { ruby "extconf.rb" }
31
- end
32
-
33
- file EXT_BUNDLE => EXT_FILES do
34
- cd EXT_DIR do
35
- sh(WIN ? 'nmake' : 'make')
36
- end
37
- cp EXT_BUNDLE, 'lib/'
38
- end