thin 1.2.4 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -207,6 +207,9 @@ static void header_done(void *data, const char *at, size_t length)
207
207
  if (rb_hash_aref(req, global_query_string) == Qnil) {
208
208
  rb_hash_aset(req, global_query_string, global_empty);
209
209
  }
210
+ if (rb_hash_aref(req, global_path_info) == Qnil) {
211
+ rb_hash_aset(req, global_path_info, global_empty);
212
+ }
210
213
 
211
214
  /* set some constants */
212
215
  rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
@@ -32,9 +32,8 @@ module Rack
32
32
  end
33
33
 
34
34
  def rack_based?
35
- ActionController.const_defined?(:Dispatcher) &&
36
- (ActionController::Dispatcher.instance_methods.include?(:call) ||
37
- ActionController::Dispatcher.instance_methods.include?("call"))
35
+ rails_version = ::Rails::VERSION
36
+ rails_version::MAJOR >= 2 && rails_version::MINOR >= 2 && rails_version::TINY >= 3
38
37
  end
39
38
 
40
39
  def load_application
@@ -1,4 +1,9 @@
1
+ require 'socket'
2
+
1
3
  module Thin
4
+ # An exception class to handle the event that server didn't start on time
5
+ class RestartTimeout < RuntimeError; end
6
+
2
7
  module Controllers
3
8
  # Control a set of servers.
4
9
  # * Generate start and stop commands and run them.
@@ -7,7 +12,10 @@ module Thin
7
12
  class Cluster < Controller
8
13
  # Cluster only options that should not be passed in the command sent
9
14
  # to the indiviual servers.
10
- CLUSTER_OPTIONS = [:servers, :only]
15
+ CLUSTER_OPTIONS = [:servers, :only, :onebyone, :wait]
16
+
17
+ # Maximum wait time for the server to be restarted
18
+ DEFAULT_WAIT_TIME = 30 # seconds
11
19
 
12
20
  # Create a new cluster of servers launched using +options+.
13
21
  def initialize(options)
@@ -15,7 +23,7 @@ module Thin
15
23
  # Cluster can only contain daemonized servers
16
24
  @options.merge!(:daemonize => true)
17
25
  end
18
-
26
+
19
27
  def first_port; @options[:port] end
20
28
  def address; @options[:address] end
21
29
  def socket; @options[:socket] end
@@ -23,7 +31,9 @@ module Thin
23
31
  def log_file; @options[:log] end
24
32
  def size; @options[:servers] end
25
33
  def only; @options[:only] end
26
-
34
+ def onebyone; @options[:onebyone] end
35
+ def wait; @options[:wait] end
36
+
27
37
  def swiftiply?
28
38
  @options.has_key?(:swiftiply)
29
39
  end
@@ -54,9 +64,50 @@ module Thin
54
64
 
55
65
  # Stop and start the servers.
56
66
  def restart
57
- stop
58
- sleep 0.1 # Let's breath a bit shall we ?
59
- start
67
+ unless onebyone
68
+ # Let's do a normal restart by defaults
69
+ stop
70
+ sleep 0.1 # Let's breath a bit shall we ?
71
+ start
72
+ else
73
+ with_each_server do |n|
74
+ stop_server(n)
75
+ sleep 0.1 # Let's breath a bit shall we ?
76
+ start_server(n)
77
+ wait_until_server_started(n)
78
+ end
79
+ end
80
+ end
81
+
82
+ def test_socket(number)
83
+ if socket
84
+ UNIXSocket.new(socket_for(number))
85
+ else
86
+ TCPSocket.new(address, number)
87
+ end
88
+ rescue
89
+ nil
90
+ end
91
+
92
+ # Make sure the server is running before moving on to the next one.
93
+ def wait_until_server_started(number)
94
+ log "Waiting for server to start ..."
95
+ STDOUT.flush # Need this to make sure user got the message
96
+
97
+ tries = 0
98
+ loop do
99
+ if test_socket = test_socket(number)
100
+ test_socket.close
101
+ break
102
+ elsif tries < wait
103
+ sleep 1
104
+ tries += 1
105
+ else
106
+ raise RestartTimeout, "The server didn't start in time. Please look at server's log file " +
107
+ "for more information, or set the value of 'wait' in your config " +
108
+ "file to be higher (defaults: 30)."
109
+ end
110
+ end
60
111
  end
61
112
 
62
113
  def server_id(number)
data/lib/thin/request.rb CHANGED
@@ -13,7 +13,11 @@ module Thin
13
13
  MAX_BODY = 1024 * (80 + 32)
14
14
  BODY_TMPFILE = 'thin-body'.freeze
15
15
  MAX_HEADER = 1024 * (80 + 32)
16
-
16
+
17
+ INITIAL_BODY = ''
18
+ # Force external_encoding of request's body to ASCII_8BIT
19
+ INITIAL_BODY.encode!(Encoding::ASCII_8BIT) if INITIAL_BODY.respond_to?(:encode)
20
+
17
21
  # Freeze some HTTP header names & values
18
22
  SERVER_SOFTWARE = 'SERVER_SOFTWARE'.freeze
19
23
  SERVER_NAME = 'SERVER_NAME'.freeze
@@ -49,7 +53,7 @@ module Thin
49
53
  @parser = Thin::HttpParser.new
50
54
  @data = ''
51
55
  @nparsed = 0
52
- @body = StringIO.new
56
+ @body = StringIO.new(INITIAL_BODY.dup)
53
57
  @env = {
54
58
  SERVER_SOFTWARE => SERVER,
55
59
  SERVER_NAME => LOCALHOST,
data/lib/thin/runner.rb CHANGED
@@ -41,7 +41,8 @@ module Thin
41
41
  :pid => 'tmp/pids/thin.pid',
42
42
  :max_conns => Server::DEFAULT_MAXIMUM_CONNECTIONS,
43
43
  :max_persistent_conns => Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS,
44
- :require => []
44
+ :require => [],
45
+ :wait => Controllers::Cluster::DEFAULT_WAIT_TIME
45
46
  }
46
47
 
47
48
  parse!
@@ -96,6 +97,8 @@ module Thin
96
97
  opts.on("-o", "--only NUM", "Send command to only one server of the cluster") { |only| @options[:only] = only.to_i }
97
98
  opts.on("-C", "--config FILE", "Load options from config file") { |file| @options[:config] = file }
98
99
  opts.on( "--all [DIR]", "Send command to each config files in DIR") { |dir| @options[:all] = dir } if Thin.linux?
100
+ opts.on("-O", "--onebyone", "Restart the cluster one by one (only works with restart command)") { @options[:onebyone] = true }
101
+ opts.on("-w", "--wait NUM", "Maximum wait time for server to be started in seconds (use with -O)") { |time| @options[:wait] = time.to_i }
99
102
  end
100
103
 
101
104
  opts.separator ""
@@ -105,7 +108,7 @@ module Thin
105
108
  opts.on("-t", "--timeout SEC", "Request or command timeout in sec " +
106
109
  "(default: #{@options[:timeout]})") { |sec| @options[:timeout] = sec.to_i }
107
110
  opts.on("-f", "--force", "Force the execution of the command") { @options[:force] = true }
108
- opts.on( "--max-conns NUM", "Maximum number of connections " +
111
+ opts.on( "--max-conns NUM", "Maximum number of open file descriptors " +
109
112
  "(default: #{@options[:max_conns]})",
110
113
  "Might require sudo to set higher then 1024") { |num| @options[:max_conns] = num.to_i } unless Thin.win?
111
114
  opts.on( "--max-persistent-conns NUM",
data/lib/thin/version.rb CHANGED
@@ -6,11 +6,11 @@ module Thin
6
6
  module VERSION #:nodoc:
7
7
  MAJOR = 1
8
8
  MINOR = 2
9
- TINY = 4
9
+ TINY = 5
10
10
 
11
11
  STRING = [MAJOR, MINOR, TINY].join('.')
12
12
 
13
- CODENAME = "Flaming Astroboy".freeze
13
+ CODENAME = "This Is Not A Web Server".freeze
14
14
 
15
15
  RACK = [1, 0].freeze # Rack protocol version
16
16
  end
@@ -232,4 +232,36 @@ describe Cluster, "with Swiftiply" do
232
232
  def options_for_swiftiply(number)
233
233
  { :address => '0.0.0.0', :port => 3000, :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :pid => "thin.#{number}.pid", :chdir => "/rails_app", :swiftiply => true }
234
234
  end
235
+ end
236
+
237
+ describe Cluster, "rolling restart" do
238
+ before do
239
+ @cluster = Cluster.new(:chdir => '/rails_app',
240
+ :address => '0.0.0.0',
241
+ :port => 3000,
242
+ :servers => 2,
243
+ :timeout => 10,
244
+ :log => 'thin.log',
245
+ :pid => 'thin.pid',
246
+ :onebyone => true,
247
+ :wait => 30
248
+ )
249
+ end
250
+
251
+ it "should restart servers one by one" do
252
+ Command.should_receive(:run).with(:stop, options_for_port(3000))
253
+ Command.should_receive(:run).with(:start, options_for_port(3000))
254
+ @cluster.should_receive(:wait_until_server_started).with(3000)
255
+
256
+ Command.should_receive(:run).with(:stop, options_for_port(3001))
257
+ Command.should_receive(:run).with(:start, options_for_port(3001))
258
+ @cluster.should_receive(:wait_until_server_started).with(3001)
259
+
260
+ @cluster.restart
261
+ end
262
+
263
+ private
264
+ def options_for_port(port)
265
+ { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
266
+ end
235
267
  end
@@ -209,7 +209,35 @@ EOS
209
209
  parser = HttpParser.new
210
210
  req = {}
211
211
  nread = parser.execute(req, req_str, 0)
212
- req.should be_has_key('HTTP_HOS_T')
212
+ req.should have_key('HTTP_HOS_T')
213
213
  }
214
214
  end
215
+
216
+ it "should parse PATH_INFO with semicolon" do
217
+ qs = "QUERY_STRING"
218
+ pi = "PATH_INFO"
219
+ {
220
+ "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
221
+ "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
222
+ "/1;a=b" => { qs => "", pi => "/1;a=b" },
223
+ "/1;a=b?" => { qs => "", pi => "/1;a=b" },
224
+ "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
225
+ "*" => { qs => "", pi => "" },
226
+ }.each do |uri, expect|
227
+ parser = HttpParser.new
228
+ env = {}
229
+ nread = parser.execute(env, "GET #{uri} HTTP/1.1\r\nHost: www.example.com\r\n\r\n", 0)
230
+
231
+ env[pi].should == expect[pi]
232
+ env[qs].should == expect[qs]
233
+ env["REQUEST_URI"].should == uri
234
+
235
+ next if uri == "*"
236
+
237
+ # Validate w/ Ruby's URI.parse
238
+ uri = URI.parse("http://example.com#{uri}")
239
+ env[qs].should == uri.query.to_s
240
+ env[pi].should == uri.path
241
+ end
242
+ end
215
243
  end
@@ -42,4 +42,8 @@ describe Request, 'processing' do
42
42
  big_headers = "X-Test: X\r\n" * (1024 * (80 + 32))
43
43
  proc { R("GET / HTTP/1.1\r\n#{big_headers}\r\n") }.should raise_error(InvalidRequest)
44
44
  end
45
+
46
+ it "should set body external encoding to ASCII_8BIT" do
47
+ Request.new.body.external_encoding.should == Encoding::ASCII_8BIT
48
+ end
45
49
  end
data/spec/spec_helper.rb CHANGED
@@ -4,7 +4,6 @@ require 'spec'
4
4
  require 'benchmark'
5
5
  require 'timeout'
6
6
  require 'fileutils'
7
- require 'benchmark_unit'
8
7
  require 'net/http'
9
8
  require 'socket'
10
9
 
@@ -23,6 +22,7 @@ end
23
22
  module Matchers
24
23
  class BeFasterThen
25
24
  def initialize(max_time)
25
+ require 'benchmark_unit'
26
26
  @max_time = max_time
27
27
  end
28
28
 
data/tasks/gem.rake CHANGED
@@ -1,8 +1,6 @@
1
1
  require 'rake/gempackagetask'
2
2
  require 'yaml'
3
3
 
4
- WIN_SUFFIX = ENV['WIN_SUFFIX'] || 'x86-mswin32'
5
-
6
4
  task :clean => :clobber_package
7
5
 
8
6
  Thin::GemSpec = Gem::Specification.new do |s|
@@ -59,16 +57,10 @@ end
59
57
  task :gem => :tag_warn
60
58
 
61
59
  namespace :gem do
62
- desc "Update the gemspec for GitHub's gem server"
63
- task :github do
64
- File.open("thin.gemspec", 'w') { |f| f << YAML.dump(Thin::GemSpec) }
65
- end
66
-
67
- desc 'Upload gems (ruby & win32) to rubyforge.org'
68
- task :upload => :gem do
69
- sh 'rubyforge login'
70
- sh "rubyforge add_release thin thin #{Thin::VERSION::STRING} pkg/#{Thin::GemSpec.full_name}.gem"
71
- sh "rubyforge add_file thin thin #{Thin::VERSION::STRING} pkg/#{Thin::GemSpec.full_name}.gem"
72
- sh "rubyforge add_file thin thin #{Thin::VERSION::STRING} pkg/#{Thin::GemSpec.full_name}-#{WIN_SUFFIX}.gem"
60
+ desc 'Upload gems to gemcutter.org'
61
+ task :push => :gem do
62
+ Dir["pkg/#{Thin::GemSpec.full_name}*.gem"].each do |file|
63
+ puts "gem push #{file}"
64
+ end
73
65
  end
74
66
  end
data/tasks/spec.rake CHANGED
@@ -1,49 +1,43 @@
1
1
  CLEAN.include %w(coverage tmp log)
2
2
 
3
- if RUBY_1_9 # RSpec not yet working w/ Ruby 1.9
4
- task :spec do
5
- warn 'RSpec not yet supporting Ruby 1.9, so cannot run the specs :('
6
- end
7
- else
8
- require 'spec/rake/spectask'
9
-
10
- PERF_SPECS = FileList['spec/perf/*_spec.rb']
11
- WIN_SPECS = %w(
12
- spec/backends/unix_server_spec.rb
13
- spec/controllers/service_spec.rb
14
- spec/daemonizing_spec.rb
15
- spec/server/unix_socket_spec.rb
16
- spec/server/swiftiply_spec.rb
17
- )
18
- # HACK Event machine causes some problems when running multiple
19
- # tests in the same VM so we split the specs in 2 before I find
20
- # a better solution...
21
- SPECS2 = %w(spec/server/threaded_spec.rb spec/server/tcp_spec.rb)
22
- SPECS = FileList['spec/**/*_spec.rb'] - PERF_SPECS - SPECS2
23
-
24
- def spec_task(name, specs)
25
- Spec::Rake::SpecTask.new(name) do |t|
26
- t.libs << 'lib'
27
- t.spec_opts = %w(-fs -c)
28
- t.spec_files = specs
29
- end
3
+ require 'spec/rake/spectask'
4
+
5
+ PERF_SPECS = FileList['spec/perf/*_spec.rb']
6
+ WIN_SPECS = %w(
7
+ spec/backends/unix_server_spec.rb
8
+ spec/controllers/service_spec.rb
9
+ spec/daemonizing_spec.rb
10
+ spec/server/unix_socket_spec.rb
11
+ spec/server/swiftiply_spec.rb
12
+ )
13
+ # HACK Event machine causes some problems when running multiple
14
+ # tests in the same VM so we split the specs in 2 before I find
15
+ # a better solution...
16
+ SPECS2 = %w(spec/server/threaded_spec.rb spec/server/tcp_spec.rb)
17
+ SPECS = FileList['spec/**/*_spec.rb'] - PERF_SPECS - SPECS2
18
+
19
+ def spec_task(name, specs)
20
+ Spec::Rake::SpecTask.new(name) do |t|
21
+ t.libs << 'lib'
22
+ t.spec_opts = %w(-fs -c)
23
+ t.spec_files = specs
30
24
  end
31
-
32
- desc "Run all examples"
33
- spec_task :spec, SPECS
34
- spec_task :spec2, SPECS2
35
- task :spec => [:compile, :spec2]
36
-
37
- desc "Run all performance examples"
38
- spec_task 'spec:perf', PERF_SPECS
39
-
40
- task :check_benchmark_unit_gem do
41
- begin
42
- require 'benchmark_unit'
43
- rescue LoadError
44
- abort "To run specs, install benchmark_unit gem"
45
- end
25
+ end
26
+
27
+ desc "Run all examples"
28
+ spec_task :spec, SPECS
29
+ spec_task :spec2, SPECS2
30
+ task :spec => [:compile, :spec2]
31
+
32
+ desc "Run all performance examples"
33
+ spec_task 'spec:perf', PERF_SPECS
34
+
35
+ task :check_benchmark_unit_gem do
36
+ begin
37
+ require 'benchmark_unit'
38
+ rescue LoadError
39
+ abort "To run specs, install benchmark_unit gem"
46
40
  end
47
-
48
- task 'spec:perf' => :check_benchmark_unit_gem
49
- end
41
+ end
42
+
43
+ task 'spec:perf' => :check_benchmark_unit_gem
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-Andre Cournoyer
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-09-05 00:00:00 -04:00
12
+ date: 2009-12-13 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -72,19 +72,14 @@ files:
72
72
  - example/thin_solaris_smf.erb
73
73
  - example/thin_solaris_smf.readme.txt
74
74
  - example/vlad.rake
75
- - lib/rack
76
- - lib/rack/adapter
77
75
  - lib/rack/adapter/loader.rb
78
76
  - lib/rack/adapter/rails.rb
79
- - lib/thin
80
- - lib/thin/backends
81
77
  - lib/thin/backends/base.rb
82
78
  - lib/thin/backends/swiftiply_client.rb
83
79
  - lib/thin/backends/tcp_server.rb
84
80
  - lib/thin/backends/unix_server.rb
85
81
  - lib/thin/command.rb
86
82
  - lib/thin/connection.rb
87
- - lib/thin/controllers
88
83
  - lib/thin/controllers/cluster.rb
89
84
  - lib/thin/controllers/controller.rb
90
85
  - lib/thin/controllers/service.rb
@@ -101,53 +96,36 @@ files:
101
96
  - lib/thin/statuses.rb
102
97
  - lib/thin/version.rb
103
98
  - lib/thin.rb
104
- - spec/backends
105
99
  - spec/backends/swiftiply_client_spec.rb
106
100
  - spec/backends/tcp_server_spec.rb
107
101
  - spec/backends/unix_server_spec.rb
108
102
  - spec/command_spec.rb
109
- - spec/configs
110
103
  - spec/configs/cluster.yml
111
104
  - spec/configs/single.yml
112
105
  - spec/connection_spec.rb
113
- - spec/controllers
114
106
  - spec/controllers/cluster_spec.rb
115
107
  - spec/controllers/controller_spec.rb
116
108
  - spec/controllers/service_spec.rb
117
109
  - spec/daemonizing_spec.rb
118
110
  - spec/headers_spec.rb
119
111
  - spec/logging_spec.rb
120
- - spec/perf
121
112
  - spec/perf/request_perf_spec.rb
122
113
  - spec/perf/response_perf_spec.rb
123
114
  - spec/perf/server_perf_spec.rb
124
- - spec/rack
125
115
  - spec/rack/loader_spec.rb
126
116
  - spec/rack/rails_adapter_spec.rb
127
- - spec/rails_app
128
- - spec/rails_app/app
129
- - spec/rails_app/app/controllers
130
117
  - spec/rails_app/app/controllers/application.rb
131
118
  - spec/rails_app/app/controllers/simple_controller.rb
132
- - spec/rails_app/app/helpers
133
119
  - spec/rails_app/app/helpers/application_helper.rb
134
- - spec/rails_app/app/views
135
- - spec/rails_app/app/views/simple
136
120
  - spec/rails_app/app/views/simple/index.html.erb
137
- - spec/rails_app/config
138
121
  - spec/rails_app/config/boot.rb
139
122
  - spec/rails_app/config/environment.rb
140
- - spec/rails_app/config/environments
141
123
  - spec/rails_app/config/environments/development.rb
142
124
  - spec/rails_app/config/environments/production.rb
143
125
  - spec/rails_app/config/environments/test.rb
144
- - spec/rails_app/config/initializers
145
126
  - spec/rails_app/config/initializers/inflections.rb
146
127
  - spec/rails_app/config/initializers/mime_types.rb
147
128
  - spec/rails_app/config/routes.rb
148
- - spec/rails_app/log
149
- - spec/rails_app/log/mongrel_debug
150
- - spec/rails_app/public
151
129
  - spec/rails_app/public/404.html
152
130
  - spec/rails_app/public/422.html
153
131
  - spec/rails_app/public/500.html
@@ -155,40 +133,33 @@ files:
155
133
  - spec/rails_app/public/dispatch.fcgi
156
134
  - spec/rails_app/public/dispatch.rb
157
135
  - spec/rails_app/public/favicon.ico
158
- - spec/rails_app/public/images
159
136
  - spec/rails_app/public/images/rails.png
160
137
  - spec/rails_app/public/index.html
161
- - spec/rails_app/public/javascripts
162
138
  - spec/rails_app/public/javascripts/application.js
163
139
  - spec/rails_app/public/javascripts/controls.js
164
140
  - spec/rails_app/public/javascripts/dragdrop.js
165
141
  - spec/rails_app/public/javascripts/effects.js
166
142
  - spec/rails_app/public/javascripts/prototype.js
167
143
  - spec/rails_app/public/robots.txt
168
- - spec/rails_app/script
169
144
  - spec/rails_app/script/about
170
145
  - spec/rails_app/script/console
171
146
  - spec/rails_app/script/destroy
172
147
  - spec/rails_app/script/generate
173
- - spec/rails_app/script/performance
174
148
  - spec/rails_app/script/performance/benchmarker
175
149
  - spec/rails_app/script/performance/profiler
176
150
  - spec/rails_app/script/performance/request
177
151
  - spec/rails_app/script/plugin
178
- - spec/rails_app/script/process
179
152
  - spec/rails_app/script/process/inspector
180
153
  - spec/rails_app/script/process/reaper
181
154
  - spec/rails_app/script/process/spawner
182
155
  - spec/rails_app/script/runner
183
156
  - spec/rails_app/script/server
184
- - spec/request
185
157
  - spec/request/mongrel_spec.rb
186
158
  - spec/request/parser_spec.rb
187
159
  - spec/request/persistent_spec.rb
188
160
  - spec/request/processing_spec.rb
189
161
  - spec/response_spec.rb
190
162
  - spec/runner_spec.rb
191
- - spec/server
192
163
  - spec/server/builder_spec.rb
193
164
  - spec/server/pipelining_spec.rb
194
165
  - spec/server/robustness_spec.rb
@@ -217,6 +188,8 @@ files:
217
188
  - ext/thin_parser/parser.rl
218
189
  has_rdoc: true
219
190
  homepage: http://code.macournoyer.com/thin/
191
+ licenses: []
192
+
220
193
  post_install_message:
221
194
  rdoc_options: []
222
195
 
@@ -237,9 +210,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
237
210
  requirements: []
238
211
 
239
212
  rubyforge_project: thin
240
- rubygems_version: 1.3.1
213
+ rubygems_version: 1.3.5
241
214
  signing_key:
242
- specification_version: 2
215
+ specification_version: 3
243
216
  summary: A thin and fast web server
244
217
  test_files: []
245
218