puma 4.3.12 → 5.6.6

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1511 -524
  3. data/LICENSE +23 -20
  4. data/README.md +120 -36
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +63 -26
  7. data/docs/compile_options.md +21 -0
  8. data/docs/deployment.md +60 -69
  9. data/docs/fork_worker.md +33 -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/jungle/README.md +9 -0
  14. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  15. data/{tools → docs}/jungle/rc.d/puma +2 -2
  16. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  17. data/docs/kubernetes.md +66 -0
  18. data/docs/nginx.md +1 -1
  19. data/docs/plugins.md +15 -15
  20. data/docs/rails_dev_mode.md +28 -0
  21. data/docs/restart.md +46 -23
  22. data/docs/signals.md +13 -11
  23. data/docs/stats.md +142 -0
  24. data/docs/systemd.md +85 -128
  25. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  26. data/ext/puma_http11/ext_help.h +1 -1
  27. data/ext/puma_http11/extconf.rb +44 -10
  28. data/ext/puma_http11/http11_parser.c +45 -47
  29. data/ext/puma_http11/http11_parser.h +1 -1
  30. data/ext/puma_http11/http11_parser.java.rl +1 -1
  31. data/ext/puma_http11/http11_parser.rl +1 -1
  32. data/ext/puma_http11/http11_parser_common.rl +0 -0
  33. data/ext/puma_http11/mini_ssl.c +225 -89
  34. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  35. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  36. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +3 -5
  37. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +109 -67
  38. data/ext/puma_http11/puma_http11.c +32 -51
  39. data/lib/puma/app/status.rb +50 -36
  40. data/lib/puma/binder.rb +225 -106
  41. data/lib/puma/cli.rb +24 -18
  42. data/lib/puma/client.rb +104 -76
  43. data/lib/puma/cluster/worker.rb +173 -0
  44. data/lib/puma/cluster/worker_handle.rb +94 -0
  45. data/lib/puma/cluster.rb +212 -220
  46. data/lib/puma/commonlogger.rb +2 -2
  47. data/lib/puma/configuration.rb +58 -49
  48. data/lib/puma/const.rb +13 -6
  49. data/lib/puma/control_cli.rb +99 -76
  50. data/lib/puma/detect.rb +29 -2
  51. data/lib/puma/dsl.rb +368 -96
  52. data/lib/puma/error_logger.rb +104 -0
  53. data/lib/puma/events.rb +55 -34
  54. data/lib/puma/io_buffer.rb +9 -2
  55. data/lib/puma/jruby_restart.rb +0 -58
  56. data/lib/puma/json_serialization.rb +96 -0
  57. data/lib/puma/launcher.rb +128 -46
  58. data/lib/puma/minissl/context_builder.rb +14 -9
  59. data/lib/puma/minissl.rb +137 -50
  60. data/lib/puma/null_io.rb +18 -1
  61. data/lib/puma/plugin/tmp_restart.rb +0 -0
  62. data/lib/puma/plugin.rb +3 -12
  63. data/lib/puma/queue_close.rb +26 -0
  64. data/lib/puma/rack/builder.rb +1 -5
  65. data/lib/puma/rack/urlmap.rb +0 -0
  66. data/lib/puma/rack_default.rb +0 -0
  67. data/lib/puma/reactor.rb +85 -369
  68. data/lib/puma/request.rb +476 -0
  69. data/lib/puma/runner.rb +46 -61
  70. data/lib/puma/server.rb +292 -763
  71. data/lib/puma/single.rb +9 -65
  72. data/lib/puma/state_file.rb +48 -8
  73. data/lib/puma/systemd.rb +46 -0
  74. data/lib/puma/thread_pool.rb +125 -57
  75. data/lib/puma/util.rb +32 -4
  76. data/lib/puma.rb +48 -0
  77. data/lib/rack/handler/puma.rb +2 -3
  78. data/lib/rack/version_restriction.rb +15 -0
  79. data/tools/{docker/Dockerfile → Dockerfile} +1 -1
  80. data/tools/trickletest.rb +0 -0
  81. metadata +28 -23
  82. data/docs/tcp_mode.md +0 -96
  83. data/ext/puma_http11/io_buffer.c +0 -155
  84. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  85. data/lib/puma/accept_nonblock.rb +0 -29
  86. data/lib/puma/tcp_logger.rb +0 -41
  87. data/tools/jungle/README.md +0 -19
  88. data/tools/jungle/init.d/README.md +0 -61
  89. data/tools/jungle/init.d/puma +0 -421
  90. data/tools/jungle/init.d/run-puma +0 -18
  91. data/tools/jungle/upstart/README.md +0 -61
  92. data/tools/jungle/upstart/puma-manager.conf +0 -31
  93. data/tools/jungle/upstart/puma.conf +0 -69
@@ -11,6 +11,7 @@ module Puma
11
11
 
12
12
  DefaultTCPHost = "0.0.0.0"
13
13
  DefaultTCPPort = 9292
14
+ DefaultWorkerCheckInterval = 5
14
15
  DefaultWorkerTimeout = 60
15
16
  DefaultWorkerShutdownTimeout = 30
16
17
  end
@@ -54,9 +55,7 @@ module Puma
54
55
  attr_reader :user_options, :file_options, :default_options
55
56
 
56
57
  def [](key)
57
- return user_options[key] if user_options.key?(key)
58
- return file_options[key] if file_options.key?(key)
59
- return default_options[key] if default_options.key?(key)
58
+ fetch(key)
60
59
  end
61
60
 
62
61
  def []=(key, value)
@@ -64,7 +63,11 @@ module Puma
64
63
  end
65
64
 
66
65
  def fetch(key, default_value = nil)
67
- self[key] || default_value
66
+ return user_options[key] if user_options.key?(key)
67
+ return file_options[key] if file_options.key?(key)
68
+ return default_options[key] if default_options.key?(key)
69
+
70
+ default_value
68
71
  end
69
72
 
70
73
  def all_of(key)
@@ -90,6 +93,12 @@ module Puma
90
93
  end
91
94
  end
92
95
  end
96
+
97
+ def final_options
98
+ default_options
99
+ .merge(file_options)
100
+ .merge(user_options)
101
+ end
93
102
  end
94
103
 
95
104
  # The main configuration class of Puma.
@@ -106,16 +115,17 @@ module Puma
106
115
  #
107
116
  # It also handles loading plugins.
108
117
  #
109
- # > Note: `:port` and `:host` are not valid keys. By they time they make it to the
118
+ # [Note:]
119
+ # `:port` and `:host` are not valid keys. By the time they make it to the
110
120
  # configuration options they are expected to be incorporated into a `:binds` key.
111
121
  # Under the hood the DSL maps `port` and `host` calls to `:binds`
112
122
  #
113
- # config = Configuration.new({}) do |user_config, file_config, default_config|
114
- # user_config.port 3003
115
- # end
116
- # config.load
117
- # puts config.options[:port]
118
- # # => 3003
123
+ # config = Configuration.new({}) do |user_config, file_config, default_config|
124
+ # user_config.port 3003
125
+ # end
126
+ # config.load
127
+ # puts config.options[:port]
128
+ # # => 3003
119
129
  #
120
130
  # It is expected that `load` is called on the configuration instance after setting
121
131
  # config. This method expands any values in `config_file` and puts them into the
@@ -137,6 +147,10 @@ module Puma
137
147
  @file_dsl = DSL.new(@options.file_options, self)
138
148
  @default_dsl = DSL.new(@options.default_options, self)
139
149
 
150
+ if !@options[:prune_bundler]
151
+ default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
152
+ end
153
+
140
154
  if block
141
155
  configure(&block)
142
156
  end
@@ -167,27 +181,37 @@ module Puma
167
181
  self
168
182
  end
169
183
 
184
+ # @version 5.0.0
185
+ def default_max_threads
186
+ Puma.mri? ? 5 : 16
187
+ end
188
+
170
189
  def puma_default_options
171
190
  {
172
- :min_threads => 0,
173
- :max_threads => 16,
191
+ :min_threads => Integer(ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS'] || 0),
192
+ :max_threads => Integer(ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS'] || default_max_threads),
174
193
  :log_requests => false,
175
194
  :debug => false,
176
195
  :binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
177
- :workers => 0,
178
- :daemon => false,
196
+ :workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
197
+ :silence_single_worker_warning => false,
179
198
  :mode => :http,
199
+ :worker_check_interval => DefaultWorkerCheckInterval,
180
200
  :worker_timeout => DefaultWorkerTimeout,
181
201
  :worker_boot_timeout => DefaultWorkerTimeout,
182
202
  :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
203
+ :worker_culling_strategy => :youngest,
183
204
  :remote_address => :socket,
184
205
  :tag => method(:infer_tag),
185
- :environment => -> { ENV['RACK_ENV'] || "development" },
206
+ :environment => -> { ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' },
186
207
  :rackup => DefaultRackup,
187
208
  :logger => STDOUT,
188
209
  :persistent_timeout => Const::PERSISTENT_TIMEOUT,
189
210
  :first_data_timeout => Const::FIRST_DATA_TIMEOUT,
190
- :raise_exception_on_sigterm => true
211
+ :raise_exception_on_sigterm => true,
212
+ :max_fast_inline => Const::MAX_FAST_INLINE,
213
+ :io_selector_backend => :auto,
214
+ :mutate_stdout_and_stderr_to_sync_on_write => true,
191
215
  }
192
216
  end
193
217
 
@@ -245,14 +269,6 @@ module Puma
245
269
  def app
246
270
  found = options[:app] || load_rackup
247
271
 
248
- if @options[:mode] == :tcp
249
- require 'puma/tcp_logger'
250
-
251
- logger = @options[:logger]
252
- quiet = !@options[:log_requests]
253
- return TCPLogger.new(logger, found, quiet)
254
- end
255
-
256
272
  if @options[:log_requests]
257
273
  require 'puma/commonlogger'
258
274
  logger = @options[:logger]
@@ -275,8 +291,19 @@ module Puma
275
291
  @plugins.create name
276
292
  end
277
293
 
278
- def run_hooks(key, arg)
279
- @options.all_of(key).each { |b| b.call arg }
294
+ def run_hooks(key, arg, events)
295
+ @options.all_of(key).each do |b|
296
+ begin
297
+ b.call arg
298
+ rescue => e
299
+ events.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
300
+ events.debug e.backtrace.join("\n")
301
+ end
302
+ end
303
+ end
304
+
305
+ def final_options
306
+ @options.final_options
280
307
  end
281
308
 
282
309
  def self.temp_path
@@ -319,6 +346,8 @@ module Puma
319
346
  raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
320
347
 
321
348
  rack_app, rack_options = rack_builder.parse_file(rackup)
349
+ rack_options = rack_options || {}
350
+
322
351
  @options.file_options.merge!(rack_options)
323
352
 
324
353
  config_ru_binds = []
@@ -332,29 +361,9 @@ module Puma
332
361
  end
333
362
 
334
363
  def self.random_token
335
- begin
336
- require 'openssl'
337
- rescue LoadError
338
- end
339
-
340
- count = 16
341
-
342
- bytes = nil
343
-
344
- if defined? OpenSSL::Random
345
- bytes = OpenSSL::Random.random_bytes(count)
346
- elsif File.exist?("/dev/urandom")
347
- File.open('/dev/urandom') { |f| bytes = f.read(count) }
348
- end
349
-
350
- if bytes
351
- token = "".dup
352
- bytes.each_byte { |b| token << b.to_s(16) }
353
- else
354
- token = (0..count).to_a.map { rand(255).to_s(16) }.join
355
- end
364
+ require 'securerandom' unless defined?(SecureRandom)
356
365
 
357
- return token
366
+ SecureRandom.hex(16)
358
367
  end
359
368
  end
360
369
  end
data/lib/puma/const.rb CHANGED
@@ -100,8 +100,9 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "4.3.12".freeze
104
- CODE_NAME = "Mysterious Traveller".freeze
103
+ PUMA_VERSION = VERSION = "5.6.6".freeze
104
+ CODE_NAME = "Birdie's Version".freeze
105
+
105
106
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
106
107
 
107
108
  FAST_TRACK_KA_TIMEOUT = 0.2
@@ -177,7 +178,6 @@ module Puma
177
178
  PORT_443 = "443".freeze
178
179
  LOCALHOST = "localhost".freeze
179
180
  LOCALHOST_IP = "127.0.0.1".freeze
180
- LOCALHOST_ADDR = "127.0.0.1:0".freeze
181
181
 
182
182
  SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
183
183
  HTTP_11 = "HTTP/1.1".freeze
@@ -230,7 +230,6 @@ module Puma
230
230
  COLON = ": ".freeze
231
231
 
232
232
  NEWLINE = "\n".freeze
233
- HTTP_INJECTION_REGEX = /[\r\n]/.freeze
234
233
 
235
234
  HIJACK_P = "rack.hijack?".freeze
236
235
  HIJACK = "rack.hijack".freeze
@@ -238,8 +237,16 @@ module Puma
238
237
 
239
238
  EARLY_HINTS = "rack.early_hints".freeze
240
239
 
241
- # Mininum interval to checks worker health
242
- WORKER_CHECK_INTERVAL = 5
240
+ # Illegal character in the key or value of response header
241
+ DQUOTE = "\"".freeze
242
+ HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
243
+ ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20#{DQUOTE}#{HTTP_HEADER_DELIMITER}]/.freeze
244
+ # header values can contain HTAB?
245
+ ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
246
+
247
+ # Banned keys of response header
248
+ BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
243
249
 
250
+ PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
244
251
  end
245
252
  end
@@ -11,7 +11,36 @@ require 'socket'
11
11
  module Puma
12
12
  class ControlCLI
13
13
 
14
- COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats}
14
+ # values must be string or nil
15
+ # value of `nil` means command cannot be processed via signal
16
+ # @version 5.0.3
17
+ CMD_PATH_SIG_MAP = {
18
+ 'gc' => nil,
19
+ 'gc-stats' => nil,
20
+ 'halt' => 'SIGQUIT',
21
+ 'info' => 'SIGINFO',
22
+ 'phased-restart' => 'SIGUSR1',
23
+ 'refork' => 'SIGURG',
24
+ 'reload-worker-directory' => nil,
25
+ 'reopen-log' => 'SIGHUP',
26
+ 'restart' => 'SIGUSR2',
27
+ 'start' => nil,
28
+ 'stats' => nil,
29
+ 'status' => '',
30
+ 'stop' => 'SIGTERM',
31
+ 'thread-backtraces' => nil,
32
+ 'worker-count-down' => 'SIGTTOU',
33
+ 'worker-count-up' => 'SIGTTIN'
34
+ }.freeze
35
+
36
+ # @deprecated 6.0.0
37
+ COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
38
+
39
+ # commands that cannot be used in a request
40
+ NO_REQ_COMMANDS = %w[info reopen-log worker-count-down worker-count-up].freeze
41
+
42
+ # @version 5.0.0
43
+ PRINTABLE_COMMANDS = %w[gc-stats stats thread-backtraces].freeze
15
44
 
16
45
  def initialize(argv, stdout=STDOUT, stderr=STDERR)
17
46
  @state = nil
@@ -22,7 +51,7 @@ module Puma
22
51
  @control_auth_token = nil
23
52
  @config_file = nil
24
53
  @command = nil
25
- @environment = ENV['RACK_ENV']
54
+ @environment = ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']
26
55
 
27
56
  @argv = argv.dup
28
57
  @stdout = stdout
@@ -30,7 +59,7 @@ module Puma
30
59
  @cli_options = {}
31
60
 
32
61
  opts = OptionParser.new do |o|
33
- o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{COMMANDS.join("|")})"
62
+ o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{CMD_PATH_SIG_MAP.keys.join("|")})"
34
63
 
35
64
  o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
36
65
  @state = arg
@@ -71,7 +100,7 @@ module Puma
71
100
  end
72
101
 
73
102
  o.on_tail("-V", "--version", "Show version") do
74
- puts Const::PUMA_VERSION
103
+ @stdout.puts Const::PUMA_VERSION
75
104
  exit
76
105
  end
77
106
  end
@@ -81,6 +110,15 @@ module Puma
81
110
 
82
111
  @command = argv.shift
83
112
 
113
+ # check presence of command
114
+ unless @command
115
+ raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
116
+ end
117
+
118
+ unless CMD_PATH_SIG_MAP.key? @command
119
+ raise "Invalid command: #{@command}"
120
+ end
121
+
84
122
  unless @config_file == '-'
85
123
  environment = @environment || 'development'
86
124
 
@@ -99,16 +137,6 @@ module Puma
99
137
  @pidfile ||= config.options[:pidfile]
100
138
  end
101
139
  end
102
-
103
- # check present of command
104
- unless @command
105
- raise "Available commands: #{COMMANDS.join(", ")}"
106
- end
107
-
108
- unless COMMANDS.include? @command
109
- raise "Invalid command: #{@command}"
110
- end
111
-
112
140
  rescue => e
113
141
  @stdout.puts e.message
114
142
  exit 1
@@ -132,7 +160,7 @@ module Puma
132
160
  @pid = sf.pid
133
161
  elsif @pidfile
134
162
  # get pid from pid_file
135
- File.open(@pidfile) { |f| @pid = f.read.to_i }
163
+ @pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
136
164
  end
137
165
  end
138
166
 
@@ -140,23 +168,27 @@ module Puma
140
168
  uri = URI.parse @control_url
141
169
 
142
170
  # create server object by scheme
143
- server = case uri.scheme
144
- when "ssl"
145
- require 'openssl'
146
- OpenSSL::SSL::SSLSocket.new(
147
- TCPSocket.new(uri.host, uri.port),
148
- OpenSSL::SSL::SSLContext.new
149
- ).tap(&:connect)
150
- when "tcp"
151
- TCPSocket.new uri.host, uri.port
152
- when "unix"
153
- UNIXSocket.new "#{uri.host}#{uri.path}"
154
- else
155
- raise "Invalid scheme: #{uri.scheme}"
156
- end
157
-
158
- if @command == "status"
159
- message "Puma is started"
171
+ server =
172
+ case uri.scheme
173
+ when 'ssl'
174
+ require 'openssl'
175
+ OpenSSL::SSL::SSLSocket.new(
176
+ TCPSocket.new(uri.host, uri.port),
177
+ OpenSSL::SSL::SSLContext.new)
178
+ .tap { |ssl| ssl.sync_close = true } # default is false
179
+ .tap(&:connect)
180
+ when 'tcp'
181
+ TCPSocket.new uri.host, uri.port
182
+ when 'unix'
183
+ # check for abstract UNIXSocket
184
+ UNIXSocket.new(@control_url.start_with?('unix://@') ?
185
+ "\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}")
186
+ else
187
+ raise "Invalid scheme: #{uri.scheme}"
188
+ end
189
+
190
+ if @command == 'status'
191
+ message 'Puma is started'
160
192
  else
161
193
  url = "/#{@command}"
162
194
 
@@ -164,10 +196,10 @@ module Puma
164
196
  url = url + "?token=#{@control_auth_token}"
165
197
  end
166
198
 
167
- server << "GET #{url} HTTP/1.0\r\n\r\n"
199
+ server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
168
200
 
169
201
  unless data = server.read
170
- raise "Server closed connection before responding"
202
+ raise 'Server closed connection before responding'
171
203
  end
172
204
 
173
205
  response = data.split("\r\n")
@@ -176,67 +208,59 @@ module Puma
176
208
  raise "Server sent empty response"
177
209
  end
178
210
 
179
- (@http,@code,@message) = response.first.split(" ",3)
211
+ @http, @code, @message = response.first.split(' ',3)
180
212
 
181
- if @code == "403"
182
- raise "Unauthorized access to server (wrong auth token)"
183
- elsif @code == "404"
213
+ if @code == '403'
214
+ raise 'Unauthorized access to server (wrong auth token)'
215
+ elsif @code == '404'
184
216
  raise "Command error: #{response.last}"
185
- elsif @code != "200"
217
+ elsif @code != '200'
186
218
  raise "Bad response from server: #{@code}"
187
219
  end
188
220
 
189
221
  message "Command #{@command} sent success"
190
- message response.last if @command == "stats" || @command == "gc-stats"
222
+ message response.last if PRINTABLE_COMMANDS.include?(@command)
191
223
  end
192
224
  ensure
193
- server.close if server && !server.closed?
225
+ if server
226
+ if uri.scheme == 'ssl'
227
+ server.sysclose
228
+ else
229
+ server.close unless server.closed?
230
+ end
231
+ end
194
232
  end
195
233
 
196
234
  def send_signal
197
235
  unless @pid
198
- raise "Neither pid nor control url available"
236
+ raise 'Neither pid nor control url available'
199
237
  end
200
238
 
201
239
  begin
240
+ sig = CMD_PATH_SIG_MAP[@command]
202
241
 
203
- case @command
204
- when "restart"
205
- Process.kill "SIGUSR2", @pid
206
-
207
- when "halt"
208
- Process.kill "QUIT", @pid
209
-
210
- when "stop"
211
- Process.kill "SIGTERM", @pid
212
-
213
- when "stats"
214
- puts "Stats not available via pid only"
215
- return
216
-
217
- when "reload-worker-directory"
218
- puts "reload-worker-directory not available via pid only"
242
+ if sig.nil?
243
+ @stdout.puts "'#{@command}' not available via pid only"
244
+ @stdout.flush unless @stdout.sync
219
245
  return
220
-
221
- when "phased-restart"
222
- Process.kill "SIGUSR1", @pid
223
-
224
- when "status"
246
+ elsif sig.start_with? 'SIG'
247
+ if Signal.list.key? sig.sub(/\ASIG/, '')
248
+ Process.kill sig, @pid
249
+ else
250
+ raise "Signal '#{sig}' not available'"
251
+ end
252
+ elsif @command == 'status'
225
253
  begin
226
254
  Process.kill 0, @pid
227
- puts "Puma is started"
255
+ @stdout.puts 'Puma is started'
256
+ @stdout.flush unless @stdout.sync
228
257
  rescue Errno::ESRCH
229
- raise "Puma is not running"
258
+ raise 'Puma is not running'
230
259
  end
231
-
232
- return
233
-
234
- else
235
260
  return
236
261
  end
237
-
238
262
  rescue SystemCallError
239
- if @command == "restart"
263
+ if @command == 'restart'
240
264
  start
241
265
  else
242
266
  raise "No pid '#{@pid}' found"
@@ -247,14 +271,13 @@ module Puma
247
271
  end
248
272
 
249
273
  def run
250
- return start if @command == "start"
251
-
274
+ return start if @command == 'start'
252
275
  prepare_configuration
253
276
 
254
- if Puma.windows?
277
+ if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
255
278
  send_request
256
279
  else
257
- @control_url ? send_request : send_signal
280
+ send_signal
258
281
  end
259
282
 
260
283
  rescue => e
@@ -262,7 +285,7 @@ module Puma
262
285
  exit 1
263
286
  end
264
287
 
265
- private
288
+ private
266
289
  def start
267
290
  require 'puma/cli'
268
291
 
data/lib/puma/detect.rb CHANGED
@@ -1,15 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # This file can be loaded independently of puma.rb, so it cannot have any code
4
+ # that assumes puma.rb is loaded.
5
+
6
+
3
7
  module Puma
4
- IS_JRUBY = defined?(JRUBY_VERSION)
8
+ # @version 5.2.1
9
+ HAS_FORK = ::Process.respond_to? :fork
10
+
11
+ IS_JRUBY = Object.const_defined? :JRUBY_VERSION
12
+
13
+ IS_OSX = RUBY_PLATFORM.include? 'darwin'
14
+
15
+ IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/) ||
16
+ IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
17
+
18
+ # @version 5.2.0
19
+ IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
5
20
 
6
21
  def self.jruby?
7
22
  IS_JRUBY
8
23
  end
9
24
 
10
- IS_WINDOWS = RUBY_PLATFORM =~ /mswin|ming|cygwin/
25
+ def self.osx?
26
+ IS_OSX
27
+ end
11
28
 
12
29
  def self.windows?
13
30
  IS_WINDOWS
14
31
  end
32
+
33
+ # @version 5.0.0
34
+ def self.mri?
35
+ IS_MRI
36
+ end
37
+
38
+ # @version 5.0.0
39
+ def self.forkable?
40
+ HAS_FORK
41
+ end
15
42
  end