puma 3.0.0.rc1 → 5.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (91) hide show
  1. checksums.yaml +5 -5
  2. data/{History.txt → History.md} +703 -70
  3. data/LICENSE +23 -20
  4. data/README.md +173 -163
  5. data/docs/architecture.md +37 -0
  6. data/{DEPLOYMENT.md → docs/deployment.md} +28 -6
  7. data/docs/fork_worker.md +31 -0
  8. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  9. data/docs/images/puma-connection-flow.png +0 -0
  10. data/docs/images/puma-general-arch.png +0 -0
  11. data/docs/jungle/README.md +13 -0
  12. data/docs/jungle/rc.d/README.md +74 -0
  13. data/docs/jungle/rc.d/puma +61 -0
  14. data/docs/jungle/rc.d/puma.conf +10 -0
  15. data/{tools → docs}/jungle/upstart/README.md +0 -0
  16. data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
  17. data/{tools → docs}/jungle/upstart/puma.conf +1 -1
  18. data/docs/nginx.md +2 -2
  19. data/docs/plugins.md +38 -0
  20. data/docs/restart.md +41 -0
  21. data/docs/signals.md +57 -3
  22. data/docs/systemd.md +228 -0
  23. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  24. data/ext/puma_http11/extconf.rb +16 -0
  25. data/ext/puma_http11/http11_parser.c +287 -468
  26. data/ext/puma_http11/http11_parser.h +1 -0
  27. data/ext/puma_http11/http11_parser.java.rl +21 -37
  28. data/ext/puma_http11/http11_parser.rl +10 -9
  29. data/ext/puma_http11/http11_parser_common.rl +4 -4
  30. data/ext/puma_http11/mini_ssl.c +159 -10
  31. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  32. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +99 -132
  33. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +30 -6
  34. data/ext/puma_http11/puma_http11.c +6 -38
  35. data/lib/puma.rb +25 -5
  36. data/lib/puma/accept_nonblock.rb +7 -1
  37. data/lib/puma/app/status.rb +53 -26
  38. data/lib/puma/binder.rb +150 -119
  39. data/lib/puma/cli.rb +56 -38
  40. data/lib/puma/client.rb +277 -80
  41. data/lib/puma/cluster.rb +326 -130
  42. data/lib/puma/commonlogger.rb +21 -20
  43. data/lib/puma/configuration.rb +160 -161
  44. data/lib/puma/const.rb +50 -47
  45. data/lib/puma/control_cli.rb +104 -63
  46. data/lib/puma/detect.rb +13 -1
  47. data/lib/puma/dsl.rb +463 -114
  48. data/lib/puma/events.rb +22 -13
  49. data/lib/puma/io_buffer.rb +9 -5
  50. data/lib/puma/jruby_restart.rb +2 -59
  51. data/lib/puma/launcher.rb +195 -105
  52. data/lib/puma/minissl.rb +110 -4
  53. data/lib/puma/minissl/context_builder.rb +76 -0
  54. data/lib/puma/null_io.rb +9 -14
  55. data/lib/puma/plugin.rb +32 -12
  56. data/lib/puma/plugin/tmp_restart.rb +19 -6
  57. data/lib/puma/rack/builder.rb +7 -5
  58. data/lib/puma/rack/urlmap.rb +11 -8
  59. data/lib/puma/rack_default.rb +2 -0
  60. data/lib/puma/reactor.rb +242 -32
  61. data/lib/puma/runner.rb +41 -30
  62. data/lib/puma/server.rb +265 -183
  63. data/lib/puma/single.rb +22 -63
  64. data/lib/puma/state_file.rb +9 -2
  65. data/lib/puma/thread_pool.rb +179 -68
  66. data/lib/puma/util.rb +3 -11
  67. data/lib/rack/handler/puma.rb +60 -11
  68. data/tools/Dockerfile +16 -0
  69. data/tools/trickletest.rb +1 -2
  70. metadata +35 -99
  71. data/COPYING +0 -55
  72. data/Gemfile +0 -13
  73. data/Manifest.txt +0 -79
  74. data/Rakefile +0 -158
  75. data/docs/config.md +0 -0
  76. data/ext/puma_http11/io_buffer.c +0 -155
  77. data/lib/puma/capistrano.rb +0 -94
  78. data/lib/puma/compat.rb +0 -18
  79. data/lib/puma/convenient.rb +0 -23
  80. data/lib/puma/daemon_ext.rb +0 -31
  81. data/lib/puma/delegation.rb +0 -11
  82. data/lib/puma/java_io_buffer.rb +0 -45
  83. data/lib/puma/rack/backports/uri/common_18.rb +0 -56
  84. data/lib/puma/rack/backports/uri/common_192.rb +0 -52
  85. data/lib/puma/rack/backports/uri/common_193.rb +0 -29
  86. data/lib/puma/tcp_logger.rb +0 -32
  87. data/puma.gemspec +0 -52
  88. data/tools/jungle/README.md +0 -9
  89. data/tools/jungle/init.d/README.md +0 -54
  90. data/tools/jungle/init.d/puma +0 -394
  91. data/tools/jungle/init.d/run-puma +0 -3
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/const'
4
+ require "puma/null_io"
2
5
  require 'stringio'
3
6
 
4
7
  module Puma
@@ -34,8 +37,6 @@ module Puma
34
37
 
35
38
  @debug = ENV.key? 'PUMA_DEBUG'
36
39
 
37
- @on_booted = []
38
-
39
40
  @hooks = Hash.new { |h,k| h[k] = [] }
40
41
  end
41
42
 
@@ -48,7 +49,7 @@ module Puma
48
49
  @hooks[hook].each { |t| t.call(*args) }
49
50
  end
50
51
 
51
- # Register a callbock for a given hook
52
+ # Register a callback for a given hook
52
53
  #
53
54
  def register(hook, obj=nil, &blk)
54
55
  if obj and blk
@@ -92,8 +93,10 @@ module Puma
92
93
  # parsing exception.
93
94
  #
94
95
  def parse_error(server, env, error)
95
- @stderr.puts "#{Time.now}: HTTP parse error, malformed request (#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}): #{error.inspect}"
96
- @stderr.puts "#{Time.now}: ENV: #{env.inspect}\n---\n"
96
+ @stderr.puts "#{Time.now}: HTTP parse error, malformed request " \
97
+ "(#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}#{env[REQUEST_PATH]}): " \
98
+ "#{error.inspect}" \
99
+ "\n---\n"
97
100
  end
98
101
 
99
102
  # An SSL error has occurred.
@@ -106,24 +109,30 @@ module Puma
106
109
  end
107
110
 
108
111
  # An unknown error has occurred.
109
- # +server+ is the Server object, +env+ the request, +error+ an exception
110
- # object, and +kind+ some additional info.
112
+ # +server+ is the Server object, +error+ an exception object,
113
+ # +kind+ some additional info, and +env+ the request.
111
114
  #
112
- def unknown_error(server, error, kind="Unknown")
115
+ def unknown_error(server, error, kind="Unknown", env=nil)
113
116
  if error.respond_to? :render
114
117
  error.render "#{Time.now}: #{kind} error", @stderr
115
118
  else
116
- @stderr.puts "#{Time.now}: #{kind} error: #{error.inspect}"
117
- @stderr.puts error.backtrace.join("\n")
119
+ if env
120
+ string_block = [ "#{Time.now}: #{kind} error handling request { #{env['REQUEST_METHOD']} #{env['PATH_INFO']} }" ]
121
+ string_block << error.inspect
122
+ else
123
+ string_block = [ "#{Time.now}: #{kind} error: #{error.inspect}" ]
124
+ end
125
+ string_block << error.backtrace
126
+ @stderr.puts string_block.join("\n")
118
127
  end
119
128
  end
120
129
 
121
- def on_booted(&b)
122
- @on_booted << b
130
+ def on_booted(&block)
131
+ register(:on_booted, &block)
123
132
  end
124
133
 
125
134
  def fire_on_booted!
126
- @on_booted.each { |b| b.call }
135
+ fire(:on_booted)
127
136
  end
128
137
 
129
138
  DEFAULT = new(STDOUT, STDERR)
@@ -1,7 +1,11 @@
1
- require 'puma/detect'
1
+ # frozen_string_literal: true
2
2
 
3
- if Puma::IS_JRUBY
4
- require 'puma/java_io_buffer'
5
- else
6
- require 'puma/puma_http11'
3
+ module Puma
4
+ class IOBuffer < String
5
+ def append(*args)
6
+ args.each { |a| concat(a) }
7
+ end
8
+
9
+ alias reset clear
10
+ end
7
11
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ffi'
2
4
 
3
5
  module Puma
@@ -20,64 +22,5 @@ module Puma
20
22
  execlp(cmd, *argv)
21
23
  raise SystemCallError.new(FFI.errno)
22
24
  end
23
-
24
- PermKey = 'PUMA_DAEMON_PERM'
25
- RestartKey = 'PUMA_DAEMON_RESTART'
26
-
27
- # Called to tell things "Your now always in daemon mode,
28
- # don't try to reenter it."
29
- #
30
- def self.perm_daemonize
31
- ENV[PermKey] = "1"
32
- end
33
-
34
- def self.daemon?
35
- ENV.key?(PermKey) || ENV.key?(RestartKey)
36
- end
37
-
38
- def self.daemon_init
39
- return true if ENV.key?(PermKey)
40
-
41
- return false unless ENV.key? RestartKey
42
-
43
- master = ENV[RestartKey]
44
-
45
- # In case the master disappears early
46
- begin
47
- Process.kill "SIGUSR2", master.to_i
48
- rescue SystemCallError => e
49
- end
50
-
51
- ENV[RestartKey] = ""
52
-
53
- setsid
54
-
55
- null = File.open "/dev/null", "w+"
56
- STDIN.reopen null
57
- STDOUT.reopen null
58
- STDERR.reopen null
59
-
60
- true
61
- end
62
-
63
- def self.daemon_start(dir, argv)
64
- ENV['PUMA_DAEMON_RESTART'] = Process.pid.to_s
65
-
66
- if k = ENV['PUMA_JRUBY_DAEMON_OPTS']
67
- ENV['JRUBY_OPTS'] = k
68
- end
69
-
70
- cmd = argv.first
71
- argv = ([:string] * argv.size).zip(argv).flatten
72
- argv << :string
73
- argv << nil
74
-
75
- chdir(dir)
76
- ret = fork
77
- return ret if ret != 0
78
- execlp(cmd, *argv)
79
- raise SystemCallError.new(FFI.errno)
80
- end
81
25
  end
82
26
  end
83
-
@@ -1,18 +1,14 @@
1
- require 'puma/server'
2
- require 'puma/const'
3
- require 'puma/configuration'
4
- require 'puma/binder'
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma/events'
5
4
  require 'puma/detect'
6
- require 'puma/daemon_ext'
7
- require 'puma/util'
8
- require 'puma/single'
9
5
  require 'puma/cluster'
10
- require 'puma/state_file'
11
-
12
- require 'puma/commonlogger'
6
+ require 'puma/single'
7
+ require 'puma/const'
8
+ require 'puma/binder'
13
9
 
14
10
  module Puma
15
- # Puam::Launcher is the single entry point for starting a Puma server based on user
11
+ # Puma::Launcher is the single entry point for starting a Puma server based on user
16
12
  # configuration. It is responsible for taking user supplied arguments and resolving them
17
13
  # with configuration in `config/puma.rb` or `config/puma/<env>.rb`.
18
14
  #
@@ -37,13 +33,13 @@ module Puma
37
33
  #
38
34
  # Examples:
39
35
  #
40
- # conf = Puma::Configuration.new do |c|
41
- # c.threads 1, 10
42
- # c.app do |env|
36
+ # conf = Puma::Configuration.new do |user_config|
37
+ # user_config.threads 1, 10
38
+ # user_config.app do |env|
43
39
  # [200, {}, ["hello world"]]
44
40
  # end
45
41
  # end
46
- # Puma::Launcher.new(conf, argv: Puma::Events.stdio).run
42
+ # Puma::Launcher.new(conf, events: Puma::Events.stdio).run
47
43
  def initialize(conf, launcher_args={})
48
44
  @runner = nil
49
45
  @events = launcher_args[:events] || Events::DEFAULT
@@ -52,9 +48,8 @@ module Puma
52
48
  @config = conf
53
49
 
54
50
  @binder = Binder.new(@events)
55
- @binder.import_from_env
56
-
57
- generate_restart_data
51
+ @binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
52
+ @binder.create_activated_fds(ENV).each { |k| ENV.delete k }
58
53
 
59
54
  @environment = conf.environment
60
55
 
@@ -64,17 +59,18 @@ module Puma
64
59
  @config.load
65
60
 
66
61
  @options = @config.options
62
+ @config.clamp
67
63
 
68
- if clustered? && (Puma.jruby? || Puma.windows?)
69
- unsupported 'worker mode not supported on JRuby or Windows'
70
- end
64
+ @events.formatter = Events::PidFormatter.new if clustered?
65
+ @events.formatter = options[:log_formatter] if @options[:log_formatter]
71
66
 
72
- if @options[:daemon] && Puma.windows?
73
- unsupported 'daemon mode not supported on Windows'
67
+ generate_restart_data
68
+
69
+ if clustered? && !Process.respond_to?(:fork)
70
+ unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
74
71
  end
75
72
 
76
- dir = @options[:directory]
77
- Dir.chdir(dir) if dir
73
+ Dir.chdir(@restart_dir)
78
74
 
79
75
  prune_bundler if prune_bundler?
80
76
 
@@ -82,18 +78,18 @@ module Puma
82
78
  set_rack_environment
83
79
 
84
80
  if clustered?
85
- @events.formatter = Events::PidFormatter.new
86
81
  @options[:logger] = @events
87
82
 
88
83
  @runner = Cluster.new(self, @events)
89
84
  else
90
85
  @runner = Single.new(self, @events)
91
86
  end
87
+ Puma.stats_object = @runner
92
88
 
93
89
  @status = :run
94
90
  end
95
91
 
96
- attr_reader :binder, :events, :config, :options
92
+ attr_reader :binder, :events, :config, :options, :restart_dir
97
93
 
98
94
  # Return stats about the server
99
95
  def stats
@@ -106,14 +102,17 @@ module Puma
106
102
  write_pid
107
103
 
108
104
  path = @options[:state]
105
+ permission = @options[:state_permission]
109
106
  return unless path
110
107
 
108
+ require 'puma/state_file'
109
+
111
110
  sf = StateFile.new
112
111
  sf.pid = Process.pid
113
112
  sf.control_url = @options[:control_url]
114
113
  sf.control_auth_token = @options[:control_auth_token]
115
114
 
116
- sf.save path
115
+ sf.save path, permission
117
116
  end
118
117
 
119
118
  # Delete the configured pidfile
@@ -122,19 +121,6 @@ module Puma
122
121
  File.unlink(path) if path && File.exist?(path)
123
122
  end
124
123
 
125
- # If configured, write the pid of the current process out
126
- # to a file.
127
- def write_pid
128
- path = @options[:pidfile]
129
- return unless path
130
-
131
- File.open(path, 'w') { |f| f.puts Process.pid }
132
- cur = Process.pid
133
- at_exit do
134
- delete_pidfile if cur == Process.pid
135
- end
136
- end
137
-
138
124
  # Begin async shutdown of the server
139
125
  def halt
140
126
  @status = :halt
@@ -164,6 +150,17 @@ module Puma
164
150
 
165
151
  # Run the server. This blocks until the server is stopped
166
152
  def run
153
+ previous_env =
154
+ if defined?(Bundler)
155
+ env = Bundler::ORIGINAL_ENV.dup
156
+ # add -rbundler/setup so we load from Gemfile when restarting
157
+ bundle = "-rbundler/setup"
158
+ env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
159
+ env
160
+ else
161
+ ENV.to_h
162
+ end
163
+
167
164
  @config.clamp
168
165
 
169
166
  @config.plugins.fire_starts self
@@ -179,26 +176,66 @@ module Puma
179
176
  graceful_stop
180
177
  when :restart
181
178
  log "* Restarting..."
179
+ ENV.replace(previous_env)
182
180
  @runner.before_restart
183
181
  restart!
184
182
  when :exit
185
183
  # nothing
186
184
  end
185
+ close_binder_listeners unless @status == :restart
187
186
  end
188
187
 
189
- # Return which tcp port the launcher is using, if it's using TCP
190
- def connected_port
191
- @binder.connected_port
188
+ # Return all tcp ports the launcher may be using, TCP or SSL
189
+ def connected_ports
190
+ @binder.connected_ports
191
+ end
192
+
193
+ def restart_args
194
+ cmd = @options[:restart_cmd]
195
+ if cmd
196
+ cmd.split(' ') + @original_argv
197
+ else
198
+ @restart_argv
199
+ end
200
+ end
201
+
202
+ def close_binder_listeners
203
+ @runner.close_control_listeners
204
+ @binder.close_listeners
205
+ end
206
+
207
+ def thread_status
208
+ Thread.list.each do |thread|
209
+ name = "Thread: TID-#{thread.object_id.to_s(36)}"
210
+ name += " #{thread['label']}" if thread['label']
211
+ name += " #{thread.name}" if thread.respond_to?(:name) && thread.name
212
+ backtrace = thread.backtrace || ["<no backtrace available>"]
213
+
214
+ yield name, backtrace
215
+ end
192
216
  end
193
217
 
194
218
  private
195
219
 
220
+ # If configured, write the pid of the current process out
221
+ # to a file.
222
+ def write_pid
223
+ path = @options[:pidfile]
224
+ return unless path
225
+
226
+ File.open(path, 'w') { |f| f.puts Process.pid }
227
+ cur = Process.pid
228
+ at_exit do
229
+ delete_pidfile if cur == Process.pid
230
+ end
231
+ end
232
+
196
233
  def reload_worker_directory
197
234
  @runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
198
235
  end
199
236
 
200
237
  def restart!
201
- @config.run_hooks :on_restart, self
238
+ @config.run_hooks :on_restart, self, @events
202
239
 
203
240
  if Puma.jruby?
204
241
  close_binder_listeners
@@ -212,46 +249,74 @@ module Puma
212
249
  Dir.chdir(@restart_dir)
213
250
  Kernel.exec(*argv)
214
251
  else
215
- redirects = {:close_others => true}
216
- @binder.listeners.each_with_index do |(l, io), i|
217
- ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
218
- redirects[io.to_i] = io.to_i
219
- end
220
-
221
252
  argv = restart_args
222
253
  Dir.chdir(@restart_dir)
223
- argv += [redirects] if RUBY_VERSION >= '1.9'
254
+ ENV.update(@binder.redirects_for_restart_env)
255
+ argv += [@binder.redirects_for_restart]
224
256
  Kernel.exec(*argv)
225
257
  end
226
258
  end
227
259
 
228
- def prune_bundler
229
- return unless defined?(Bundler)
230
- puma = Bundler.rubygems.loaded_specs("puma")
231
- dirs = puma.require_paths.map { |x| File.join(puma.full_gem_path, x) }
260
+ def dependencies_and_files_to_require_after_prune
261
+ puma = spec_for_gem("puma")
262
+
263
+ deps = puma.runtime_dependencies.map do |d|
264
+ "#{d.name}:#{spec_for_gem(d.name).version}"
265
+ end
266
+
267
+ [deps, require_paths_for_gem(puma) + extra_runtime_deps_directories]
268
+ end
269
+
270
+ def extra_runtime_deps_directories
271
+ Array(@options[:extra_runtime_dependencies]).map do |d_name|
272
+ if (spec = spec_for_gem(d_name))
273
+ require_paths_for_gem(spec)
274
+ else
275
+ log "* Could not load extra dependency: #{d_name}"
276
+ nil
277
+ end
278
+ end.flatten.compact
279
+ end
280
+
281
+ def puma_wild_location
282
+ puma = spec_for_gem("puma")
283
+ dirs = require_paths_for_gem(puma)
232
284
  puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }
285
+ File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
286
+ end
233
287
 
234
- unless puma_lib_dir
288
+ def prune_bundler
289
+ return unless defined?(Bundler)
290
+ require_rubygems_min_version!(Gem::Version.new("2.2"), "prune_bundler")
291
+ unless puma_wild_location
235
292
  log "! Unable to prune Bundler environment, continuing"
236
293
  return
237
294
  end
238
295
 
239
- deps = puma.runtime_dependencies.map do |d|
240
- spec = Bundler.rubygems.loaded_specs(d.name)
241
- "#{d.name}:#{spec.version.to_s}"
242
- end
296
+ deps, dirs = dependencies_and_files_to_require_after_prune
243
297
 
244
298
  log '* Pruning Bundler environment'
245
299
  home = ENV['GEM_HOME']
246
- Bundler.with_clean_env do
300
+ bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
301
+ with_unbundled_env do
247
302
  ENV['GEM_HOME'] = home
303
+ ENV['BUNDLE_GEMFILE'] = bundle_gemfile
248
304
  ENV['PUMA_BUNDLER_PRUNED'] = '1'
249
- wild = File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
250
- args = [Gem.ruby, wild, '-I', dirs.join(':'), deps.join(',')] + @original_argv
305
+ args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':'), deps.join(',')] + @original_argv
306
+ # Ruby 2.0+ defaults to true which breaks socket activation
307
+ args += [{:close_others => false}]
251
308
  Kernel.exec(*args)
252
309
  end
253
310
  end
254
311
 
312
+ def spec_for_gem(gem_name)
313
+ Bundler.rubygems.loaded_specs(gem_name)
314
+ end
315
+
316
+ def require_paths_for_gem(gem_spec)
317
+ gem_spec.full_require_paths
318
+ end
319
+
255
320
  def log(str)
256
321
  @events.log str
257
322
  end
@@ -260,15 +325,6 @@ module Puma
260
325
  (@options[:workers] || 0) > 0
261
326
  end
262
327
 
263
- def restart_args
264
- cmd = @options[:restart_cmd]
265
- if cmd
266
- cmd.split(' ') + @original_argv
267
- else
268
- @restart_argv
269
- end
270
- end
271
-
272
328
  def unsupported(str)
273
329
  @events.error(str)
274
330
  raise UnsupportedOption
@@ -285,8 +341,8 @@ module Puma
285
341
  end
286
342
 
287
343
  def title
288
- buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
289
- buffer << " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
344
+ buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
345
+ buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
290
346
  buffer
291
347
  end
292
348
 
@@ -303,34 +359,29 @@ module Puma
303
359
  @options[:prune_bundler] && clustered? && !@options[:preload_app]
304
360
  end
305
361
 
306
- def close_binder_listeners
307
- @binder.listeners.each do |l, io|
308
- io.close
309
- uri = URI.parse(l)
310
- next unless uri.scheme == 'unix'
311
- File.unlink("#{uri.host}#{uri.path}")
312
- end
313
- end
314
-
315
-
316
362
  def generate_restart_data
317
- # Use the same trick as unicorn, namely favor PWD because
318
- # it will contain an unresolved symlink, useful for when
319
- # the pwd is /data/releases/current.
320
- if dir = ENV['PWD']
363
+ if dir = @options[:directory]
364
+ @restart_dir = dir
365
+
366
+ elsif Puma.windows?
367
+ # I guess the value of PWD is garbage on windows so don't bother
368
+ # using it.
369
+ @restart_dir = Dir.pwd
370
+
371
+ # Use the same trick as unicorn, namely favor PWD because
372
+ # it will contain an unresolved symlink, useful for when
373
+ # the pwd is /data/releases/current.
374
+ elsif dir = ENV['PWD']
321
375
  s_env = File.stat(dir)
322
376
  s_pwd = File.stat(Dir.pwd)
323
377
 
324
378
  if s_env.ino == s_pwd.ino and (Puma.jruby? or s_env.dev == s_pwd.dev)
325
379
  @restart_dir = dir
326
- @config.configure { |c| c.worker_directory dir }
327
380
  end
328
381
  end
329
382
 
330
383
  @restart_dir ||= Dir.pwd
331
384
 
332
- require 'rubygems'
333
-
334
385
  # if $0 is a file in the current directory, then restart
335
386
  # it the same, otherwise add -S on there because it was
336
387
  # picked up in PATH.
@@ -341,9 +392,10 @@ module Puma
341
392
  arg0 = [Gem.ruby, "-S", $0]
342
393
  end
343
394
 
344
- # Detect and reinject -Ilib from the command line
395
+ # Detect and reinject -Ilib from the command line, used for testing without bundler
396
+ # cruby has an expanded path, jruby has just "lib"
345
397
  lib = File.expand_path "lib"
346
- arg0[1,0] = ["-I", lib] if $:[0] == lib
398
+ arg0[1,0] = ["-I", lib] if [lib, "lib"].include?($LOAD_PATH[0])
347
399
 
348
400
  if defined? Puma::WILD_ARGS
349
401
  @restart_argv = arg0 + Puma::WILD_ARGS + @original_argv
@@ -361,36 +413,74 @@ module Puma
361
413
  log "*** SIGUSR2 not implemented, signal based restart unavailable!"
362
414
  end
363
415
 
416
+ unless Puma.jruby?
417
+ begin
418
+ Signal.trap "SIGUSR1" do
419
+ phased_restart
420
+ end
421
+ rescue Exception
422
+ log "*** SIGUSR1 not implemented, signal based restart unavailable!"
423
+ end
424
+ end
425
+
364
426
  begin
365
- Signal.trap "SIGUSR1" do
366
- phased_restart
427
+ Signal.trap "SIGTERM" do
428
+ graceful_stop
429
+
430
+ raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
367
431
  end
368
432
  rescue Exception
369
- log "*** SIGUSR1 not implemented, signal based restart unavailable!"
433
+ log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
370
434
  end
371
435
 
372
436
  begin
373
- Signal.trap "SIGTERM" do
437
+ Signal.trap "SIGINT" do
374
438
  stop
375
439
  end
376
440
  rescue Exception
377
- log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
441
+ log "*** SIGINT not implemented, signal based gracefully stopping unavailable!"
378
442
  end
379
443
 
380
444
  begin
381
445
  Signal.trap "SIGHUP" do
382
- @runner.redirect_io
446
+ if @runner.redirected_io?
447
+ @runner.redirect_io
448
+ else
449
+ stop
450
+ end
383
451
  end
384
452
  rescue Exception
385
453
  log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
386
454
  end
387
455
 
388
- if Puma.jruby?
389
- Signal.trap("INT") do
390
- @status = :exit
391
- graceful_stop
392
- exit
456
+ begin
457
+ unless Puma.jruby? # INFO in use by JVM already
458
+ Signal.trap "SIGINFO" do
459
+ thread_status do |name, backtrace|
460
+ @events.log name
461
+ @events.log backtrace.map { |bt| " #{bt}" }
462
+ end
463
+ end
393
464
  end
465
+ rescue Exception
466
+ # Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
467
+ # to see this constantly on Linux.
468
+ end
469
+ end
470
+
471
+ def require_rubygems_min_version!(min_version, feature)
472
+ return if min_version <= Gem::Version.new(Gem::VERSION)
473
+
474
+ raise "#{feature} is not supported on your version of RubyGems. " \
475
+ "You must have RubyGems #{min_version}+ to use this feature."
476
+ end
477
+
478
+ def with_unbundled_env
479
+ bundler_ver = Gem::Version.new(Bundler::VERSION)
480
+ if bundler_ver < Gem::Version.new('2.1.0')
481
+ Bundler.with_clean_env { yield }
482
+ else
483
+ Bundler.with_unbundled_env { yield }
394
484
  end
395
485
  end
396
486
  end