puma 3.6.0 → 3.12.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.

Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/{History.txt → History.md} +293 -79
  3. data/README.md +143 -227
  4. data/docs/architecture.md +36 -0
  5. data/{DEPLOYMENT.md → docs/deployment.md} +0 -0
  6. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  7. data/docs/images/puma-connection-flow.png +0 -0
  8. data/docs/images/puma-general-arch.png +0 -0
  9. data/docs/plugins.md +28 -0
  10. data/docs/restart.md +39 -0
  11. data/docs/signals.md +56 -3
  12. data/docs/systemd.md +124 -22
  13. data/ext/puma_http11/extconf.rb +2 -0
  14. data/ext/puma_http11/http11_parser.c +85 -84
  15. data/ext/puma_http11/http11_parser.h +1 -0
  16. data/ext/puma_http11/http11_parser.rl +10 -9
  17. data/ext/puma_http11/io_buffer.c +7 -7
  18. data/ext/puma_http11/mini_ssl.c +62 -6
  19. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
  20. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -2
  21. data/ext/puma_http11/puma_http11.c +1 -0
  22. data/lib/puma.rb +13 -5
  23. data/lib/puma/app/status.rb +8 -0
  24. data/lib/puma/binder.rb +21 -14
  25. data/lib/puma/cli.rb +49 -33
  26. data/lib/puma/client.rb +39 -4
  27. data/lib/puma/cluster.rb +51 -11
  28. data/lib/puma/commonlogger.rb +19 -20
  29. data/lib/puma/compat.rb +3 -7
  30. data/lib/puma/configuration.rb +133 -130
  31. data/lib/puma/const.rb +13 -37
  32. data/lib/puma/control_cli.rb +38 -35
  33. data/lib/puma/convenient.rb +3 -3
  34. data/lib/puma/detect.rb +3 -1
  35. data/lib/puma/dsl.rb +80 -58
  36. data/lib/puma/events.rb +6 -8
  37. data/lib/puma/io_buffer.rb +1 -1
  38. data/lib/puma/jruby_restart.rb +0 -1
  39. data/lib/puma/launcher.rb +52 -30
  40. data/lib/puma/minissl.rb +73 -4
  41. data/lib/puma/null_io.rb +6 -13
  42. data/lib/puma/plugin/tmp_restart.rb +1 -2
  43. data/lib/puma/rack/builder.rb +3 -0
  44. data/lib/puma/rack/urlmap.rb +9 -8
  45. data/lib/puma/reactor.rb +135 -0
  46. data/lib/puma/runner.rb +23 -1
  47. data/lib/puma/server.rb +117 -34
  48. data/lib/puma/single.rb +14 -3
  49. data/lib/puma/thread_pool.rb +67 -20
  50. data/lib/puma/util.rb +1 -5
  51. data/lib/rack/handler/puma.rb +58 -17
  52. data/tools/jungle/README.md +12 -2
  53. data/tools/jungle/init.d/README.md +9 -2
  54. data/tools/jungle/init.d/puma +32 -62
  55. data/tools/jungle/init.d/run-puma +5 -1
  56. data/tools/jungle/rc.d/README.md +74 -0
  57. data/tools/jungle/rc.d/puma +61 -0
  58. data/tools/jungle/rc.d/puma.conf +10 -0
  59. data/tools/trickletest.rb +1 -1
  60. metadata +22 -92
  61. data/Gemfile +0 -13
  62. data/Manifest.txt +0 -77
  63. data/Rakefile +0 -158
  64. data/lib/puma/rack/backports/uri/common_18.rb +0 -59
  65. data/lib/puma/rack/backports/uri/common_192.rb +0 -55
  66. data/puma.gemspec +0 -52
@@ -55,6 +55,14 @@ module Puma
55
55
  return rack_response(200, OK_STATUS)
56
56
  end
57
57
 
58
+ when /\/gc$/
59
+ GC.start
60
+ return rack_response(200, OK_STATUS)
61
+
62
+ when /\/gc-stats$/
63
+ json = "{" + GC.stat.map { |k, v| "\"#{k}\": #{v}" }.join(",") + "}"
64
+ return rack_response(200, json)
65
+
58
66
  when /\/stats$/
59
67
  return rack_response(200, @cli.stats)
60
68
  else
@@ -1,5 +1,8 @@
1
- require 'puma/const'
2
1
  require 'uri'
2
+ require 'socket'
3
+
4
+ require 'puma/const'
5
+ require 'puma/util'
3
6
 
4
7
  module Puma
5
8
  class Binder
@@ -102,7 +105,7 @@ module Puma
102
105
  io = add_tcp_listener uri.host, uri.port, opt, bak
103
106
  end
104
107
 
105
- @listeners << [str, io]
108
+ @listeners << [str, io] if io
106
109
  when "unix"
107
110
  path = "#{uri.host}#{uri.path}".gsub("%20", " ")
108
111
 
@@ -117,7 +120,7 @@ module Puma
117
120
 
118
121
  umask = nil
119
122
  mode = nil
120
- backlog = nil
123
+ backlog = 1024
121
124
 
122
125
  if uri.query
123
126
  params = Util.parse_query uri.query
@@ -140,11 +143,11 @@ module Puma
140
143
 
141
144
  @listeners << [str, io]
142
145
  when "ssl"
143
- MiniSSL.check
144
-
145
146
  params = Util.parse_query uri.query
146
147
  require 'puma/minissl'
147
148
 
149
+ MiniSSL.check
150
+
148
151
  ctx = MiniSSL::Context.new
149
152
 
150
153
  if defined?(JRUBY_VERSION)
@@ -159,6 +162,7 @@ module Puma
159
162
  end
160
163
 
161
164
  ctx.keystore_pass = params['keystore-pass']
165
+ ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
162
166
  else
163
167
  unless params['key']
164
168
  @events.error "Please specify the SSL key via 'key='"
@@ -179,6 +183,7 @@ module Puma
179
183
  end
180
184
 
181
185
  ctx.ca = params['ca'] if params['ca']
186
+ ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
182
187
  end
183
188
 
184
189
  if params['verify_mode']
@@ -206,7 +211,7 @@ module Puma
206
211
  io = add_ssl_listener uri.host, uri.port, ctx
207
212
  end
208
213
 
209
- @listeners << [str, io]
214
+ @listeners << [str, io] if io
210
215
  else
211
216
  logger.error "Invalid URI: #{str}"
212
217
  end
@@ -242,9 +247,10 @@ module Puma
242
247
  end
243
248
  end
244
249
 
245
- def localhost_addresses
246
- addrs = TCPSocket.gethostbyname "localhost"
247
- addrs[3..-1].uniq
250
+ def loopback_addresses
251
+ Socket.ip_address_list.select do |addrinfo|
252
+ addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
253
+ end.map { |addrinfo| addrinfo.ip_address }.uniq
248
254
  end
249
255
 
250
256
  # Tell the server to listen on host +host+, port +port+.
@@ -256,7 +262,7 @@ module Puma
256
262
  #
257
263
  def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
258
264
  if host == "localhost"
259
- localhost_addresses.each do |addr|
265
+ loopback_addresses.each do |addr|
260
266
  add_tcp_listener addr, port, optimize_for_latency, backlog
261
267
  end
262
268
  return
@@ -295,8 +301,8 @@ module Puma
295
301
  MiniSSL.check
296
302
 
297
303
  if host == "localhost"
298
- localhost_addresses.each do |addr|
299
- add_ssl_listener addr, port, optimize_for_latency, backlog
304
+ loopback_addresses.each do |addr|
305
+ add_ssl_listener addr, port, ctx, optimize_for_latency, backlog
300
306
  end
301
307
  return
302
308
  end
@@ -309,6 +315,7 @@ module Puma
309
315
  s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
310
316
  s.listen backlog
311
317
 
318
+
312
319
  ssl = MiniSSL::Server.new s, ctx
313
320
  env = @proto_env.dup
314
321
  env[HTTPS_KEY] = HTTPS
@@ -340,7 +347,7 @@ module Puma
340
347
 
341
348
  # Tell the server to listen on +path+ as a UNIX domain socket.
342
349
  #
343
- def add_unix_listener(path, umask=nil, mode=nil, backlog=nil)
350
+ def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
344
351
  @unix_paths << path
345
352
 
346
353
  # Let anyone connect by default
@@ -361,7 +368,7 @@ module Puma
361
368
  end
362
369
 
363
370
  s = UNIXServer.new(path)
364
- s.listen backlog if backlog
371
+ s.listen backlog
365
372
  @ios << s
366
373
  ensure
367
374
  File.umask old_mask
@@ -1,7 +1,11 @@
1
1
  require 'optparse'
2
2
  require 'uri'
3
3
 
4
+ require 'puma'
5
+ require 'puma/configuration'
4
6
  require 'puma/launcher'
7
+ require 'puma/const'
8
+ require 'puma/events'
5
9
 
6
10
  module Puma
7
11
  class << self
@@ -44,21 +48,21 @@ module Puma
44
48
  @parser.parse! @argv
45
49
 
46
50
  if file = @argv.shift
47
- @conf.configure do |c|
48
- c.rackup file
51
+ @conf.configure do |user_config, file_config|
52
+ file_config.rackup file
49
53
  end
50
54
  end
51
55
  rescue UnsupportedOption
52
56
  exit 1
53
57
  end
54
58
 
55
- @conf.configure do |c|
59
+ @conf.configure do |user_config, file_config|
56
60
  if @stdout || @stderr
57
- c.stdout_redirect @stdout, @stderr, @append
61
+ user_config.stdout_redirect @stdout, @stderr, @append
58
62
  end
59
63
 
60
64
  if @control_url
61
- c.activate_control_app @control_url, @control_options
65
+ user_config.activate_control_app @control_url, @control_options
62
66
  end
63
67
  end
64
68
 
@@ -80,27 +84,35 @@ module Puma
80
84
  raise UnsupportedOption
81
85
  end
82
86
 
87
+ def configure_control_url(command_line_arg)
88
+ if command_line_arg
89
+ @control_url = command_line_arg
90
+ elsif Puma.jruby?
91
+ unsupported "No default url available on JRuby"
92
+ end
93
+ end
94
+
83
95
  # Build the OptionParser object to handle the available options.
84
96
  #
85
97
 
86
98
  def setup_options
87
- @conf = Configuration.new do |c|
99
+ @conf = Configuration.new do |user_config, file_config|
88
100
  @parser = OptionParser.new do |o|
89
101
  o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
90
- c.bind arg
102
+ user_config.bind arg
91
103
  end
92
104
 
93
105
  o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
94
- c.load arg
106
+ file_config.load arg
95
107
  end
96
108
 
97
- o.on "--control URL", "The bind url to use for the control server",
98
- "Use 'auto' to use temp unix server" do |arg|
99
- if arg
100
- @control_url = arg
101
- elsif Puma.jruby?
102
- unsupported "No default url available on JRuby"
103
- end
109
+ o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
110
+ configure_control_url(arg)
111
+ end
112
+
113
+ # alias --control-url for backwards-compatibility
114
+ o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
115
+ configure_control_url(arg)
104
116
  end
105
117
 
106
118
  o.on "--control-token TOKEN",
@@ -109,21 +121,21 @@ module Puma
109
121
  end
110
122
 
111
123
  o.on "-d", "--daemon", "Daemonize the server into the background" do
112
- c.daemonize
113
- c.quiet
124
+ user_config.daemonize
125
+ user_config.quiet
114
126
  end
115
127
 
116
128
  o.on "--debug", "Log lowlevel debugging information" do
117
- c.debug
129
+ user_config.debug
118
130
  end
119
131
 
120
132
  o.on "--dir DIR", "Change to DIR before starting" do |d|
121
- c.directory d
133
+ user_config.directory d
122
134
  end
123
135
 
124
136
  o.on "-e", "--environment ENVIRONMENT",
125
137
  "The environment to run the Rack app on (default development)" do |arg|
126
- c.environment arg
138
+ user_config.environment arg
127
139
  end
128
140
 
129
141
  o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
@@ -132,50 +144,54 @@ module Puma
132
144
 
133
145
  o.on "-p", "--port PORT", "Define the TCP port to bind to",
134
146
  "Use -b for more advanced options" do |arg|
135
- c.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
147
+ user_config.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
136
148
  end
137
149
 
138
150
  o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
139
- c.pidfile arg
151
+ user_config.pidfile arg
140
152
  end
141
153
 
142
154
  o.on "--preload", "Preload the app. Cluster mode only" do
143
- c.preload_app!
155
+ user_config.preload_app!
144
156
  end
145
157
 
146
158
  o.on "--prune-bundler", "Prune out the bundler env if possible" do
147
- c.prune_bundler
159
+ user_config.prune_bundler
148
160
  end
149
161
 
150
162
  o.on "-q", "--quiet", "Do not log requests internally (default true)" do
151
- c.quiet
163
+ user_config.quiet
152
164
  end
153
165
 
154
166
  o.on "-v", "--log-requests", "Log requests as they occur" do
155
- c.log_requests
167
+ user_config.log_requests
156
168
  end
157
169
 
158
170
  o.on "-R", "--restart-cmd CMD",
159
171
  "The puma command to run during a hot restart",
160
172
  "Default: inferred" do |cmd|
161
- c.restart_command cmd
173
+ user_config.restart_command cmd
162
174
  end
163
175
 
164
176
  o.on "-S", "--state PATH", "Where to store the state details" do |arg|
165
- c.state_path arg
177
+ user_config.state_path arg
166
178
  end
167
179
 
168
180
  o.on '-t', '--threads INT', "min:max threads to use (default 0:16)" do |arg|
169
181
  min, max = arg.split(":")
170
182
  if max
171
- c.threads min, max
183
+ user_config.threads min, max
172
184
  else
173
- c.threads min, min
185
+ user_config.threads min, min
174
186
  end
175
187
  end
176
188
 
177
189
  o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
178
- c.tcp_mode!
190
+ user_config.tcp_mode!
191
+ end
192
+
193
+ o.on "--early-hints", "Enable early hints support" do
194
+ user_config.early_hints
179
195
  end
180
196
 
181
197
  o.on "-V", "--version", "Print the version information" do
@@ -185,11 +201,11 @@ module Puma
185
201
 
186
202
  o.on "-w", "--workers COUNT",
187
203
  "Activate cluster mode: How many worker processes to create" do |arg|
188
- c.workers arg
204
+ user_config.workers arg
189
205
  end
190
206
 
191
207
  o.on "--tag NAME", "Additional text to display in process listing" do |arg|
192
- c.tag arg
208
+ user_config.tag arg
193
209
  end
194
210
 
195
211
  o.on "--redirect-stdout FILE", "Redirect STDOUT to a specific file" do |arg|
@@ -21,6 +21,17 @@ module Puma
21
21
 
22
22
  class ConnectionError < RuntimeError; end
23
23
 
24
+ # An instance of this class represents a unique request from a client.
25
+ # For example a web request from a browser or from CURL. This
26
+ #
27
+ # An instance of `Puma::Client` can be used as if it were an IO object
28
+ # for example it is passed into `IO.select` inside of the `Puma::Reactor`.
29
+ # This is accomplished by the `to_io` method which gets called on any
30
+ # non-IO objects being used with the IO api such as `IO.select.
31
+ #
32
+ # Instances of this class are responsible for knowing if
33
+ # the header and body are fully buffered via the `try_to_finish` method.
34
+ # They can be used to "time out" a response via the `timeout_at` reader.
24
35
  class Client
25
36
  include Puma::Const
26
37
  extend Puma::Delegation
@@ -111,6 +122,7 @@ module Puma
111
122
  begin
112
123
  @io.close
113
124
  rescue IOError
125
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
114
126
  end
115
127
  end
116
128
 
@@ -155,7 +167,8 @@ module Puma
155
167
  len = line.strip.to_i(16)
156
168
  if len == 0
157
169
  @body.rewind
158
- @buffer = nil
170
+ rest = io.read
171
+ @buffer = rest.empty? ? nil : rest
159
172
  @requests_served += 1
160
173
  @ready = true
161
174
  return true
@@ -215,14 +228,20 @@ module Puma
215
228
  end
216
229
 
217
230
  def setup_body
218
- @in_data_phase = true
231
+ if @env[HTTP_EXPECT] == CONTINUE
232
+ # TODO allow a hook here to check the headers before
233
+ # going forward
234
+ @io << HTTP_11_100
235
+ @io.flush
236
+ end
237
+
219
238
  @read_header = false
220
239
 
221
240
  body = @parser.body
222
241
 
223
242
  te = @env[TRANSFER_ENCODING2]
224
243
 
225
- if te == CHUNKED
244
+ if te && CHUNKED.casecmp(te) == 0
226
245
  return setup_chunked_body(body)
227
246
  end
228
247
 
@@ -276,6 +295,14 @@ module Puma
276
295
  raise ConnectionError, "Connection error detected during read"
277
296
  end
278
297
 
298
+ # No data means a closed socket
299
+ unless data
300
+ @buffer = nil
301
+ @requests_served += 1
302
+ @ready = true
303
+ raise EOFError
304
+ end
305
+
279
306
  if @buffer
280
307
  @buffer << data
281
308
  else
@@ -290,7 +317,7 @@ module Puma
290
317
  raise HttpParserError,
291
318
  "HEADER is longer than allowed, aborting client early."
292
319
  end
293
-
320
+
294
321
  false
295
322
  end
296
323
 
@@ -305,6 +332,14 @@ module Puma
305
332
  raise e
306
333
  end
307
334
 
335
+ # No data means a closed socket
336
+ unless data
337
+ @buffer = nil
338
+ @requests_served += 1
339
+ @ready = true
340
+ raise EOFError
341
+ end
342
+
308
343
  if @buffer
309
344
  @buffer << data
310
345
  else
@@ -1,8 +1,24 @@
1
1
  require 'puma/runner'
2
+ require 'puma/util'
3
+ require 'puma/plugin'
4
+
2
5
  require 'time'
3
6
 
4
7
  module Puma
8
+ # This class is instantiated by the `Puma::Launcher` and used
9
+ # to boot and serve a Ruby application when puma "workers" are needed
10
+ # i.e. when using multi-processes. For example `$ puma -w 5`
11
+ #
12
+ # At the core of this class is running an instance of `Puma::Server` which
13
+ # gets created via the `start_server` method from the `Puma::Runner` class
14
+ # that this inherits from.
15
+ #
16
+ # An instance of this class will spawn the number of processes passed in
17
+ # via the `spawn_workers` method call. Each worker will have it's own
18
+ # instance of a `Puma::Server`.
5
19
  class Cluster < Runner
20
+ WORKER_CHECK_INTERVAL = 5
21
+
6
22
  def initialize(cli, events)
7
23
  super cli, events
8
24
 
@@ -19,7 +35,7 @@ module Puma
19
35
  @workers.each { |x| x.term }
20
36
 
21
37
  begin
22
- Process.waitall
38
+ @workers.each { |w| Process.waitpid(w.pid) }
23
39
  rescue Interrupt
24
40
  log "! Cancelled waiting for workers"
25
41
  end
@@ -110,6 +126,7 @@ module Puma
110
126
 
111
127
  def spawn_workers
112
128
  diff = @options[:workers] - @workers.size
129
+ return if diff < 1
113
130
 
114
131
  master = Process.pid
115
132
 
@@ -135,6 +152,21 @@ module Puma
135
152
  end
136
153
  end
137
154
 
155
+ def cull_workers
156
+ diff = @workers.size - @options[:workers]
157
+ return if diff < 1
158
+
159
+ debug "Culling #{diff.inspect} workers"
160
+
161
+ workers_to_cull = @workers[-diff,diff]
162
+ debug "Workers to cull: #{workers_to_cull.inspect}"
163
+
164
+ workers_to_cull.each do |worker|
165
+ log "- Worker #{worker.index} (pid: #{worker.pid}) terminating"
166
+ worker.term
167
+ end
168
+ end
169
+
138
170
  def next_worker_index
139
171
  all_positions = 0...@options[:workers]
140
172
  occupied_positions = @workers.map { |w| w.index }
@@ -149,7 +181,7 @@ module Puma
149
181
  def check_workers(force=false)
150
182
  return if !force && @next_check && @next_check >= Time.now
151
183
 
152
- @next_check = Time.now + 5
184
+ @next_check = Time.now + WORKER_CHECK_INTERVAL
153
185
 
154
186
  any = false
155
187
 
@@ -175,6 +207,7 @@ module Puma
175
207
 
176
208
  @workers.delete_if(&:dead?)
177
209
 
210
+ cull_workers
178
211
  spawn_workers
179
212
 
180
213
  if all_workers_booted?
@@ -202,12 +235,13 @@ module Puma
202
235
  begin
203
236
  @wakeup.write "!" unless @wakeup.closed?
204
237
  rescue SystemCallError, IOError
238
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
205
239
  end
206
240
  end
207
241
 
208
242
  def worker(index, master)
209
- title = "puma: cluster worker #{index}: #{master}"
210
- title << " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
243
+ title = "puma: cluster worker #{index}: #{master}"
244
+ title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
211
245
  $0 = title
212
246
 
213
247
  Signal.trap "SIGINT", "IGNORE"
@@ -245,6 +279,7 @@ module Puma
245
279
  begin
246
280
  @worker_write << "b#{Process.pid}\n"
247
281
  rescue SystemCallError, IOError
282
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
248
283
  STDERR.puts "Master seems to have exited, exiting."
249
284
  return
250
285
  end
@@ -253,13 +288,16 @@ module Puma
253
288
  base_payload = "p#{Process.pid}"
254
289
 
255
290
  while true
256
- sleep 5
291
+ sleep WORKER_CHECK_INTERVAL
257
292
  begin
258
- b = server.backlog
259
- r = server.running
260
- payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r} }\n!
293
+ b = server.backlog || 0
294
+ r = server.running || 0
295
+ t = server.pool_capacity || 0
296
+ m = server.max_threads || 0
297
+ payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n!
261
298
  io << payload
262
299
  rescue IOError
300
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
263
301
  break
264
302
  end
265
303
  end
@@ -315,7 +353,7 @@ module Puma
315
353
  def stats
316
354
  old_worker_count = @workers.count { |w| w.phase != @phase }
317
355
  booted_worker_count = @workers.count { |w| w.booted? }
318
- worker_status = '[' + @workers.map{ |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
356
+ worker_status = '[' + @workers.map { |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
319
357
  %Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
320
358
  end
321
359
 
@@ -337,7 +375,6 @@ module Puma
337
375
 
338
376
  Signal.trap "TTOU" do
339
377
  @options[:workers] -= 1 if @options[:workers] >= 2
340
- @workers.last.term
341
378
  wakeup!
342
379
  end
343
380
 
@@ -351,7 +388,10 @@ module Puma
351
388
  log "Early termination of worker"
352
389
  exit! 0
353
390
  else
391
+ stop_workers
354
392
  stop
393
+
394
+ raise SignalException, "SIGTERM"
355
395
  end
356
396
  end
357
397
  end
@@ -445,7 +485,7 @@ module Puma
445
485
 
446
486
  force_check = false
447
487
 
448
- res = IO.select([read], nil, nil, 5)
488
+ res = IO.select([read], nil, nil, WORKER_CHECK_INTERVAL)
449
489
 
450
490
  if res
451
491
  req = read.read_nonblock(1)