puma 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

@@ -7,17 +7,7 @@ README.md
7
7
  Rakefile
8
8
  TODO
9
9
  bin/puma
10
- examples/builder.rb
11
- examples/camping/README
12
- examples/camping/blog.rb
13
- examples/camping/tepee.rb
14
- examples/httpd.conf
15
- examples/mime.yaml
16
- examples/mongrel.conf
17
- examples/monitrc
18
- examples/random_thrash.rb
19
- examples/simpletest.rb
20
- examples/webrick_compare.rb
10
+ bin/pumactl
21
11
  ext/puma_http11/PumaHttp11Service.java
22
12
  ext/puma_http11/ext_help.h
23
13
  ext/puma_http11/extconf.rb
@@ -30,23 +20,21 @@ ext/puma_http11/org/jruby/puma/Http11.java
30
20
  ext/puma_http11/org/jruby/puma/Http11Parser.java
31
21
  ext/puma_http11/puma_http11.c
32
22
  lib/puma.rb
23
+ lib/puma/app/status.rb
33
24
  lib/puma/cli.rb
34
25
  lib/puma/const.rb
26
+ lib/puma/control_cli.rb
35
27
  lib/puma/events.rb
36
- lib/puma/gems.rb
37
- lib/puma/mime_types.yml
28
+ lib/puma/null_io.rb
38
29
  lib/puma/rack_patch.rb
39
30
  lib/puma/server.rb
40
31
  lib/puma/thread_pool.rb
41
- lib/puma/utils.rb
42
32
  lib/rack/handler/puma.rb
43
33
  puma.gemspec
44
- tasks/gem.rake
45
- tasks/java.rake
46
- tasks/native.rake
47
- tasks/ragel.rake
34
+ test/ab_rs.rb
48
35
  test/lobster.ru
49
36
  test/mime.yaml
37
+ test/test_app_status.rb
50
38
  test/test_cli.rb
51
39
  test/test_http10.rb
52
40
  test/test_http11.rb
data/Rakefile CHANGED
@@ -1,12 +1,90 @@
1
- #
2
- # NOTE: Keep this file clean.
3
- # Add your customizations inside tasks directory.
4
- # Thank You.
5
- #
6
-
7
1
  require 'rubygems'
8
2
 
3
+ require 'hoe'
4
+
9
5
  IS_JRUBY = defined?(RUBY_ENGINE) ? RUBY_ENGINE == "jruby" : false
10
6
 
11
- # load rakefile extensions (tasks)
12
- Dir['tasks/*.rake'].sort.each { |f| load f }
7
+ HOE = Hoe.spec 'puma' do
8
+ self.rubyforge_name = 'puma'
9
+ self.readme_file = "README.md"
10
+ developer 'Evan Phoenix', 'evan@phx.io'
11
+
12
+ spec_extras[:extensions] = ["ext/puma_http11/extconf.rb"]
13
+ spec_extras[:executables] = ['puma', 'pumactl']
14
+
15
+ dependency 'rack', '~> 1.2'
16
+
17
+ extra_dev_deps << ['rake-compiler', "~> 0.7.0"]
18
+
19
+ clean_globs.push('test_*.log', 'log')
20
+ end
21
+
22
+ task :test => [:compile]
23
+
24
+ # hoe/test and rake-compiler don't seem to play well together, so disable
25
+ # hoe/test's .gemtest touch file thingy for now
26
+ HOE.spec.files -= [".gemtest"]
27
+
28
+ file "#{HOE.spec.name}.gemspec" => ['Rakefile'] do |t|
29
+ puts "Generating #{t.name}"
30
+ File.open(t.name, 'w') { |f| f.puts HOE.spec.to_ruby }
31
+ end
32
+
33
+ desc "Generate or update the standalone gemspec file for the project"
34
+ task :gemspec => ["#{HOE.spec.name}.gemspec"]
35
+
36
+ # the following tasks ease the build of C file from Ragel one
37
+
38
+ file 'ext/puma_http11/http11_parser.c' => ['ext/puma_http11/http11_parser.rl'] do |t|
39
+ begin
40
+ sh "ragel #{t.prerequisites.last} -C -G2 -o #{t.name}"
41
+ rescue
42
+ fail "Could not build wrapper using Ragel (it failed or not installed?)"
43
+ end
44
+ end
45
+
46
+ file 'ext/puma_http11/org/jruby/puma/Http11Parser.java' => ['ext/puma_http11/http11_parser.java.rl'] do |t|
47
+ begin
48
+ sh "ragel #{t.prerequisites.last} -J -G2 -o #{t.name}"
49
+ rescue
50
+ fail "Could not build wrapper using Ragel (it failed or not installed?)"
51
+ end
52
+ end
53
+
54
+ if IS_JRUBY
55
+ require 'rake/javaextensiontask'
56
+
57
+ # build http11 java extension
58
+ Rake::JavaExtensionTask.new('puma_http11', HOE.spec)
59
+
60
+ task :ragel => 'ext/puma_http11/org/jruby/puma/Http11Parser.java'
61
+ else
62
+ # use rake-compiler for building the extension
63
+ require 'rake/extensiontask'
64
+
65
+ # build http11 C extension
66
+ Rake::ExtensionTask.new('puma_http11', HOE.spec) do |ext|
67
+ # define target for extension (supporting fat binaries)
68
+ if RUBY_PLATFORM =~ /mingw|mswin/ then
69
+ RUBY_VERSION =~ /(\d+\.\d+)/
70
+ ext.lib_dir = "lib/#{$1}"
71
+ elsif ENV['CROSS']
72
+ # define cross-compilation tasks when not on Windows.
73
+ ext.cross_compile = true
74
+ ext.cross_platform = ['i386-mswin32', 'i386-mingw32']
75
+ end
76
+
77
+ # cleanup versioned library directory
78
+ CLEAN.include 'lib/{1.8,1.9}'
79
+ end
80
+
81
+ task :ragel => 'ext/puma_http11/http11_parser.c'
82
+ end
83
+
84
+ task :ext_clean do
85
+ sh "rm -rf lib/puma_http11.bundle"
86
+ sh "rm -rf lib/puma_http11.jar"
87
+ sh "rm -rf lib/puma_http11.so"
88
+ end
89
+
90
+ task :clean => :ext_clean
data/bin/puma CHANGED
@@ -10,6 +10,7 @@ cli = Puma::CLI.new ARGV
10
10
  begin
11
11
  cli.run
12
12
  rescue => e
13
+ raise e if $DEBUG
13
14
  STDERR.puts e.message
14
15
  exit 1
15
16
  end
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'puma/control_cli'
4
+
5
+ cli = Puma::ControlCLI.new ARGV
6
+
7
+ begin
8
+ cli.run
9
+ rescue => e
10
+ STDERR.puts e.message
11
+ exit 1
12
+ end
@@ -8,11 +8,8 @@ require 'etc'
8
8
  require 'uri'
9
9
  require 'stringio'
10
10
 
11
- # Gem conditional loader
12
- require 'puma/gems'
13
11
  require 'thread'
14
12
 
15
13
  # Ruby Puma
16
14
  require 'puma/const'
17
15
  require 'puma/server'
18
- require 'puma/utils'
@@ -0,0 +1,37 @@
1
+ module Puma
2
+ module App
3
+ class Status
4
+ def initialize(server, cli)
5
+ @server = server
6
+ @cli = cli
7
+ end
8
+
9
+ def call(env)
10
+ case env['PATH_INFO']
11
+ when "/stop"
12
+ @server.stop
13
+ return [200, {}, ['{ "status": "ok" }']]
14
+
15
+ when "/halt"
16
+ @server.halt
17
+ return [200, {}, ['{ "status": "ok" }']]
18
+
19
+ when "/restart"
20
+ if @cli and @cli.restart_on_stop!
21
+ @server.stop
22
+ return [200, {}, ['{ "status": "ok" }']]
23
+ else
24
+ return [200, {}, ['{ "status": "not configured" }']]
25
+ end
26
+
27
+ when "/stats"
28
+ b = @server.backlog
29
+ r = @server.running
30
+ return [200, {}, ["{ \"backlog\": #{b}, \"running\": #{r} }"]]
31
+ end
32
+
33
+ [404, {}, ["Unsupported action"]]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -7,10 +7,20 @@ require 'puma/const'
7
7
  require 'rack/commonlogger'
8
8
 
9
9
  module Puma
10
+ # Handles invoke a Puma::Server in a command line style.
11
+ #
10
12
  class CLI
11
13
  DefaultTCPHost = "0.0.0.0"
12
14
  DefaultTCPPort = 9292
13
15
 
16
+ IS_JRUBY = defined?(JRUBY_VERSION)
17
+
18
+ # Create a new CLI object using +argv+ as the command line
19
+ # arguments.
20
+ #
21
+ # +stdout+ and +stderr+ can be set to IO-like objects which
22
+ # this object will report status on.
23
+ #
14
24
  def initialize(argv, stdout=STDOUT, stderr=STDERR)
15
25
  @argv = argv
16
26
  @stdout = stdout
@@ -18,18 +28,78 @@ module Puma
18
28
 
19
29
  @events = Events.new @stdout, @stderr
20
30
 
31
+ @server = nil
32
+ @status = nil
33
+
34
+ @restart = false
35
+ @temp_status_path = nil
36
+
21
37
  setup_options
38
+
39
+ generate_restart_data
40
+ end
41
+
42
+ def restart_on_stop!
43
+ if @restart_argv
44
+ @restart = true
45
+ return true
46
+ else
47
+ return false
48
+ end
49
+ end
50
+
51
+ def generate_restart_data
52
+ # Use the same trick as unicorn, namely favor PWD because
53
+ # it will contain an unresolved symlink, useful for when
54
+ # the pwd is /data/releases/current.
55
+ if dir = ENV['PWD']
56
+ s_env = File.stat(dir)
57
+ s_pwd = File.stat(Dir.pwd)
58
+
59
+ if s_env.ino == s_pwd.ino and s_env.dev == s_pwd.dev
60
+ @restart_dir = dir
61
+ end
62
+ end
63
+
64
+ @restart_dir ||= Dir.pwd
65
+
66
+ if defined? Rubinius::OS_ARGV
67
+ @restart_argv = Rubinius::OS_ARGV
68
+ else
69
+ require 'rubygems'
70
+
71
+ # if $0 is a file in the current directory, then restart
72
+ # it the same, otherwise add -S on there because it was
73
+ # picked up in PATH.
74
+ #
75
+ if File.exists?($0)
76
+ @restart_argv = [Gem.ruby, $0] + ARGV
77
+ else
78
+ @restart_argv = [Gem.ruby, "-S", $0] + ARGV
79
+ end
80
+ end
22
81
  end
23
82
 
83
+ def restart!
84
+ Dir.chdir @restart_dir
85
+ Kernel.exec(*@restart_argv)
86
+ end
87
+
88
+ # Write +str+ to +@stdout+
89
+ #
24
90
  def log(str)
25
91
  @stdout.puts str
26
92
  end
27
93
 
94
+ # Write +str+ to +@stderr+
95
+ #
28
96
  def error(str)
29
97
  @stderr.puts "ERROR: #{str}"
30
98
  exit 1
31
99
  end
32
100
 
101
+ # Build the OptionParser object to handle the available options.
102
+ #
33
103
  def setup_options
34
104
  @options = {
35
105
  :min_threads => 0,
@@ -63,6 +133,27 @@ module Puma
63
133
  end
64
134
  end
65
135
 
136
+ o.on "-S", "--state PATH", "Where to store the state details" do |arg|
137
+ @options[:state] = arg
138
+ end
139
+
140
+ o.on "--status [URL]", "The bind url to use for the status server" do |arg|
141
+ if arg and arg != "@"
142
+ @options[:status_address] = arg
143
+ elsif IS_JRUBY
144
+ raise NotImplementedError, "No default url available on JRuby"
145
+ else
146
+ require 'tmpdir'
147
+
148
+ t = (Time.now.to_f * 1000).to_i
149
+ path = "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
150
+
151
+ @temp_status_path = path
152
+
153
+ @options[:status_address] = "unix://#{path}"
154
+ end
155
+ end
156
+
66
157
  end
67
158
 
68
159
  @parser.banner = "puma <options> <rackup file>"
@@ -73,6 +164,9 @@ module Puma
73
164
  end
74
165
  end
75
166
 
167
+ # Load the specified rackup file, pull an options from
168
+ # the rackup file, and set @app.
169
+ #
76
170
  def load_rackup
77
171
  @app, options = Rack::Builder.parse_file @rackup
78
172
  @options.merge! options
@@ -84,6 +178,9 @@ module Puma
84
178
  end
85
179
  end
86
180
 
181
+ # If configured, write the pid of the current process out
182
+ # to a file.
183
+ #
87
184
  def write_pid
88
185
  if path = @options[:pidfile]
89
186
  File.open(path, "w") do |f|
@@ -92,14 +189,34 @@ module Puma
92
189
  end
93
190
  end
94
191
 
192
+ def write_state
193
+ require 'yaml'
194
+
195
+ if path = @options[:state]
196
+ state = { "pid" => Process.pid }
197
+
198
+ if url = @options[:status_address]
199
+ state["status_address"] = url
200
+ end
201
+
202
+ File.open(path, "w") do |f|
203
+ f.write state.to_yaml
204
+ end
205
+ end
206
+ end
207
+
208
+ # :nodoc:
95
209
  def parse_options
96
210
  @parser.parse! @argv
97
211
  end
98
212
 
213
+ # Parse the options, load the rackup, start the server and wait
214
+ # for it to finish.
215
+ #
99
216
  def run
100
217
  parse_options
101
218
 
102
- @rackup = ARGV.shift || "config.ru"
219
+ @rackup = @argv.shift || "config.ru"
103
220
 
104
221
  unless File.exists?(@rackup)
105
222
  raise "Missing rackup file '#{@rackup}'"
@@ -107,8 +224,9 @@ module Puma
107
224
 
108
225
  load_rackup
109
226
  write_pid
227
+ write_state
110
228
 
111
- unless @quiet
229
+ unless @options[:quiet]
112
230
  @app = Rack::CommonLogger.new(@app, STDOUT)
113
231
  end
114
232
 
@@ -148,8 +266,33 @@ module Puma
148
266
  end
149
267
  end
150
268
 
151
- if server.attempt_bonjour(@rackup)
152
- log "* Announced services via bonjour"
269
+ @server = server
270
+
271
+ if str = @options[:status_address]
272
+ require 'puma/app/status'
273
+
274
+ uri = URI.parse str
275
+
276
+ app = Puma::App::Status.new server, self
277
+ status = Puma::Server.new app, @events
278
+ status.min_threads = 0
279
+ status.max_threads = 1
280
+
281
+ case uri.scheme
282
+ when "tcp"
283
+ log "* Starting status server on #{str}"
284
+ status.add_tcp_listener uri.host, uri.port
285
+ when "unix"
286
+ log "* Starting status server on #{str}"
287
+ path = "#{uri.host}#{uri.path}"
288
+
289
+ status.add_unix_listener path
290
+ else
291
+ error "Invalid status URI: #{str}"
292
+ end
293
+
294
+ status.run
295
+ @status = status
153
296
  end
154
297
 
155
298
  log "Use Ctrl-C to stop"
@@ -157,8 +300,21 @@ module Puma
157
300
  begin
158
301
  server.run.join
159
302
  rescue Interrupt
160
- log " - Shutting down..."
303
+ log " - Gracefully stopping, waiting for requests to finish"
304
+ server.stop(true)
305
+ log " - Goodbye!"
161
306
  end
307
+
308
+ File.unlink @temp_status_path if @temp_status_path
309
+
310
+ if @restart
311
+ log "* Restarting..."
312
+ restart!
313
+ end
314
+ end
315
+
316
+ def stop
317
+ @server.stop(true) if @server
162
318
  end
163
319
  end
164
320
  end