puma 3.5.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 (67) hide show
  1. checksums.yaml +5 -5
  2. data/{History.txt → History.md} +318 -75
  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 +67 -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 +22 -17
  25. data/lib/puma/cli.rb +49 -33
  26. data/lib/puma/client.rb +149 -4
  27. data/lib/puma/cluster.rb +54 -12
  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 +19 -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 +61 -30
  40. data/lib/puma/minissl.rb +85 -4
  41. data/lib/puma/null_io.rb +6 -13
  42. data/lib/puma/plugin.rb +12 -1
  43. data/lib/puma/plugin/tmp_restart.rb +1 -2
  44. data/lib/puma/rack/builder.rb +3 -0
  45. data/lib/puma/rack/urlmap.rb +9 -8
  46. data/lib/puma/reactor.rb +135 -0
  47. data/lib/puma/runner.rb +27 -1
  48. data/lib/puma/server.rb +134 -32
  49. data/lib/puma/single.rb +17 -3
  50. data/lib/puma/thread_pool.rb +67 -20
  51. data/lib/puma/util.rb +1 -5
  52. data/lib/rack/handler/puma.rb +58 -17
  53. data/tools/jungle/README.md +12 -2
  54. data/tools/jungle/init.d/README.md +9 -2
  55. data/tools/jungle/init.d/puma +32 -62
  56. data/tools/jungle/init.d/run-puma +5 -1
  57. data/tools/jungle/rc.d/README.md +74 -0
  58. data/tools/jungle/rc.d/puma +61 -0
  59. data/tools/jungle/rc.d/puma.conf +10 -0
  60. data/tools/trickletest.rb +1 -1
  61. metadata +22 -92
  62. data/Gemfile +0 -13
  63. data/Manifest.txt +0 -77
  64. data/Rakefile +0 -158
  65. data/lib/puma/rack/backports/uri/common_18.rb +0 -59
  66. data/lib/puma/rack/backports/uri/common_192.rb +0 -55
  67. data/puma.gemspec +0 -52
@@ -53,6 +53,8 @@ module Puma
53
53
  415 => 'Unsupported Media Type',
54
54
  416 => 'Range Not Satisfiable',
55
55
  417 => 'Expectation Failed',
56
+ 418 => 'I\'m A Teapot',
57
+ 421 => 'Misdirected Request',
56
58
  422 => 'Unprocessable Entity',
57
59
  423 => 'Locked',
58
60
  424 => 'Failed Dependency',
@@ -60,6 +62,7 @@ module Puma
60
62
  428 => 'Precondition Required',
61
63
  429 => 'Too Many Requests',
62
64
  431 => 'Request Header Fields Too Large',
65
+ 451 => 'Unavailable For Legal Reasons',
63
66
  500 => 'Internal Server Error',
64
67
  501 => 'Not Implemented',
65
68
  502 => 'Bad Gateway',
@@ -73,19 +76,14 @@ module Puma
73
76
  511 => 'Network Authentication Required'
74
77
  }
75
78
 
76
- SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
77
- [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
78
- }.flatten]
79
-
80
79
  # For some HTTP status codes the client only expects headers.
81
80
  #
82
81
 
83
- no_body = {}
84
- ((100..199).to_a << 204 << 205 << 304).each do |code|
85
- no_body[code] = true
86
- end
87
-
88
- STATUS_WITH_NO_ENTITY_BODY = no_body
82
+ STATUS_WITH_NO_ENTITY_BODY = {
83
+ 204 => true,
84
+ 205 => true,
85
+ 304 => true
86
+ }
89
87
 
90
88
  # Frequently used constants when constructing requests or responses. Many times
91
89
  # the constant just refers to a string with the same contents. Using these constants
@@ -100,10 +98,10 @@ module Puma
100
98
  # too taxing on performance.
101
99
  module Const
102
100
 
103
- PUMA_VERSION = VERSION = "3.5.0".freeze
104
- CODE_NAME = "Amateur Raccoon Rocketry".freeze
101
+ PUMA_VERSION = VERSION = "3.12.0".freeze
102
+ CODE_NAME = "Llamas in Pajamas".freeze
105
103
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
106
-
104
+
107
105
  FAST_TRACK_KA_TIMEOUT = 0.2
108
106
 
109
107
  # The default number of seconds for another request within a persistent
@@ -118,15 +116,6 @@ module Puma
118
116
  # sending data back
119
117
  WRITE_TIMEOUT = 10
120
118
 
121
- # How long, after raising the ForceShutdown of a thread during
122
- # forced shutdown mode, to wait for the thread to try and finish
123
- # up it's work before leaving the thread to die on the vine.
124
- SHUTDOWN_GRACE_TIME = 5 # seconds
125
-
126
- DATE = "Date".freeze
127
-
128
- SCRIPT_NAME = "SCRIPT_NAME".freeze
129
-
130
119
  # The original URI requested by the client.
131
120
  REQUEST_URI= 'REQUEST_URI'.freeze
132
121
  REQUEST_PATH = 'REQUEST_PATH'.freeze
@@ -163,26 +152,12 @@ module Puma
163
152
  # Maximum request body size before it is moved out of memory and into a tempfile for reading.
164
153
  MAX_BODY = MAX_HEADER
165
154
 
166
- # A frozen format for this is about 15% faster
167
- STATUS_FORMAT = "HTTP/1.1 %d %s\r\nConnection: close\r\n".freeze
168
-
169
- CONTENT_TYPE = "Content-Type".freeze
170
-
171
- LAST_MODIFIED = "Last-Modified".freeze
172
- ETAG = "ETag".freeze
173
- SLASH = "/".freeze
174
155
  REQUEST_METHOD = "REQUEST_METHOD".freeze
175
- GET = "GET".freeze
176
156
  HEAD = "HEAD".freeze
177
157
  # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
178
- ETAG_FORMAT = "\"%x-%x-%x\"".freeze
179
158
  LINE_END = "\r\n".freeze
180
159
  REMOTE_ADDR = "REMOTE_ADDR".freeze
181
160
  HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR".freeze
182
- HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE".freeze
183
- HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH".freeze
184
- REDIRECT = "HTTP/1.1 302 Found\r\nLocation: %s\r\nConnection: close\r\n\r\n".freeze
185
- HOST = "HOST".freeze
186
161
 
187
162
  SERVER_NAME = "SERVER_NAME".freeze
188
163
  SERVER_PORT = "SERVER_PORT".freeze
@@ -195,7 +170,6 @@ module Puma
195
170
 
196
171
  SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
197
172
  HTTP_11 = "HTTP/1.1".freeze
198
- HTTP_10 = "HTTP/1.0".freeze
199
173
 
200
174
  SERVER_SOFTWARE = "SERVER_SOFTWARE".freeze
201
175
  GATEWAY_INTERFACE = "GATEWAY_INTERFACE".freeze
@@ -219,7 +193,10 @@ module Puma
219
193
 
220
194
  HTTP_VERSION = "HTTP_VERSION".freeze
221
195
  HTTP_CONNECTION = "HTTP_CONNECTION".freeze
196
+ HTTP_EXPECT = "HTTP_EXPECT".freeze
197
+ CONTINUE = "100-continue".freeze
222
198
 
199
+ HTTP_11_100 = "HTTP/1.1 100 Continue\r\n\r\n".freeze
223
200
  HTTP_11_200 = "HTTP/1.1 200 OK\r\n".freeze
224
201
  HTTP_10_200 = "HTTP/1.0 200 OK\r\n".freeze
225
202
 
@@ -229,6 +206,7 @@ module Puma
229
206
  CONTENT_LENGTH2 = "content-length".freeze
230
207
  CONTENT_LENGTH_S = "Content-Length: ".freeze
231
208
  TRANSFER_ENCODING = "transfer-encoding".freeze
209
+ TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING".freeze
232
210
 
233
211
  CONNECTION_CLOSE = "Connection: close\r\n".freeze
234
212
  CONNECTION_KEEP_ALIVE = "Connection: Keep-Alive\r\n".freeze
@@ -236,6 +214,8 @@ module Puma
236
214
  TRANSFER_ENCODING_CHUNKED = "Transfer-Encoding: chunked\r\n".freeze
237
215
  CLOSE_CHUNKED = "0\r\n\r\n".freeze
238
216
 
217
+ CHUNKED = "chunked".freeze
218
+
239
219
  COLON = ": ".freeze
240
220
 
241
221
  NEWLINE = "\n".freeze
@@ -243,5 +223,7 @@ module Puma
243
223
  HIJACK_P = "rack.hijack?".freeze
244
224
  HIJACK = "rack.hijack".freeze
245
225
  HIJACK_IO = "rack.hijack_io".freeze
226
+
227
+ EARLY_HINTS = "rack.early_hints".freeze
246
228
  end
247
229
  end
@@ -1,5 +1,5 @@
1
1
  require 'optparse'
2
- require 'puma'
2
+ require 'puma/state_file'
3
3
  require 'puma/const'
4
4
  require 'puma/detect'
5
5
  require 'puma/configuration'
@@ -9,7 +9,7 @@ require 'socket'
9
9
  module Puma
10
10
  class ControlCLI
11
11
 
12
- COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory}
12
+ COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats}
13
13
 
14
14
  def initialize(argv, stdout=STDOUT, stderr=STDERR)
15
15
  @state = nil
@@ -69,6 +69,7 @@ module Puma
69
69
  end
70
70
 
71
71
  opts.order!(argv) { |a| opts.terminate a }
72
+ opts.parse!
72
73
 
73
74
  @command = argv.shift
74
75
 
@@ -78,11 +79,12 @@ module Puma
78
79
  end
79
80
 
80
81
  if @config_file
81
- config = Puma::Configuration.from_file @config_file
82
- @state ||= config.options[:state]
83
- @control_url ||= config.options[:control_url]
82
+ config = Puma::Configuration.new({ config_files: [@config_file] }, {})
83
+ config.load
84
+ @state ||= config.options[:state]
85
+ @control_url ||= config.options[:control_url]
84
86
  @control_auth_token ||= config.options[:control_auth_token]
85
- @pidfile ||= config.options[:pidfile]
87
+ @pidfile ||= config.options[:pidfile]
86
88
  end
87
89
  end
88
90
 
@@ -97,6 +99,7 @@ module Puma
97
99
 
98
100
  rescue => e
99
101
  @stdout.puts e.message
102
+ @stdout.puts e.backtrace
100
103
  exit 1
101
104
  end
102
105
 
@@ -167,7 +170,7 @@ module Puma
167
170
  end
168
171
 
169
172
  message "Command #{@command} sent success"
170
- message response.last if @command == "stats"
173
+ message response.last if @command == "stats" || @command == "gc-stats"
171
174
  end
172
175
 
173
176
  @server.close
@@ -179,46 +182,45 @@ module Puma
179
182
  end
180
183
 
181
184
  begin
182
- Process.getpgid @pid
183
- rescue SystemCallError
184
- if @command == "restart"
185
- start
186
- else
187
- raise "No pid '#{@pid}' found"
188
- end
189
- end
190
185
 
191
- case @command
192
- when "restart"
193
- Process.kill "SIGUSR2", @pid
186
+ case @command
187
+ when "restart"
188
+ Process.kill "SIGUSR2", @pid
194
189
 
195
- when "halt"
196
- Process.kill "QUIT", @pid
190
+ when "halt"
191
+ Process.kill "QUIT", @pid
197
192
 
198
- when "stop"
199
- Process.kill "SIGTERM", @pid
193
+ when "stop"
194
+ Process.kill "SIGTERM", @pid
200
195
 
201
- when "stats"
202
- puts "Stats not available via pid only"
203
- return
196
+ when "stats"
197
+ puts "Stats not available via pid only"
198
+ return
204
199
 
205
- when "reload-worker-directory"
206
- puts "reload-worker-directory not available via pid only"
207
- return
200
+ when "reload-worker-directory"
201
+ puts "reload-worker-directory not available via pid only"
202
+ return
208
203
 
209
- when "phased-restart"
210
- Process.kill "SIGUSR1", @pid
204
+ when "phased-restart"
205
+ Process.kill "SIGUSR1", @pid
211
206
 
212
- else
213
- message "Puma is started"
214
- return
207
+ else
208
+ return
209
+ end
210
+
211
+ rescue SystemCallError
212
+ if @command == "restart"
213
+ start
214
+ else
215
+ raise "No pid '#{@pid}' found"
216
+ end
215
217
  end
216
218
 
217
219
  message "Command #{@command} sent success"
218
220
  end
219
221
 
220
222
  def run
221
- start if @command == "start"
223
+ return start if @command == "start"
222
224
 
223
225
  prepare_configuration
224
226
 
@@ -230,6 +232,7 @@ module Puma
230
232
 
231
233
  rescue => e
232
234
  message e.message
235
+ message e.backtrace
233
236
  exit 1
234
237
  end
235
238
 
@@ -242,7 +245,7 @@ module Puma
242
245
  run_args += ["-S", @state] if @state
243
246
  run_args += ["-q"] if @quiet
244
247
  run_args += ["--pidfile", @pidfile] if @pidfile
245
- run_args += ["--control", @control_url] if @control_url
248
+ run_args += ["--control-url", @control_url] if @control_url
246
249
  run_args += ["--control-token", @control_auth_token] if @control_auth_token
247
250
  run_args += ["-C", @config_file] if @config_file
248
251
 
@@ -3,12 +3,12 @@ require 'puma/configuration'
3
3
 
4
4
  module Puma
5
5
  def self.run(opts={})
6
- cfg = Puma::Configuration.new do |c|
6
+ cfg = Puma::Configuration.new do |user_config|
7
7
  if port = opts[:port]
8
- c.port port
8
+ user_config.port port
9
9
  end
10
10
 
11
- c.quiet
11
+ user_config.quiet
12
12
 
13
13
  yield c
14
14
  end
@@ -5,7 +5,9 @@ module Puma
5
5
  IS_JRUBY
6
6
  end
7
7
 
8
+ IS_WINDOWS = RUBY_PLATFORM =~ /mswin|ming|cygwin/
9
+
8
10
  def self.windows?
9
- RUBY_PLATFORM =~ /mswin32|ming32/
11
+ IS_WINDOWS
10
12
  end
11
13
  end
@@ -1,20 +1,35 @@
1
1
  module Puma
2
2
  # The methods that are available for use inside the config file.
3
+ # These same methods are used in Puma cli and the rack handler
4
+ # internally.
3
5
  #
6
+ # Used manually (via CLI class):
7
+ #
8
+ # config = Configuration.new({}) do |user_config|
9
+ # user_config.port 3001
10
+ # end
11
+ # config.load
12
+ #
13
+ # puts config.options[:binds]
14
+ # "tcp://127.0.0.1:3001"
15
+ #
16
+ # Used to load file:
17
+ #
18
+ # $ cat puma_config.rb
19
+ # port 3002
20
+ #
21
+ # config = Configuration.new(config_file: "puma_config.rb")
22
+ # config.load
23
+ #
24
+ # puts config.options[:binds]
25
+ # # => "tcp://127.0.0.1:3002"
26
+ #
27
+ # Detailed docs can be found in `examples/config.rb`
4
28
  class DSL
5
29
  include ConfigDefault
6
30
 
7
- def self.load(options, cfg, path)
8
- d = new(options, cfg)
9
- d._load_from(path)
10
-
11
- options
12
- ensure
13
- d._offer_plugins
14
- end
15
-
16
31
  def initialize(options, config)
17
- @config = config
32
+ @config = config
18
33
  @options = options
19
34
 
20
35
  @plugins = []
@@ -40,36 +55,10 @@ module Puma
40
55
  @plugins.clear
41
56
  end
42
57
 
43
- def _run(&blk)
44
- blk.call self
45
- ensure
46
- _offer_plugins
47
- end
48
-
49
58
  def inject(&blk)
50
59
  instance_eval(&blk)
51
60
  end
52
61
 
53
- # Load configuration from another named file. If the file name is absolute,
54
- # load the file as an absolute path. Otherwise load it relative to the
55
- # current config file.
56
- #
57
- def import(file)
58
- if File.extname(file) == ""
59
- file += ".rb"
60
- end
61
-
62
- if file[0,1] == "/"
63
- path = file
64
- elsif @path
65
- path = File.join File.dirname(@path), file
66
- else
67
- raise "No original configuration path to import relative to"
68
- end
69
-
70
- DSL.new(@options, @config)._load_from(path)
71
- end
72
-
73
62
  def get(key,default=nil)
74
63
  @options[key.to_sym] || default
75
64
  end
@@ -115,15 +104,35 @@ module Puma
115
104
  end
116
105
 
117
106
  # Load additional configuration from a file
107
+ # Files get loaded later via Configuration#load
118
108
  def load(file)
119
- _ary(:config_files) << file
109
+ @options[:config_files] ||= []
110
+ @options[:config_files] << file
120
111
  end
121
112
 
122
- # Bind the server to +url+. tcp:// and unix:// are the only accepted
123
- # protocols.
113
+ # Adds a binding for the server to +url+. tcp://, unix://, and ssl:// are the only accepted
114
+ # protocols. Use query parameters within the url to specify options.
115
+ #
116
+ # @note multiple urls can be bound to, calling `bind` does not overwrite previous bindings.
124
117
  #
118
+ # @example Explicitly the socket backlog depth (default is 1024)
119
+ # bind('unix:///var/run/puma.sock?backlog=2048')
120
+ #
121
+ # @example Set up ssl cert
122
+ # bind('ssl://127.0.0.1:9292?key=key.key&cert=cert.pem')
123
+ #
124
+ # @example Prefer low-latency over higher throughput (via `Socket::TCP_NODELAY`)
125
+ # bind('tcp://0.0.0.0:9292?low_latency=true')
126
+ #
127
+ # @example Set socket permissions
128
+ # bind('unix:///var/run/puma.sock?umask=0111')
125
129
  def bind(url)
126
- _ary(:binds) << url
130
+ @options[:binds] ||= []
131
+ @options[:binds] << url
132
+ end
133
+
134
+ def clear_binds!
135
+ @options[:binds] = []
127
136
  end
128
137
 
129
138
  # Define the TCP port to bind to. Use +bind+ for more advanced options.
@@ -137,7 +146,13 @@ module Puma
137
146
  # them
138
147
  #
139
148
  def persistent_timeout(seconds)
140
- @options[:persistent_timeout] = seconds
149
+ @options[:persistent_timeout] = Integer(seconds)
150
+ end
151
+
152
+ # Define how long the tcp socket stays open, if no data has been received
153
+ #
154
+ def first_data_timeout(seconds)
155
+ @options[:first_data_timeout] = Integer(seconds)
141
156
  end
142
157
 
143
158
  # Work around leaky apps that leave garbage in Thread locals
@@ -154,7 +169,7 @@ module Puma
154
169
  end
155
170
 
156
171
  # When shutting down, drain the accept socket of pending
157
- # connections and proces them. This loops over the accept
172
+ # connections and process them. This loops over the accept
158
173
  # socket until there are no more read events and then stops
159
174
  # looking and waits for the requests to finish.
160
175
  def drain_on_shutdown(which=true)
@@ -192,7 +207,8 @@ module Puma
192
207
  # This can be called multiple times to add code each time.
193
208
  #
194
209
  def on_restart(&block)
195
- _ary(:on_restart) << block
210
+ @options[:on_restart] ||= []
211
+ @options[:on_restart] << block
196
212
  end
197
213
 
198
214
  # Command to use to restart puma. This should be just how to
@@ -238,6 +254,10 @@ module Puma
238
254
  @options[:mode] = :tcp
239
255
  end
240
256
 
257
+ def early_hints(answer=true)
258
+ @options[:early_hints] = answer
259
+ end
260
+
241
261
  # Redirect STDOUT and STDERR to files specified.
242
262
  def stdout_redirect(stdout=nil, stderr=nil, append=false)
243
263
  @options[:redirect_stdout] = stdout
@@ -264,11 +284,13 @@ module Puma
264
284
  end
265
285
 
266
286
  def ssl_bind(host, port, opts)
287
+ verify = opts.fetch(:verify_mode, 'none')
288
+
267
289
  if defined?(JRUBY_VERSION)
268
290
  keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
269
- bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{opts[:verify_mode] || 'peer'}"
291
+ bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}"
270
292
  else
271
- bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&verify_mode=#{opts[:verify_mode] || 'peer'}"
293
+ bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&verify_mode=#{verify}"
272
294
  end
273
295
  end
274
296
 
@@ -295,7 +317,8 @@ module Puma
295
317
  # This can be called multiple times to add hooks.
296
318
  #
297
319
  def before_fork(&block)
298
- _ary(:before_fork) << block
320
+ @options[:before_fork] ||= []
321
+ @options[:before_fork] << block
299
322
  end
300
323
 
301
324
  # *Cluster mode only* Code to run in a worker when it boots to setup
@@ -304,7 +327,8 @@ module Puma
304
327
  # This can be called multiple times to add hooks.
305
328
  #
306
329
  def on_worker_boot(&block)
307
- _ary(:before_worker_boot) << block
330
+ @options[:before_worker_boot] ||= []
331
+ @options[:before_worker_boot] << block
308
332
  end
309
333
 
310
334
  # *Cluster mode only* Code to run immediately before a worker shuts
@@ -315,7 +339,8 @@ module Puma
315
339
  # This can be called multiple times to add hooks.
316
340
  #
317
341
  def on_worker_shutdown(&block)
318
- _ary(:before_worker_shutdown) << block
342
+ @options[:before_worker_shutdown] ||= []
343
+ @options[:before_worker_shutdown] << block
319
344
  end
320
345
 
321
346
  # *Cluster mode only* Code to run in the master when it is
@@ -324,7 +349,8 @@ module Puma
324
349
  # This can be called multiple times to add hooks.
325
350
  #
326
351
  def on_worker_fork(&block)
327
- _ary(:before_worker_fork) << block
352
+ @options[:before_worker_fork] ||= []
353
+ @options[:before_worker_fork] << block
328
354
  end
329
355
 
330
356
  # *Cluster mode only* Code to run in the master after it starts
@@ -333,7 +359,8 @@ module Puma
333
359
  # This can be called multiple times to add hooks.
334
360
  #
335
361
  def after_worker_fork(&block)
336
- _ary(:after_worker_fork) << block
362
+ @options[:after_worker_fork] ||= []
363
+ @options[:after_worker_fork] = block
337
364
  end
338
365
 
339
366
  alias_method :after_worker_boot, :after_worker_fork
@@ -397,17 +424,17 @@ module Puma
397
424
  # that have not checked in within the given +timeout+.
398
425
  # This mitigates hung processes. Default value is 60 seconds.
399
426
  def worker_timeout(timeout)
400
- @options[:worker_timeout] = timeout
427
+ @options[:worker_timeout] = Integer(timeout)
401
428
  end
402
429
 
403
430
  # *Cluster mode only* Set the timeout for workers to boot
404
431
  def worker_boot_timeout(timeout)
405
- @options[:worker_boot_timeout] = timeout
432
+ @options[:worker_boot_timeout] = Integer(timeout)
406
433
  end
407
434
 
408
435
  # *Cluster mode only* Set the timeout for worker shutdown
409
436
  def worker_shutdown_timeout(timeout)
410
- @options[:worker_shutdown_timeout] = timeout
437
+ @options[:worker_shutdown_timeout] = Integer(timeout)
411
438
  end
412
439
 
413
440
  # When set to true (the default), workers accept all requests
@@ -475,10 +502,5 @@ module Puma
475
502
  end
476
503
  end
477
504
 
478
- private
479
-
480
- def _ary(key)
481
- (@options.cur[key] ||= [])
482
- end
483
505
  end
484
506
  end