puma 3.12.4 → 4.3.3

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +122 -2
  3. data/README.md +76 -48
  4. data/docs/architecture.md +1 -0
  5. data/docs/deployment.md +24 -4
  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 +20 -10
  10. data/docs/restart.md +4 -2
  11. data/docs/systemd.md +27 -9
  12. data/docs/tcp_mode.md +96 -0
  13. data/ext/puma_http11/PumaHttp11Service.java +2 -0
  14. data/ext/puma_http11/extconf.rb +13 -0
  15. data/ext/puma_http11/http11_parser.c +37 -62
  16. data/ext/puma_http11/http11_parser.java.rl +21 -37
  17. data/ext/puma_http11/http11_parser_common.rl +3 -3
  18. data/ext/puma_http11/mini_ssl.c +78 -8
  19. data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
  20. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +91 -106
  21. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  22. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -4
  23. data/ext/puma_http11/puma_http11.c +2 -0
  24. data/lib/puma.rb +8 -0
  25. data/lib/puma/accept_nonblock.rb +7 -1
  26. data/lib/puma/app/status.rb +35 -29
  27. data/lib/puma/binder.rb +38 -60
  28. data/lib/puma/cli.rb +4 -0
  29. data/lib/puma/client.rb +221 -199
  30. data/lib/puma/cluster.rb +53 -30
  31. data/lib/puma/configuration.rb +4 -3
  32. data/lib/puma/const.rb +22 -18
  33. data/lib/puma/control_cli.rb +30 -5
  34. data/lib/puma/dsl.rb +299 -75
  35. data/lib/puma/events.rb +4 -1
  36. data/lib/puma/io_buffer.rb +1 -6
  37. data/lib/puma/launcher.rb +95 -53
  38. data/lib/puma/minissl.rb +35 -17
  39. data/lib/puma/minissl/context_builder.rb +76 -0
  40. data/lib/puma/plugin.rb +5 -2
  41. data/lib/puma/plugin/tmp_restart.rb +2 -0
  42. data/lib/puma/rack/builder.rb +2 -0
  43. data/lib/puma/rack/urlmap.rb +2 -0
  44. data/lib/puma/rack_default.rb +2 -0
  45. data/lib/puma/reactor.rb +110 -57
  46. data/lib/puma/runner.rb +11 -3
  47. data/lib/puma/server.rb +59 -48
  48. data/lib/puma/single.rb +3 -3
  49. data/lib/puma/thread_pool.rb +15 -33
  50. data/lib/puma/util.rb +1 -6
  51. data/lib/rack/handler/puma.rb +3 -3
  52. data/tools/docker/Dockerfile +16 -0
  53. data/tools/jungle/init.d/puma +6 -6
  54. data/tools/trickletest.rb +0 -1
  55. metadata +25 -12
  56. data/lib/puma/compat.rb +0 -14
  57. data/lib/puma/convenient.rb +0 -25
  58. data/lib/puma/daemon_ext.rb +0 -33
  59. data/lib/puma/delegation.rb +0 -13
  60. data/lib/puma/java_io_buffer.rb +0 -47
  61. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
@@ -19,8 +19,6 @@ module Puma
19
19
  # via the `spawn_workers` method call. Each worker will have it's own
20
20
  # instance of a `Puma::Server`.
21
21
  class Cluster < Runner
22
- WORKER_CHECK_INTERVAL = 5
23
-
24
22
  def initialize(cli, events)
25
23
  super cli, events
26
24
 
@@ -37,7 +35,11 @@ module Puma
37
35
  @workers.each { |x| x.term }
38
36
 
39
37
  begin
40
- @workers.each { |w| Process.waitpid(w.pid) }
38
+ loop do
39
+ wait_workers
40
+ break if @workers.empty?
41
+ sleep 0.2
42
+ end
41
43
  rescue Interrupt
42
44
  log "! Cancelled waiting for workers"
43
45
  end
@@ -69,12 +71,13 @@ module Puma
69
71
  @signal = "TERM"
70
72
  @options = options
71
73
  @first_term_sent = nil
74
+ @started_at = Time.now
72
75
  @last_checkin = Time.now
73
76
  @last_status = '{}'
74
- @dead = false
77
+ @term = false
75
78
  end
76
79
 
77
- attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status
80
+ attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
78
81
 
79
82
  def booted?
80
83
  @stage == :booted
@@ -85,12 +88,8 @@ module Puma
85
88
  @stage = :booted
86
89
  end
87
90
 
88
- def dead?
89
- @dead
90
- end
91
-
92
- def dead!
93
- @dead = true
91
+ def term?
92
+ @term
94
93
  end
95
94
 
96
95
  def ping!(status)
@@ -107,9 +106,9 @@ module Puma
107
106
  if @first_term_sent && (Time.now - @first_term_sent) > @options[:worker_shutdown_timeout]
108
107
  @signal = "KILL"
109
108
  else
109
+ @term ||= true
110
110
  @first_term_sent ||= Time.now
111
111
  end
112
-
113
112
  Process.kill @signal, @pid
114
113
  rescue Errno::ESRCH
115
114
  end
@@ -183,7 +182,7 @@ module Puma
183
182
  def check_workers(force=false)
184
183
  return if !force && @next_check && @next_check >= Time.now
185
184
 
186
- @next_check = Time.now + WORKER_CHECK_INTERVAL
185
+ @next_check = Time.now + Const::WORKER_CHECK_INTERVAL
187
186
 
188
187
  any = false
189
188
 
@@ -200,15 +199,7 @@ module Puma
200
199
  # during this loop by giving the kernel time to kill them.
201
200
  sleep 1 if any
202
201
 
203
- while @workers.any?
204
- pid = Process.waitpid(-1, Process::WNOHANG)
205
- break unless pid
206
-
207
- @workers.delete_if { |w| w.pid == pid }
208
- end
209
-
210
- @workers.delete_if(&:dead?)
211
-
202
+ wait_workers
212
203
  cull_workers
213
204
  spawn_workers
214
205
 
@@ -225,8 +216,10 @@ module Puma
225
216
  log "- Stopping #{w.pid} for phased upgrade..."
226
217
  end
227
218
 
228
- w.term
229
- log "- #{w.signal} sent to #{w.pid}..."
219
+ unless w.term?
220
+ w.term
221
+ log "- #{w.signal} sent to #{w.pid}..."
222
+ end
230
223
  end
231
224
  end
232
225
  end
@@ -253,6 +246,7 @@ module Puma
253
246
  @suicide_pipe.close
254
247
 
255
248
  Thread.new do
249
+ Puma.set_thread_name "worker check pipe"
256
250
  IO.select [@check_pipe]
257
251
  log "! Detected parent died, dying"
258
252
  exit! 1
@@ -275,6 +269,7 @@ module Puma
275
269
  server = start_server
276
270
 
277
271
  Signal.trap "SIGTERM" do
272
+ @worker_write << "e#{Process.pid}\n" rescue nil
278
273
  server.stop
279
274
  end
280
275
 
@@ -287,10 +282,11 @@ module Puma
287
282
  end
288
283
 
289
284
  Thread.new(@worker_write) do |io|
285
+ Puma.set_thread_name "stat payload"
290
286
  base_payload = "p#{Process.pid}"
291
287
 
292
288
  while true
293
- sleep WORKER_CHECK_INTERVAL
289
+ sleep Const::WORKER_CHECK_INTERVAL
294
290
  begin
295
291
  b = server.backlog || 0
296
292
  r = server.running || 0
@@ -352,11 +348,13 @@ module Puma
352
348
  Dir.chdir dir
353
349
  end
354
350
 
351
+ # Inside of a child process, this will return all zeroes, as @workers is only populated in
352
+ # the master process.
355
353
  def stats
356
354
  old_worker_count = @workers.count { |w| w.phase != @phase }
357
355
  booted_worker_count = @workers.count { |w| w.booted? }
358
- 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(",") + ']'
359
- %Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
356
+ worker_status = '[' + @workers.map { |w| %Q!{ "started_at": "#{w.started_at.utc.iso8601}", "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(",") + ']'
357
+ %Q!{ "started_at": "#{@started_at.utc.iso8601}", "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
360
358
  end
361
359
 
362
360
  def preload?
@@ -390,10 +388,13 @@ module Puma
390
388
  log "Early termination of worker"
391
389
  exit! 0
392
390
  else
391
+ @launcher.close_binder_listeners
392
+
393
393
  stop_workers
394
394
  stop
395
395
 
396
- raise SignalException, "SIGTERM"
396
+ raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
397
+ exit 0 # Clean exit, workers were stopped
397
398
  end
398
399
  end
399
400
  end
@@ -487,7 +488,7 @@ module Puma
487
488
 
488
489
  force_check = false
489
490
 
490
- res = IO.select([read], nil, nil, WORKER_CHECK_INTERVAL)
491
+ res = IO.select([read], nil, nil, Const::WORKER_CHECK_INTERVAL)
491
492
 
492
493
  if res
493
494
  req = read.read_nonblock(1)
@@ -503,8 +504,11 @@ module Puma
503
504
  w.boot!
504
505
  log "- Worker #{w.index} (pid: #{pid}) booted, phase: #{w.phase}"
505
506
  force_check = true
507
+ when "e"
508
+ # external term, see worker method, Signal.trap "SIGTERM"
509
+ w.instance_variable_set :@term, true
506
510
  when "t"
507
- w.dead!
511
+ w.term unless w.term?
508
512
  force_check = true
509
513
  when "p"
510
514
  w.ping!(result.sub(/^\d+/,'').chomp)
@@ -527,5 +531,24 @@ module Puma
527
531
  @wakeup.close
528
532
  end
529
533
  end
534
+
535
+ private
536
+
537
+ # loops thru @workers, removing workers that exited, and calling
538
+ # `#term` if needed
539
+ def wait_workers
540
+ @workers.reject! do |w|
541
+ begin
542
+ if Process.wait(w.pid, Process::WNOHANG)
543
+ true
544
+ else
545
+ w.term if w.term?
546
+ nil
547
+ end
548
+ rescue Errno::ECHILD
549
+ true # child is already terminated
550
+ end
551
+ end
552
+ end
530
553
  end
531
554
  end
@@ -20,7 +20,7 @@ module Puma
20
20
  # In this class any "user" specified options take precedence over any
21
21
  # "file" specified options, take precedence over any "default" options.
22
22
  #
23
- # User input is prefered over "defaults":
23
+ # User input is preferred over "defaults":
24
24
  # user_options = { foo: "bar" }
25
25
  # default_options = { foo: "zoo" }
26
26
  # options = UserFileDefaultOptions.new(user_options, default_options)
@@ -32,7 +32,7 @@ module Puma
32
32
  # puts options.all_of(:foo)
33
33
  # # => ["bar", "zoo"]
34
34
  #
35
- # A "file" option can be set. This config will be prefered over "default" options
35
+ # A "file" option can be set. This config will be preferred over "default" options
36
36
  # but will defer to any available "user" specified options.
37
37
  #
38
38
  # user_options = { foo: "bar" }
@@ -186,7 +186,8 @@ module Puma
186
186
  :rackup => DefaultRackup,
187
187
  :logger => STDOUT,
188
188
  :persistent_timeout => Const::PERSISTENT_TIMEOUT,
189
- :first_data_timeout => Const::FIRST_DATA_TIMEOUT
189
+ :first_data_timeout => Const::FIRST_DATA_TIMEOUT,
190
+ :raise_exception_on_sigterm => true
190
191
  }
191
192
  end
192
193
 
@@ -100,8 +100,8 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "3.12.4".freeze
104
- CODE_NAME = "Llamas in Pajamas".freeze
103
+ PUMA_VERSION = VERSION = "4.3.3".freeze
104
+ CODE_NAME = "Mysterious Traveller".freeze
105
105
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
106
106
 
107
107
  FAST_TRACK_KA_TIMEOUT = 0.2
@@ -129,27 +129,24 @@ module Puma
129
129
  REQUEST_URI= 'REQUEST_URI'.freeze
130
130
  REQUEST_PATH = 'REQUEST_PATH'.freeze
131
131
  QUERY_STRING = 'QUERY_STRING'.freeze
132
+ CONTENT_LENGTH = "CONTENT_LENGTH".freeze
132
133
 
133
134
  PATH_INFO = 'PATH_INFO'.freeze
134
135
 
135
136
  PUMA_TMP_BASE = "puma".freeze
136
137
 
137
- # Indicate that we couldn't parse the request
138
- ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n".freeze
139
-
140
- # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
141
- ERROR_404_RESPONSE = "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND".freeze
142
-
143
- # The standard empty 408 response for requests that timed out.
144
- ERROR_408_RESPONSE = "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze
145
-
146
- CONTENT_LENGTH = "CONTENT_LENGTH".freeze
147
-
148
- # Indicate that there was an internal error, obviously.
149
- ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze
150
-
151
- # A common header for indicating the server is too busy. Not used yet.
152
- ERROR_503_RESPONSE = "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
138
+ ERROR_RESPONSE = {
139
+ # Indicate that we couldn't parse the request
140
+ 400 => "HTTP/1.1 400 Bad Request\r\n\r\n".freeze,
141
+ # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
142
+ 404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND".freeze,
143
+ # The standard empty 408 response for requests that timed out.
144
+ 408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze,
145
+ # Indicate that there was an internal error, obviously.
146
+ 500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze,
147
+ # A common header for indicating the server is too busy. Not used yet.
148
+ 503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
149
+ }
153
150
 
154
151
  # The basic max request size we'll try to read.
155
152
  CHUNK_SIZE = 16 * 1024
@@ -167,6 +164,9 @@ module Puma
167
164
  LINE_END = "\r\n".freeze
168
165
  REMOTE_ADDR = "REMOTE_ADDR".freeze
169
166
  HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR".freeze
167
+ HTTP_X_FORWARDED_SSL = "HTTP_X_FORWARDED_SSL".freeze
168
+ HTTP_X_FORWARDED_SCHEME = "HTTP_X_FORWARDED_SCHEME".freeze
169
+ HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO".freeze
170
170
 
171
171
  SERVER_NAME = "SERVER_NAME".freeze
172
172
  SERVER_PORT = "SERVER_PORT".freeze
@@ -235,5 +235,9 @@ module Puma
235
235
  HIJACK_IO = "rack.hijack_io".freeze
236
236
 
237
237
  EARLY_HINTS = "rack.early_hints".freeze
238
+
239
+ # Mininum interval to checks worker health
240
+ WORKER_CHECK_INTERVAL = 5
241
+
238
242
  end
239
243
  end
@@ -22,6 +22,7 @@ module Puma
22
22
  @control_auth_token = nil
23
23
  @config_file = nil
24
24
  @command = nil
25
+ @environment = ENV['RACK_ENV']
25
26
 
26
27
  @argv = argv.dup
27
28
  @stdout = stdout
@@ -59,6 +60,11 @@ module Puma
59
60
  @config_file = arg
60
61
  end
61
62
 
63
+ o.on "-e", "--environment ENVIRONMENT",
64
+ "The environment to run the Rack app on (default development)" do |arg|
65
+ @environment = arg
66
+ end
67
+
62
68
  o.on_tail("-H", "--help", "Show this message") do
63
69
  @stdout.puts o
64
70
  exit
@@ -76,8 +82,12 @@ module Puma
76
82
  @command = argv.shift
77
83
 
78
84
  unless @config_file == '-'
79
- if @config_file.nil? and File.exist?('config/puma.rb')
80
- @config_file = 'config/puma.rb'
85
+ environment = @environment || 'development'
86
+
87
+ if @config_file.nil?
88
+ @config_file = %W(config/puma/#{environment}.rb config/puma.rb).find do |f|
89
+ File.exist?(f)
90
+ end
81
91
  end
82
92
 
83
93
  if @config_file
@@ -101,7 +111,6 @@ module Puma
101
111
 
102
112
  rescue => e
103
113
  @stdout.puts e.message
104
- @stdout.puts e.backtrace
105
114
  exit 1
106
115
  end
107
116
 
@@ -123,7 +132,7 @@ module Puma
123
132
  @pid = sf.pid
124
133
  elsif @pidfile
125
134
  # get pid from pid_file
126
- @pid = File.open(@pidfile).gets.to_i
135
+ File.open(@pidfile) { |f| @pid = f.read.to_i }
127
136
  end
128
137
  end
129
138
 
@@ -132,6 +141,12 @@ module Puma
132
141
 
133
142
  # create server object by scheme
134
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)
135
150
  when "tcp"
136
151
  TCPSocket.new uri.host, uri.port
137
152
  when "unix"
@@ -206,6 +221,16 @@ module Puma
206
221
  when "phased-restart"
207
222
  Process.kill "SIGUSR1", @pid
208
223
 
224
+ when "status"
225
+ begin
226
+ Process.kill 0, @pid
227
+ puts "Puma is started"
228
+ rescue Errno::ESRCH
229
+ raise "Puma is not running"
230
+ end
231
+
232
+ return
233
+
209
234
  else
210
235
  return
211
236
  end
@@ -234,7 +259,6 @@ module Puma
234
259
 
235
260
  rescue => e
236
261
  message e.message
237
- message e.backtrace
238
262
  exit 1
239
263
  end
240
264
 
@@ -250,6 +274,7 @@ module Puma
250
274
  run_args += ["--control-url", @control_url] if @control_url
251
275
  run_args += ["--control-token", @control_auth_token] if @control_auth_token
252
276
  run_args += ["-C", @config_file] if @config_file
277
+ run_args += ["-e", @environment] if @environment
253
278
 
254
279
  events = Puma::Events.new @stdout, @stderr
255
280
 
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'puma/const'
4
+
3
5
  module Puma
4
- # The methods that are available for use inside the config file.
6
+ # The methods that are available for use inside the configuration file.
5
7
  # These same methods are used in Puma cli and the rack handler
6
8
  # internally.
7
9
  #
@@ -26,7 +28,8 @@ module Puma
26
28
  # puts config.options[:binds]
27
29
  # # => "tcp://127.0.0.1:3002"
28
30
  #
29
- # Detailed docs can be found in `examples/config.rb`
31
+ # You can also find many examples being used by the test suite in
32
+ # +test/config+.
30
33
  class DSL
31
34
  include ConfigDefault
32
35
 
@@ -79,9 +82,22 @@ module Puma
79
82
  @plugins << @config.load_plugin(name)
80
83
  end
81
84
 
82
- # Use +obj+ or +block+ as the Rack app. This allows a config file to
83
- # be the app itself.
85
+ # Use an object or block as the rack application. This allows the
86
+ # configuration file to be the application itself.
87
+ #
88
+ # @example
89
+ # app do |env|
90
+ # body = 'Hello, World!'
84
91
  #
92
+ # [
93
+ # 200,
94
+ # {
95
+ # 'Content-Type' => 'text/plain',
96
+ # 'Content-Length' => body.length.to_s
97
+ # },
98
+ # [body]
99
+ # ]
100
+ # end
85
101
  def app(obj=nil, &block)
86
102
  obj ||= block
87
103
 
@@ -90,9 +106,20 @@ module Puma
90
106
  @options[:app] = obj
91
107
  end
92
108
 
93
- # Start the Puma control rack app on +url+. This app can be communicated
94
- # with to control the main server.
109
+ # Start the Puma control rack application on +url+. This application can
110
+ # be communicated with to control the main server. Additionally, you can
111
+ # provide an authentication token, so all requests to the control server
112
+ # will need to include that token as a query parameter. This allows for
113
+ # simple authentication.
114
+ #
115
+ # Check out {Puma::App::Status} to see what the app has available.
95
116
  #
117
+ # @example
118
+ # activate_control_app 'unix:///var/run/pumactl.sock'
119
+ # @example
120
+ # activate_control_app 'unix:///var/run/pumactl.sock', { auth_token: '12345' }
121
+ # @example
122
+ # activate_control_app 'unix:///var/run/pumactl.sock', { no_token: true }
96
123
  def activate_control_app(url="auto", opts={})
97
124
  if url == "auto"
98
125
  path = Configuration.temp_path
@@ -103,7 +130,12 @@ module Puma
103
130
  end
104
131
 
105
132
  if opts[:no_token]
106
- auth_token = :none
133
+ # We need to use 'none' rather than :none because this value will be
134
+ # passed on to an instance of OptionParser, which doesn't support
135
+ # symbols as option values.
136
+ #
137
+ # See: https://github.com/puma/puma/issues/1193#issuecomment-305995488
138
+ auth_token = 'none'
107
139
  else
108
140
  auth_token = opts[:auth_token]
109
141
  auth_token ||= Configuration.random_token
@@ -120,22 +152,29 @@ module Puma
120
152
  @options[:config_files] << file
121
153
  end
122
154
 
123
- # Adds a binding for the server to +url+. tcp://, unix://, and ssl:// are the only accepted
124
- # protocols. Use query parameters within the url to specify options.
155
+ # Bind the server to +url+. "tcp://", "unix://" and "ssl://" are the only
156
+ # accepted protocols. Multiple urls can be bound to, calling `bind` does
157
+ # not overwrite previous bindings.
125
158
  #
126
- # @note multiple urls can be bound to, calling `bind` does not overwrite previous bindings.
159
+ # The default is "tcp://0.0.0.0:9292".
127
160
  #
128
- # @example Explicitly the socket backlog depth (default is 1024)
129
- # bind('unix:///var/run/puma.sock?backlog=2048')
161
+ # You can use query parameters within the url to specify options:
130
162
  #
131
- # @example Set up ssl cert
132
- # bind('ssl://127.0.0.1:9292?key=key.key&cert=cert.pem')
163
+ # - Set the socket backlog depth with +backlog+, default is 1024.
164
+ # - Set up an SSL certificate with +key+ & +cert+.
165
+ # - Set whether to optimize for low latency instead of throughput with
166
+ # +low_latency+, default is to optimize for low latency. This is done
167
+ # via +Socket::TCP_NODELAY+.
168
+ # - Set socket permissions with +umask+.
133
169
  #
134
- # @example Prefer low-latency over higher throughput (via `Socket::TCP_NODELAY`)
135
- # bind('tcp://0.0.0.0:9292?low_latency=true')
136
- #
137
- # @example Set socket permissions
138
- # bind('unix:///var/run/puma.sock?umask=0111')
170
+ # @example Backlog depth
171
+ # bind 'unix:///var/run/puma.sock?backlog=512'
172
+ # @example SSL cert
173
+ # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem'
174
+ # @example Disable optimization for low latency
175
+ # bind 'tcp://0.0.0.0:9292?low_latency=false'
176
+ # @example Socket permissions
177
+ # bind 'unix:///var/run/puma.sock?umask=0111'
139
178
  def bind(url)
140
179
  @options[:binds] ||= []
141
180
  @options[:binds] << url
@@ -147,33 +186,40 @@ module Puma
147
186
 
148
187
  # Define the TCP port to bind to. Use +bind+ for more advanced options.
149
188
  #
189
+ # @example
190
+ # port 9292
150
191
  def port(port, host=nil)
151
192
  host ||= default_host
152
193
  bind "tcp://#{host}:#{port}"
153
194
  end
154
195
 
155
- # Define how long persistent connections can be idle before puma closes
156
- # them
157
- #
196
+ # Define how long persistent connections can be idle before Puma closes
197
+ # them.
158
198
  def persistent_timeout(seconds)
159
199
  @options[:persistent_timeout] = Integer(seconds)
160
200
  end
161
201
 
162
- # Define how long the tcp socket stays open, if no data has been received
163
- #
202
+ # Define how long the tcp socket stays open, if no data has been received.
164
203
  def first_data_timeout(seconds)
165
204
  @options[:first_data_timeout] = Integer(seconds)
166
205
  end
167
206
 
168
207
  # Work around leaky apps that leave garbage in Thread locals
169
- # across requests
170
- #
208
+ # across requests.
171
209
  def clean_thread_locals(which=true)
172
210
  @options[:clean_thread_locals] = which
173
211
  end
174
212
 
175
- # Daemonize the server into the background. Highly suggest that
176
- # this be combined with +pidfile+ and +stdout_redirect+.
213
+ # Daemonize the server into the background. It's highly recommended to
214
+ # use this in combination with +pidfile+ and +stdout_redirect+.
215
+ #
216
+ # The default is "false".
217
+ #
218
+ # @example
219
+ # daemonize
220
+ #
221
+ # @example
222
+ # daemonize false
177
223
  def daemonize(which=true)
178
224
  @options[:daemon] = which
179
225
  end
@@ -186,7 +232,13 @@ module Puma
186
232
  @options[:drain_on_shutdown] = which
187
233
  end
188
234
 
189
- # Set the environment in which the Rack's app will run.
235
+ # Set the environment in which the rack's app will run. The value must be
236
+ # a string.
237
+ #
238
+ # The default is "development".
239
+ #
240
+ # @example
241
+ # environment 'production'
190
242
  def environment(environment)
191
243
  @options[:environment] = environment
192
244
  end
@@ -212,30 +264,41 @@ module Puma
212
264
  end
213
265
 
214
266
  # Code to run before doing a restart. This code should
215
- # close logfiles, database connections, etc.
267
+ # close log files, database connections, etc.
216
268
  #
217
269
  # This can be called multiple times to add code each time.
218
270
  #
271
+ # @example
272
+ # on_restart do
273
+ # puts 'On restart...'
274
+ # end
219
275
  def on_restart(&block)
220
276
  @options[:on_restart] ||= []
221
277
  @options[:on_restart] << block
222
278
  end
223
279
 
224
- # Command to use to restart puma. This should be just how to
225
- # load puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments
226
- # to puma, as those are the same as the original process.
280
+ # Command to use to restart Puma. This should be just how to
281
+ # load Puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments
282
+ # to Puma, as those are the same as the original process.
227
283
  #
284
+ # @example
285
+ # restart_command '/u/app/lolcat/bin/restart_puma'
228
286
  def restart_command(cmd)
229
287
  @options[:restart_cmd] = cmd.to_s
230
288
  end
231
289
 
232
- # Store the pid of the server in the file at +path+.
290
+ # Store the pid of the server in the file at "path".
291
+ #
292
+ # @example
293
+ # pidfile '/u/apps/lolcat/tmp/pids/puma.pid'
233
294
  def pidfile(path)
234
295
  @options[:pidfile] = path.to_s
235
296
  end
236
297
 
237
- # Disable request logging.
298
+ # Disable request logging, if this isn't used it'll be enabled by default.
238
299
  #
300
+ # @example
301
+ # quiet
239
302
  def quiet(which=true)
240
303
  @options[:log_requests] = !which
241
304
  end
@@ -254,6 +317,10 @@ module Puma
254
317
 
255
318
  # Load +path+ as a rackup file.
256
319
  #
320
+ # The default is "config.ru".
321
+ #
322
+ # @example
323
+ # rackup '/u/apps/lolcat/config.ru'
257
324
  def rackup(path)
258
325
  @options[:rackup] = path.to_s
259
326
  end
@@ -268,16 +335,32 @@ module Puma
268
335
  @options[:early_hints] = answer
269
336
  end
270
337
 
271
- # Redirect STDOUT and STDERR to files specified.
338
+ # Redirect STDOUT and STDERR to files specified. The +append+ parameter
339
+ # specifies whether the output is appended, the default is +false+.
340
+ #
341
+ # @example
342
+ # stdout_redirect '/app/lolcat/log/stdout', '/app/lolcat/log/stderr'
343
+ # @example
344
+ # stdout_redirect '/app/lolcat/log/stdout', '/app/lolcat/log/stderr', true
272
345
  def stdout_redirect(stdout=nil, stderr=nil, append=false)
273
346
  @options[:redirect_stdout] = stdout
274
347
  @options[:redirect_stderr] = stderr
275
348
  @options[:redirect_append] = append
276
349
  end
277
350
 
351
+ def log_formatter(&block)
352
+ @options[:log_formatter] = block
353
+ end
354
+
278
355
  # Configure +min+ to be the minimum number of threads to use to answer
279
356
  # requests and +max+ the maximum.
280
357
  #
358
+ # The default is "0, 16".
359
+ #
360
+ # @example
361
+ # threads 0, 16
362
+ # @example
363
+ # threads 5, 5
281
364
  def threads(min, max)
282
365
  min = Integer(min)
283
366
  max = Integer(max)
@@ -293,81 +376,135 @@ module Puma
293
376
  @options[:max_threads] = max
294
377
  end
295
378
 
379
+ # Instead of "bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'" you
380
+ # can also use the "ssl_bind" option.
381
+ #
382
+ # @example
383
+ # ssl_bind '127.0.0.1', '9292', {
384
+ # cert: path_to_cert,
385
+ # key: path_to_key,
386
+ # ssl_cipher_filter: cipher_filter, # optional
387
+ # verify_mode: verify_mode, # default 'none'
388
+ # }
389
+ # @example For JRuby additional keys are required: keystore & keystore_pass.
390
+ # ssl_bind '127.0.0.1', '9292', {
391
+ # cert: path_to_cert,
392
+ # key: path_to_key,
393
+ # ssl_cipher_filter: cipher_filter, # optional
394
+ # verify_mode: verify_mode, # default 'none'
395
+ # keystore: path_to_keystore,
396
+ # keystore_pass: password
397
+ # }
296
398
  def ssl_bind(host, port, opts)
297
- verify = opts.fetch(:verify_mode, 'none')
399
+ verify = opts.fetch(:verify_mode, 'none').to_s
400
+ no_tlsv1 = opts.fetch(:no_tlsv1, 'false')
401
+ no_tlsv1_1 = opts.fetch(:no_tlsv1_1, 'false')
402
+ ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
298
403
 
299
404
  if defined?(JRUBY_VERSION)
300
405
  keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
301
- bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}"
406
+ bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
302
407
  else
303
- bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&verify_mode=#{verify}"
408
+ ssl_cipher_filter = "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" if opts[:ssl_cipher_filter]
409
+ bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}#{ssl_cipher_filter}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
304
410
  end
305
411
  end
306
412
 
307
413
  # Use +path+ as the file to store the server info state. This is
308
- # used by pumactl to query and control the server.
414
+ # used by +pumactl+ to query and control the server.
309
415
  #
416
+ # @example
417
+ # state_path '/u/apps/lolcat/tmp/pids/puma.state'
310
418
  def state_path(path)
311
419
  @options[:state] = path.to_s
312
420
  end
313
421
 
314
- # *Cluster mode only* How many worker processes to run.
422
+ # How many worker processes to run. Typically this is set to
423
+ # to the number of available cores.
315
424
  #
425
+ # The default is 0.
426
+ #
427
+ # @note Cluster mode only.
316
428
  def workers(count)
317
429
  @options[:workers] = count.to_i
318
430
  end
319
431
 
320
- # *Cluster mode only* Code to run immediately before master process
432
+ # Code to run immediately before master process
321
433
  # forks workers (once on boot). These hooks can block if necessary
322
- # to wait for background operations unknown to puma to finish before
434
+ # to wait for background operations unknown to Puma to finish before
323
435
  # the process terminates.
324
- # This can be used to close any connections to remote servers (database, redis, ...)
325
- # that were opened when preloading the code
436
+ # This can be used to close any connections to remote servers (database,
437
+ # Redis, ...) that were opened when preloading the code.
326
438
  #
327
- # This can be called multiple times to add hooks.
439
+ # This can be called multiple times to add several hooks.
328
440
  #
441
+ # @note Cluster mode only.
442
+ # @example
443
+ # before_fork do
444
+ # puts "Starting workers..."
445
+ # end
329
446
  def before_fork(&block)
330
447
  @options[:before_fork] ||= []
331
448
  @options[:before_fork] << block
332
449
  end
333
450
 
334
- # *Cluster mode only* Code to run in a worker when it boots to setup
451
+ # Code to run in a worker when it boots to setup
335
452
  # the process before booting the app.
336
453
  #
337
- # This can be called multiple times to add hooks.
454
+ # This can be called multiple times to add several hooks.
338
455
  #
456
+ # @note Cluster mode only.
457
+ # @example
458
+ # on_worker_fork do
459
+ # puts 'Before worker fork...'
460
+ # end
339
461
  def on_worker_boot(&block)
340
462
  @options[:before_worker_boot] ||= []
341
463
  @options[:before_worker_boot] << block
342
464
  end
343
465
 
344
- # *Cluster mode only* Code to run immediately before a worker shuts
466
+ # Code to run immediately before a worker shuts
345
467
  # down (after it has finished processing HTTP requests). These hooks
346
468
  # can block if necessary to wait for background operations unknown
347
- # to puma to finish before the process terminates.
469
+ # to Puma to finish before the process terminates.
348
470
  #
349
- # This can be called multiple times to add hooks.
471
+ # This can be called multiple times to add several hooks.
350
472
  #
473
+ # @note Cluster mode only.
474
+ # @example
475
+ # on_worker_shutdown do
476
+ # puts 'On worker shutdown...'
477
+ # end
351
478
  def on_worker_shutdown(&block)
352
479
  @options[:before_worker_shutdown] ||= []
353
480
  @options[:before_worker_shutdown] << block
354
481
  end
355
482
 
356
- # *Cluster mode only* Code to run in the master when it is
357
- # about to create the worker by forking itself.
483
+ # Code to run in the master right before a worker is started. The worker's
484
+ # index is passed as an argument.
358
485
  #
359
- # This can be called multiple times to add hooks.
486
+ # This can be called multiple times to add several hooks.
360
487
  #
488
+ # @note Cluster mode only.
489
+ # @example
490
+ # on_worker_fork do
491
+ # puts 'Before worker fork...'
492
+ # end
361
493
  def on_worker_fork(&block)
362
494
  @options[:before_worker_fork] ||= []
363
495
  @options[:before_worker_fork] << block
364
496
  end
365
497
 
366
- # *Cluster mode only* Code to run in the master after it starts
367
- # a worker.
498
+ # Code to run in the master after a worker has been started. The worker's
499
+ # index is passed as an argument.
368
500
  #
369
- # This can be called multiple times to add hooks.
501
+ # This is called everytime a worker is to be started.
370
502
  #
503
+ # @note Cluster mode only.
504
+ # @example
505
+ # after_worker_fork do
506
+ # puts 'After worker fork...'
507
+ # end
371
508
  def after_worker_fork(&block)
372
509
  @options[:after_worker_fork] ||= []
373
510
  @options[:after_worker_fork] = block
@@ -375,7 +512,26 @@ module Puma
375
512
 
376
513
  alias_method :after_worker_boot, :after_worker_fork
377
514
 
515
+ # Code to run out-of-band when the worker is idle.
516
+ # These hooks run immediately after a request has finished
517
+ # processing and there are no busy threads on the worker.
518
+ # The worker doesn't accept new requests until this code finishes.
519
+ #
520
+ # This hook is useful for running out-of-band garbage collection
521
+ # or scheduling asynchronous tasks to execute after a response.
522
+ #
523
+ # This can be called multiple times to add several hooks.
524
+ def out_of_band(&block)
525
+ @options[:out_of_band] ||= []
526
+ @options[:out_of_band] << block
527
+ end
528
+
378
529
  # The directory to operate out of.
530
+ #
531
+ # The default is the current directory.
532
+ #
533
+ # @example
534
+ # directory '/u/apps/lolcat'
379
535
  def directory(dir)
380
536
  @options[:directory] = dir.to_s
381
537
  end
@@ -386,22 +542,28 @@ module Puma
386
542
  directory dir
387
543
  end
388
544
 
389
- # Run the app as a raw TCP app instead of an HTTP rack app
545
+ # Run the app as a raw TCP app instead of an HTTP rack app.
390
546
  def tcp_mode
391
547
  @options[:mode] = :tcp
392
548
  end
393
549
 
394
- # *Cluster mode only* Preload the application before starting
395
- # the workers and setting up the listen ports. This conflicts
396
- # with using the phased restart feature, you can't use both.
550
+ # Preload the application before starting the workers; this conflicts with
551
+ # phased restart feature. This is off by default.
397
552
  #
553
+ # @note Cluster mode only.
554
+ # @example
555
+ # preload_app!
398
556
  def preload_app!(answer=true)
399
557
  @options[:preload_app] = answer
400
558
  end
401
559
 
402
- # Use +obj+ or +block+ as the low level error handler. This allows a config file to
403
- # change the default error on the server.
560
+ # Use +obj+ or +block+ as the low level error handler. This allows the
561
+ # configuration file to change the default error on the server.
404
562
  #
563
+ # @example
564
+ # lowlevel_error_handler do |err|
565
+ # [200, {}, ["error page"]]
566
+ # end
405
567
  def lowlevel_error_handler(obj=nil, &block)
406
568
  obj ||= block
407
569
  raise "Provide either a #call'able or a block" unless obj
@@ -411,38 +573,100 @@ module Puma
411
573
  # This option is used to allow your app and its gems to be
412
574
  # properly reloaded when not using preload.
413
575
  #
414
- # When set, if puma detects that it's been invoked in the
576
+ # When set, if Puma detects that it's been invoked in the
415
577
  # context of Bundler, it will cleanup the environment and
416
578
  # re-run itself outside the Bundler environment, but directly
417
579
  # using the files that Bundler has setup.
418
580
  #
419
- # This means that puma is now decoupled from your Bundler
581
+ # This means that Puma is now decoupled from your Bundler
420
582
  # context and when each worker loads, it will be loading a
421
583
  # new Bundler context and thus can float around as the release
422
584
  # dictates.
585
+ #
586
+ # See also: extra_runtime_dependencies
587
+ #
588
+ # @note This is incompatible with +preload_app!+.
589
+ # @note This is only supported for RubyGems 2.2+
423
590
  def prune_bundler(answer=true)
424
591
  @options[:prune_bundler] = answer
425
592
  end
426
593
 
427
- # Additional text to display in process listing
594
+ # By default, Puma will raise SignalException when SIGTERM is received. In
595
+ # environments where SIGTERM is something expected, you can suppress these
596
+ # with this option.
597
+ #
598
+ # This can be useful for example in Kubernetes, where rolling restart is
599
+ # guaranteed usually on infrastructure level.
600
+ #
601
+ # @example
602
+ # raise_exception_on_sigterm false
603
+ def raise_exception_on_sigterm(answer=true)
604
+ @options[:raise_exception_on_sigterm] = answer
605
+ end
606
+
607
+ # When using prune_bundler, if extra runtime dependencies need to be loaded to
608
+ # initialize your app, then this setting can be used. This includes any Puma plugins.
609
+ #
610
+ # Before bundler is pruned, the gem names supplied will be looked up in the bundler
611
+ # context and then loaded again after bundler is pruned.
612
+ # Only applies if prune_bundler is used.
613
+ #
614
+ # @example
615
+ # extra_runtime_dependencies ['gem_name_1', 'gem_name_2']
616
+ # @example
617
+ # extra_runtime_dependencies ['puma_worker_killer', 'puma-heroku']
618
+ def extra_runtime_dependencies(answer = [])
619
+ @options[:extra_runtime_dependencies] = Array(answer)
620
+ end
621
+
622
+ # Additional text to display in process listing.
623
+ #
624
+ # If you do not specify a tag, Puma will infer it. If you do not want Puma
625
+ # to add a tag, use an empty string.
626
+ #
627
+ # @example
628
+ # tag 'app name'
629
+ # @example
630
+ # tag ''
428
631
  def tag(string)
429
632
  @options[:tag] = string.to_s
430
633
  end
431
634
 
432
- # *Cluster mode only* Set the timeout for workers in seconds
433
- # When set the master process will terminate any workers
434
- # that have not checked in within the given +timeout+.
435
- # This mitigates hung processes. Default value is 60 seconds.
635
+ # Verifies that all workers have checked in to the master process within
636
+ # the given timeout. If not the worker process will be restarted. This is
637
+ # not a request timeout, it is to protect against a hung or dead process.
638
+ # Setting this value will not protect against slow requests.
639
+ #
640
+ # The minimum value is 6 seconds, the default value is 60 seconds.
641
+ #
642
+ # @note Cluster mode only.
643
+ # @example
644
+ # worker_timeout 60
436
645
  def worker_timeout(timeout)
437
- @options[:worker_timeout] = Integer(timeout)
646
+ timeout = Integer(timeout)
647
+ min = Const::WORKER_CHECK_INTERVAL
648
+
649
+ if timeout <= min
650
+ raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
651
+ end
652
+
653
+ @options[:worker_timeout] = timeout
438
654
  end
439
655
 
440
- # *Cluster mode only* Set the timeout for workers to boot
656
+ # Change the default worker timeout for booting.
657
+ #
658
+ # If unspecified, this defaults to the value of worker_timeout.
659
+ #
660
+ # @note Cluster mode only.
661
+ # @example:
662
+ # worker_boot_timeout 60
441
663
  def worker_boot_timeout(timeout)
442
664
  @options[:worker_boot_timeout] = Integer(timeout)
443
665
  end
444
666
 
445
- # *Cluster mode only* Set the timeout for worker shutdown
667
+ # Set the timeout for worker shutdown
668
+ #
669
+ # @note Cluster mode only.
446
670
  def worker_shutdown_timeout(timeout)
447
671
  @options[:worker_shutdown_timeout] = Integer(timeout)
448
672
  end
@@ -459,7 +683,7 @@ module Puma
459
683
  # Note that setting this to false disables HTTP keepalive and
460
684
  # slow clients will occupy a handler thread while the request
461
685
  # is being sent. A reverse proxy, such as nginx, can handle
462
- # slow clients and queue requests before they reach puma.
686
+ # slow clients and queue requests before they reach Puma.
463
687
  def queue_requests(answer=true)
464
688
  @options[:queue_requests] = answer
465
689
  end
@@ -488,7 +712,7 @@ module Puma
488
712
  # is used, allowing headers such as X-Forwarded-For
489
713
  # to be used as well.
490
714
  # * Any string - this allows you to hardcode remote address to any value
491
- # you wish. Because puma never uses this field anyway, it's
715
+ # you wish. Because Puma never uses this field anyway, it's
492
716
  # format is entirely in your hands.
493
717
  def set_remote_address(val=:socket)
494
718
  case val