puma 4.3.0-java

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.

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