puma 3.7.1 → 4.1.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 (74) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +229 -1
  3. data/README.md +179 -212
  4. data/docs/architecture.md +37 -0
  5. data/{DEPLOYMENT.md → 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 +28 -0
  10. data/docs/restart.md +41 -0
  11. data/docs/signals.md +56 -3
  12. data/docs/systemd.md +130 -37
  13. data/ext/puma_http11/PumaHttp11Service.java +2 -0
  14. data/ext/puma_http11/extconf.rb +8 -0
  15. data/ext/puma_http11/http11_parser.c +84 -84
  16. data/ext/puma_http11/http11_parser.rl +9 -9
  17. data/ext/puma_http11/mini_ssl.c +105 -9
  18. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
  19. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  20. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +30 -6
  21. data/lib/puma.rb +10 -0
  22. data/lib/puma/accept_nonblock.rb +2 -0
  23. data/lib/puma/app/status.rb +13 -0
  24. data/lib/puma/binder.rb +33 -18
  25. data/lib/puma/cli.rb +48 -33
  26. data/lib/puma/client.rb +94 -22
  27. data/lib/puma/cluster.rb +69 -21
  28. data/lib/puma/commonlogger.rb +2 -0
  29. data/lib/puma/configuration.rb +134 -136
  30. data/lib/puma/const.rb +16 -2
  31. data/lib/puma/control_cli.rb +31 -18
  32. data/lib/puma/convenient.rb +5 -3
  33. data/lib/puma/daemon_ext.rb +2 -0
  34. data/lib/puma/delegation.rb +2 -0
  35. data/lib/puma/detect.rb +2 -0
  36. data/lib/puma/dsl.rb +349 -113
  37. data/lib/puma/events.rb +8 -4
  38. data/lib/puma/io_buffer.rb +3 -6
  39. data/lib/puma/jruby_restart.rb +2 -1
  40. data/lib/puma/launcher.rb +60 -36
  41. data/lib/puma/minissl.rb +85 -28
  42. data/lib/puma/null_io.rb +2 -0
  43. data/lib/puma/plugin.rb +2 -0
  44. data/lib/puma/plugin/tmp_restart.rb +3 -2
  45. data/lib/puma/rack/builder.rb +4 -1
  46. data/lib/puma/rack/urlmap.rb +2 -0
  47. data/lib/puma/rack_default.rb +2 -0
  48. data/lib/puma/reactor.rb +218 -30
  49. data/lib/puma/runner.rb +18 -4
  50. data/lib/puma/server.rb +149 -56
  51. data/lib/puma/single.rb +16 -5
  52. data/lib/puma/state_file.rb +2 -0
  53. data/lib/puma/tcp_logger.rb +2 -0
  54. data/lib/puma/thread_pool.rb +59 -6
  55. data/lib/puma/util.rb +2 -6
  56. data/lib/rack/handler/puma.rb +58 -19
  57. data/tools/jungle/README.md +12 -2
  58. data/tools/jungle/init.d/README.md +2 -0
  59. data/tools/jungle/init.d/puma +8 -8
  60. data/tools/jungle/init.d/run-puma +1 -1
  61. data/tools/jungle/rc.d/README.md +74 -0
  62. data/tools/jungle/rc.d/puma +61 -0
  63. data/tools/jungle/rc.d/puma.conf +10 -0
  64. data/tools/trickletest.rb +1 -1
  65. metadata +25 -85
  66. data/.github/issue_template.md +0 -20
  67. data/Gemfile +0 -12
  68. data/Manifest.txt +0 -77
  69. data/Rakefile +0 -158
  70. data/gemfiles/2.1-Gemfile +0 -12
  71. data/lib/puma/compat.rb +0 -14
  72. data/lib/puma/java_io_buffer.rb +0 -45
  73. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  74. data/puma.gemspec +0 -52
data/lib/puma/const.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  #encoding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  module Puma
3
5
  class UnsupportedOption < RuntimeError
4
6
  end
@@ -53,6 +55,8 @@ module Puma
53
55
  415 => 'Unsupported Media Type',
54
56
  416 => 'Range Not Satisfiable',
55
57
  417 => 'Expectation Failed',
58
+ 418 => 'I\'m A Teapot',
59
+ 421 => 'Misdirected Request',
56
60
  422 => 'Unprocessable Entity',
57
61
  423 => 'Locked',
58
62
  424 => 'Failed Dependency',
@@ -60,6 +64,7 @@ module Puma
60
64
  428 => 'Precondition Required',
61
65
  429 => 'Too Many Requests',
62
66
  431 => 'Request Header Fields Too Large',
67
+ 451 => 'Unavailable For Legal Reasons',
63
68
  500 => 'Internal Server Error',
64
69
  501 => 'Not Implemented',
65
70
  502 => 'Bad Gateway',
@@ -95,8 +100,8 @@ module Puma
95
100
  # too taxing on performance.
96
101
  module Const
97
102
 
98
- PUMA_VERSION = VERSION = "3.7.1".freeze
99
- CODE_NAME = "Snowy Sagebrush".freeze
103
+ PUMA_VERSION = VERSION = "4.1.0".freeze
104
+ CODE_NAME = "Fourth and One".freeze
100
105
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
101
106
 
102
107
  FAST_TRACK_KA_TIMEOUT = 0.2
@@ -155,6 +160,9 @@ module Puma
155
160
  LINE_END = "\r\n".freeze
156
161
  REMOTE_ADDR = "REMOTE_ADDR".freeze
157
162
  HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR".freeze
163
+ HTTP_X_FORWARDED_SSL = "HTTP_X_FORWARDED_SSL".freeze
164
+ HTTP_X_FORWARDED_SCHEME = "HTTP_X_FORWARDED_SCHEME".freeze
165
+ HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO".freeze
158
166
 
159
167
  SERVER_NAME = "SERVER_NAME".freeze
160
168
  SERVER_PORT = "SERVER_PORT".freeze
@@ -220,5 +228,11 @@ module Puma
220
228
  HIJACK_P = "rack.hijack?".freeze
221
229
  HIJACK = "rack.hijack".freeze
222
230
  HIJACK_IO = "rack.hijack_io".freeze
231
+
232
+ EARLY_HINTS = "rack.early_hints".freeze
233
+
234
+ # Mininum interval to checks worker health
235
+ WORKER_CHECK_INTERVAL = 5
236
+
223
237
  end
224
238
  end
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
- require 'puma/state_file'
3
- require 'puma/const'
4
- require 'puma/detect'
5
- require 'puma/configuration'
4
+ require_relative 'state_file'
5
+ require_relative 'const'
6
+ require_relative 'detect'
7
+ require_relative 'configuration'
6
8
  require 'uri'
7
9
  require 'socket'
8
10
 
9
11
  module Puma
10
12
  class ControlCLI
11
13
 
12
- COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory}
14
+ COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats}
13
15
 
14
16
  def initialize(argv, stdout=STDOUT, stderr=STDERR)
15
17
  @state = nil
@@ -69,6 +71,7 @@ module Puma
69
71
  end
70
72
 
71
73
  opts.order!(argv) { |a| opts.terminate a }
74
+ opts.parse!
72
75
 
73
76
  @command = argv.shift
74
77
 
@@ -78,11 +81,12 @@ module Puma
78
81
  end
79
82
 
80
83
  if @config_file
81
- config = Puma::Configuration.from_file @config_file
82
- @state ||= config.options[:state]
83
- @control_url ||= config.options[:control_url]
84
+ config = Puma::Configuration.new({ config_files: [@config_file] }, {})
85
+ config.load
86
+ @state ||= config.options[:state]
87
+ @control_url ||= config.options[:control_url]
84
88
  @control_auth_token ||= config.options[:control_auth_token]
85
- @pidfile ||= config.options[:pidfile]
89
+ @pidfile ||= config.options[:pidfile]
86
90
  end
87
91
  end
88
92
 
@@ -126,7 +130,7 @@ module Puma
126
130
  uri = URI.parse @control_url
127
131
 
128
132
  # create server object by scheme
129
- @server = case uri.scheme
133
+ server = case uri.scheme
130
134
  when "tcp"
131
135
  TCPSocket.new uri.host, uri.port
132
136
  when "unix"
@@ -144,9 +148,9 @@ module Puma
144
148
  url = url + "?token=#{@control_auth_token}"
145
149
  end
146
150
 
147
- @server << "GET #{url} HTTP/1.0\r\n\r\n"
151
+ server << "GET #{url} HTTP/1.0\r\n\r\n"
148
152
 
149
- unless data = @server.read
153
+ unless data = server.read
150
154
  raise "Server closed connection before responding"
151
155
  end
152
156
 
@@ -167,10 +171,10 @@ module Puma
167
171
  end
168
172
 
169
173
  message "Command #{@command} sent success"
170
- message response.last if @command == "stats"
174
+ message response.last if @command == "stats" || @command == "gc-stats"
171
175
  end
172
-
173
- @server.close
176
+ ensure
177
+ server.close if server && !server.closed?
174
178
  end
175
179
 
176
180
  def send_signal
@@ -201,8 +205,17 @@ module Puma
201
205
  when "phased-restart"
202
206
  Process.kill "SIGUSR1", @pid
203
207
 
208
+ when "status"
209
+ begin
210
+ Process.kill 0, @pid
211
+ puts "Puma is started"
212
+ rescue Errno::ESRCH
213
+ raise "Puma is not running"
214
+ end
215
+
216
+ return
217
+
204
218
  else
205
- message "Puma is started"
206
219
  return
207
220
  end
208
221
 
@@ -218,7 +231,7 @@ module Puma
218
231
  end
219
232
 
220
233
  def run
221
- start if @command == "start"
234
+ return start if @command == "start"
222
235
 
223
236
  prepare_configuration
224
237
 
@@ -242,7 +255,7 @@ module Puma
242
255
  run_args += ["-S", @state] if @state
243
256
  run_args += ["-q"] if @quiet
244
257
  run_args += ["--pidfile", @pidfile] if @pidfile
245
- run_args += ["--control", @control_url] if @control_url
258
+ run_args += ["--control-url", @control_url] if @control_url
246
259
  run_args += ["--control-token", @control_auth_token] if @control_auth_token
247
260
  run_args += ["-C", @config_file] if @config_file
248
261
 
@@ -1,14 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/launcher'
2
4
  require 'puma/configuration'
3
5
 
4
6
  module Puma
5
7
  def self.run(opts={})
6
- cfg = Puma::Configuration.new do |c|
8
+ cfg = Puma::Configuration.new do |user_config|
7
9
  if port = opts[:port]
8
- c.port port
10
+ user_config.port port
9
11
  end
10
12
 
11
- c.quiet
13
+ user_config.quiet
12
14
 
13
15
  yield c
14
16
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Process
2
4
 
3
5
  # This overrides the default version because it is broken if it
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  module Delegation
3
5
  def forward(what, who)
data/lib/puma/detect.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  IS_JRUBY = defined?(JRUBY_VERSION)
3
5
 
data/lib/puma/dsl.rb CHANGED
@@ -1,20 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma/const'
4
+
1
5
  module Puma
2
- # The methods that are available for use inside the config file.
6
+ # The methods that are available for use inside the configuration file.
7
+ # These same methods are used in Puma cli and the rack handler
8
+ # internally.
9
+ #
10
+ # Used manually (via CLI class):
11
+ #
12
+ # config = Configuration.new({}) do |user_config|
13
+ # user_config.port 3001
14
+ # end
15
+ # config.load
16
+ #
17
+ # puts config.options[:binds]
18
+ # "tcp://127.0.0.1:3001"
3
19
  #
20
+ # Used to load file:
21
+ #
22
+ # $ cat puma_config.rb
23
+ # port 3002
24
+ #
25
+ # config = Configuration.new(config_file: "puma_config.rb")
26
+ # config.load
27
+ #
28
+ # puts config.options[:binds]
29
+ # # => "tcp://127.0.0.1:3002"
30
+ #
31
+ # You can also find many examples being used by the test suite in
32
+ # +test/config+.
4
33
  class DSL
5
34
  include ConfigDefault
6
35
 
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
36
  def initialize(options, config)
17
- @config = config
37
+ @config = config
18
38
  @options = options
19
39
 
20
40
  @plugins = []
@@ -40,34 +60,16 @@ module Puma
40
60
  @plugins.clear
41
61
  end
42
62
 
43
- def _run(&blk)
44
- blk.call self
45
- ensure
46
- _offer_plugins
63
+ def set_default_host(host)
64
+ @options[:default_host] = host
47
65
  end
48
66
 
49
- def inject(&blk)
50
- instance_eval(&blk)
67
+ def default_host
68
+ @options[:default_host] || Configuration::DefaultTCPHost
51
69
  end
52
70
 
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
+ def inject(&blk)
72
+ instance_eval(&blk)
71
73
  end
72
74
 
73
75
  def get(key,default=nil)
@@ -80,9 +82,22 @@ module Puma
80
82
  @plugins << @config.load_plugin(name)
81
83
  end
82
84
 
83
- # Use +obj+ or +block+ as the Rack app. This allows a config file to
84
- # 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!'
85
91
  #
92
+ # [
93
+ # 200,
94
+ # {
95
+ # 'Content-Type' => 'text/plain',
96
+ # 'Content-Length' => body.length.to_s
97
+ # },
98
+ # [body]
99
+ # ]
100
+ # end
86
101
  def app(obj=nil, &block)
87
102
  obj ||= block
88
103
 
@@ -91,9 +106,20 @@ module Puma
91
106
  @options[:app] = obj
92
107
  end
93
108
 
94
- # Start the Puma control rack app on +url+. This app can be communicated
95
- # 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.
96
114
  #
115
+ # Check out {Puma::App::Status} to see what the app has available.
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 }
97
123
  def activate_control_app(url="auto", opts={})
98
124
  if url == "auto"
99
125
  path = Configuration.temp_path
@@ -104,7 +130,12 @@ module Puma
104
130
  end
105
131
 
106
132
  if opts[:no_token]
107
- 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'
108
139
  else
109
140
  auth_token = opts[:auth_token]
110
141
  auth_token ||= Configuration.random_token
@@ -115,53 +146,99 @@ module Puma
115
146
  end
116
147
 
117
148
  # Load additional configuration from a file
149
+ # Files get loaded later via Configuration#load
118
150
  def load(file)
119
- _ary(:config_files) << file
151
+ @options[:config_files] ||= []
152
+ @options[:config_files] << file
120
153
  end
121
154
 
122
- # Bind the server to +url+. tcp:// and unix:// are the only accepted
123
- # protocols.
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.
158
+ #
159
+ # The default is "tcp://0.0.0.0:9292".
124
160
  #
161
+ # You can use query parameters within the url to specify options:
162
+ #
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+.
169
+ #
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'
125
178
  def bind(url)
126
- _ary(:binds) << url
179
+ @options[:binds] ||= []
180
+ @options[:binds] << url
181
+ end
182
+
183
+ def clear_binds!
184
+ @options[:binds] = []
127
185
  end
128
186
 
129
187
  # Define the TCP port to bind to. Use +bind+ for more advanced options.
130
188
  #
189
+ # @example
190
+ # port 9292
131
191
  def port(port, host=nil)
132
- host ||= Configuration::DefaultTCPHost
192
+ host ||= default_host
133
193
  bind "tcp://#{host}:#{port}"
134
194
  end
135
195
 
136
- # Define how long persistent connections can be idle before puma closes
137
- # them
138
- #
196
+ # Define how long persistent connections can be idle before Puma closes
197
+ # them.
139
198
  def persistent_timeout(seconds)
140
- @options[:persistent_timeout] = seconds
199
+ @options[:persistent_timeout] = Integer(seconds)
200
+ end
201
+
202
+ # Define how long the tcp socket stays open, if no data has been received.
203
+ def first_data_timeout(seconds)
204
+ @options[:first_data_timeout] = Integer(seconds)
141
205
  end
142
206
 
143
207
  # Work around leaky apps that leave garbage in Thread locals
144
- # across requests
145
- #
208
+ # across requests.
146
209
  def clean_thread_locals(which=true)
147
210
  @options[:clean_thread_locals] = which
148
211
  end
149
212
 
150
- # Daemonize the server into the background. Highly suggest that
151
- # 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
152
223
  def daemonize(which=true)
153
224
  @options[:daemon] = which
154
225
  end
155
226
 
156
227
  # When shutting down, drain the accept socket of pending
157
- # connections and proces them. This loops over the accept
228
+ # connections and process them. This loops over the accept
158
229
  # socket until there are no more read events and then stops
159
230
  # looking and waits for the requests to finish.
160
231
  def drain_on_shutdown(which=true)
161
232
  @options[:drain_on_shutdown] = which
162
233
  end
163
234
 
164
- # 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'
165
242
  def environment(environment)
166
243
  @options[:environment] = environment
167
244
  end
@@ -187,29 +264,41 @@ module Puma
187
264
  end
188
265
 
189
266
  # Code to run before doing a restart. This code should
190
- # close logfiles, database connections, etc.
267
+ # close log files, database connections, etc.
191
268
  #
192
269
  # This can be called multiple times to add code each time.
193
270
  #
271
+ # @example
272
+ # on_restart do
273
+ # puts 'On restart...'
274
+ # end
194
275
  def on_restart(&block)
195
- _ary(:on_restart) << block
276
+ @options[:on_restart] ||= []
277
+ @options[:on_restart] << block
196
278
  end
197
279
 
198
- # Command to use to restart puma. This should be just how to
199
- # load puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments
200
- # 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.
201
283
  #
284
+ # @example
285
+ # restart_command '/u/app/lolcat/bin/restart_puma'
202
286
  def restart_command(cmd)
203
287
  @options[:restart_cmd] = cmd.to_s
204
288
  end
205
289
 
206
- # 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'
207
294
  def pidfile(path)
208
295
  @options[:pidfile] = path.to_s
209
296
  end
210
297
 
211
- # Disable request logging.
298
+ # Disable request logging, if this isn't used it'll be enabled by default.
212
299
  #
300
+ # @example
301
+ # quiet
213
302
  def quiet(which=true)
214
303
  @options[:log_requests] = !which
215
304
  end
@@ -228,6 +317,10 @@ module Puma
228
317
 
229
318
  # Load +path+ as a rackup file.
230
319
  #
320
+ # The default is "config.ru".
321
+ #
322
+ # @example
323
+ # rackup '/u/apps/lolcat/config.ru'
231
324
  def rackup(path)
232
325
  @options[:rackup] = path.to_s
233
326
  end
@@ -238,16 +331,36 @@ module Puma
238
331
  @options[:mode] = :tcp
239
332
  end
240
333
 
241
- # Redirect STDOUT and STDERR to files specified.
334
+ def early_hints(answer=true)
335
+ @options[:early_hints] = answer
336
+ end
337
+
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
242
345
  def stdout_redirect(stdout=nil, stderr=nil, append=false)
243
346
  @options[:redirect_stdout] = stdout
244
347
  @options[:redirect_stderr] = stderr
245
348
  @options[:redirect_append] = append
246
349
  end
247
350
 
351
+ def log_formatter(&block)
352
+ @options[:log_formatter] = block
353
+ end
354
+
248
355
  # Configure +min+ to be the minimum number of threads to use to answer
249
356
  # requests and +max+ the maximum.
250
357
  #
358
+ # The default is "0, 16".
359
+ #
360
+ # @example
361
+ # threads 0, 16
362
+ # @example
363
+ # threads 5, 5
251
364
  def threads(min, max)
252
365
  min = Integer(min)
253
366
  max = Integer(max)
@@ -263,84 +376,162 @@ module Puma
263
376
  @options[:max_threads] = max
264
377
  end
265
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
+ # }
266
398
  def ssl_bind(host, port, opts)
267
399
  verify = opts.fetch(:verify_mode, 'none')
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)
268
403
 
269
404
  if defined?(JRUBY_VERSION)
270
405
  keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
271
- 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}"
272
407
  else
273
- 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}"
274
410
  end
275
411
  end
276
412
 
277
413
  # Use +path+ as the file to store the server info state. This is
278
- # used by pumactl to query and control the server.
414
+ # used by +pumactl+ to query and control the server.
279
415
  #
416
+ # @example
417
+ # state_path '/u/apps/lolcat/tmp/pids/puma.state'
280
418
  def state_path(path)
281
419
  @options[:state] = path.to_s
282
420
  end
283
421
 
284
- # *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.
285
424
  #
425
+ # The default is 0.
426
+ #
427
+ # @note Cluster mode only.
286
428
  def workers(count)
287
429
  @options[:workers] = count.to_i
288
430
  end
289
431
 
290
- # *Cluster mode only* Code to run immediately before master process
432
+ # Code to run immediately before master process
291
433
  # forks workers (once on boot). These hooks can block if necessary
292
- # to wait for background operations unknown to puma to finish before
434
+ # to wait for background operations unknown to Puma to finish before
293
435
  # the process terminates.
294
- # This can be used to close any connections to remote servers (database, redis, ...)
295
- # 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.
296
438
  #
297
- # This can be called multiple times to add hooks.
439
+ # This can be called multiple times to add several hooks.
298
440
  #
441
+ # @note Cluster mode only.
442
+ # @example
443
+ # before_fork do
444
+ # puts "Starting workers..."
445
+ # end
299
446
  def before_fork(&block)
300
- _ary(:before_fork) << block
447
+ @options[:before_fork] ||= []
448
+ @options[:before_fork] << block
301
449
  end
302
450
 
303
- # *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
304
452
  # the process before booting the app.
305
453
  #
306
- # This can be called multiple times to add hooks.
454
+ # This can be called multiple times to add several hooks.
307
455
  #
456
+ # @note Cluster mode only.
457
+ # @example
458
+ # on_worker_fork do
459
+ # puts 'Before worker fork...'
460
+ # end
308
461
  def on_worker_boot(&block)
309
- _ary(:before_worker_boot) << block
462
+ @options[:before_worker_boot] ||= []
463
+ @options[:before_worker_boot] << block
310
464
  end
311
465
 
312
- # *Cluster mode only* Code to run immediately before a worker shuts
466
+ # Code to run immediately before a worker shuts
313
467
  # down (after it has finished processing HTTP requests). These hooks
314
468
  # can block if necessary to wait for background operations unknown
315
- # to puma to finish before the process terminates.
469
+ # to Puma to finish before the process terminates.
316
470
  #
317
- # This can be called multiple times to add hooks.
471
+ # This can be called multiple times to add several hooks.
318
472
  #
473
+ # @note Cluster mode only.
474
+ # @example
475
+ # on_worker_shutdown do
476
+ # puts 'On worker shutdown...'
477
+ # end
319
478
  def on_worker_shutdown(&block)
320
- _ary(:before_worker_shutdown) << block
479
+ @options[:before_worker_shutdown] ||= []
480
+ @options[:before_worker_shutdown] << block
321
481
  end
322
482
 
323
- # *Cluster mode only* Code to run in the master when it is
324
- # 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.
325
485
  #
326
- # This can be called multiple times to add hooks.
486
+ # This can be called multiple times to add several hooks.
327
487
  #
488
+ # @note Cluster mode only.
489
+ # @example
490
+ # on_worker_fork do
491
+ # puts 'Before worker fork...'
492
+ # end
328
493
  def on_worker_fork(&block)
329
- _ary(:before_worker_fork) << block
494
+ @options[:before_worker_fork] ||= []
495
+ @options[:before_worker_fork] << block
330
496
  end
331
497
 
332
- # *Cluster mode only* Code to run in the master after it starts
333
- # 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.
334
500
  #
335
- # This can be called multiple times to add hooks.
501
+ # This is called everytime a worker is to be started.
336
502
  #
503
+ # @note Cluster mode only.
504
+ # @example
505
+ # after_worker_fork do
506
+ # puts 'After worker fork...'
507
+ # end
337
508
  def after_worker_fork(&block)
338
- _ary(:after_worker_fork) << block
509
+ @options[:after_worker_fork] ||= []
510
+ @options[:after_worker_fork] = block
339
511
  end
340
512
 
341
513
  alias_method :after_worker_boot, :after_worker_fork
342
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
+
343
529
  # The directory to operate out of.
530
+ #
531
+ # The default is the current directory.
532
+ #
533
+ # @example
534
+ # directory '/u/apps/lolcat'
344
535
  def directory(dir)
345
536
  @options[:directory] = dir.to_s
346
537
  end
@@ -351,22 +542,28 @@ module Puma
351
542
  directory dir
352
543
  end
353
544
 
354
- # 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.
355
546
  def tcp_mode
356
547
  @options[:mode] = :tcp
357
548
  end
358
549
 
359
- # *Cluster mode only* Preload the application before starting
360
- # the workers and setting up the listen ports. This conflicts
361
- # 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.
362
552
  #
553
+ # @note Cluster mode only.
554
+ # @example
555
+ # preload_app!
363
556
  def preload_app!(answer=true)
364
557
  @options[:preload_app] = answer
365
558
  end
366
559
 
367
- # Use +obj+ or +block+ as the low level error handler. This allows a config file to
368
- # 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.
369
562
  #
563
+ # @example
564
+ # lowlevel_error_handler do |err|
565
+ # [200, {}, ["error page"]]
566
+ # end
370
567
  def lowlevel_error_handler(obj=nil, &block)
371
568
  obj ||= block
372
569
  raise "Provide either a #call'able or a block" unless obj
@@ -376,40 +573,84 @@ module Puma
376
573
  # This option is used to allow your app and its gems to be
377
574
  # properly reloaded when not using preload.
378
575
  #
379
- # 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
380
577
  # context of Bundler, it will cleanup the environment and
381
578
  # re-run itself outside the Bundler environment, but directly
382
579
  # using the files that Bundler has setup.
383
580
  #
384
- # This means that puma is now decoupled from your Bundler
581
+ # This means that Puma is now decoupled from your Bundler
385
582
  # context and when each worker loads, it will be loading a
386
583
  # new Bundler context and thus can float around as the release
387
584
  # dictates.
585
+ #
586
+ # @note This is incompatible with +preload_app!+.
388
587
  def prune_bundler(answer=true)
389
588
  @options[:prune_bundler] = answer
390
589
  end
391
590
 
392
- # Additional text to display in process listing
591
+ # By default, Puma will raise SignalException when SIGTERM is received. In
592
+ # environments where SIGTERM is something expected, you can suppress these
593
+ # with this option.
594
+ #
595
+ # This can be useful for example in Kubernetes, where rolling restart is
596
+ # guaranteed usually on infrastructure level.
597
+ #
598
+ # @example
599
+ # raise_exception_on_sigterm false
600
+ def raise_exception_on_sigterm(answer=true)
601
+ @options[:raise_exception_on_sigterm] = answer
602
+ end
603
+
604
+ # Additional text to display in process listing.
605
+ #
606
+ # If you do not specify a tag, Puma will infer it. If you do not want Puma
607
+ # to add a tag, use an empty string.
608
+ #
609
+ # @example
610
+ # tag 'app name'
611
+ # @example
612
+ # tag ''
393
613
  def tag(string)
394
614
  @options[:tag] = string.to_s
395
615
  end
396
616
 
397
- # *Cluster mode only* Set the timeout for workers in seconds
398
- # When set the master process will terminate any workers
399
- # that have not checked in within the given +timeout+.
400
- # This mitigates hung processes. Default value is 60 seconds.
617
+ # Verifies that all workers have checked in to the master process within
618
+ # the given timeout. If not the worker process will be restarted. This is
619
+ # not a request timeout, it is to protect against a hung or dead process.
620
+ # Setting this value will not protect against slow requests.
621
+ #
622
+ # The minimum value is 6 seconds, the default value is 60 seconds.
623
+ #
624
+ # @note Cluster mode only.
625
+ # @example
626
+ # worker_timeout 60
401
627
  def worker_timeout(timeout)
628
+ timeout = Integer(timeout)
629
+ min = Const::WORKER_CHECK_INTERVAL
630
+
631
+ if timeout <= min
632
+ raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
633
+ end
634
+
402
635
  @options[:worker_timeout] = timeout
403
636
  end
404
637
 
405
- # *Cluster mode only* Set the timeout for workers to boot
638
+ # Change the default worker timeout for booting.
639
+ #
640
+ # If unspecified, this defaults to the value of worker_timeout.
641
+ #
642
+ # @note Cluster mode only.
643
+ # @example:
644
+ # worker_boot_timeout 60
406
645
  def worker_boot_timeout(timeout)
407
- @options[:worker_boot_timeout] = timeout
646
+ @options[:worker_boot_timeout] = Integer(timeout)
408
647
  end
409
648
 
410
- # *Cluster mode only* Set the timeout for worker shutdown
649
+ # Set the timeout for worker shutdown
650
+ #
651
+ # @note Cluster mode only.
411
652
  def worker_shutdown_timeout(timeout)
412
- @options[:worker_shutdown_timeout] = timeout
653
+ @options[:worker_shutdown_timeout] = Integer(timeout)
413
654
  end
414
655
 
415
656
  # When set to true (the default), workers accept all requests
@@ -424,7 +665,7 @@ module Puma
424
665
  # Note that setting this to false disables HTTP keepalive and
425
666
  # slow clients will occupy a handler thread while the request
426
667
  # is being sent. A reverse proxy, such as nginx, can handle
427
- # slow clients and queue requests before they reach puma.
668
+ # slow clients and queue requests before they reach Puma.
428
669
  def queue_requests(answer=true)
429
670
  @options[:queue_requests] = answer
430
671
  end
@@ -453,7 +694,7 @@ module Puma
453
694
  # is used, allowing headers such as X-Forwarded-For
454
695
  # to be used as well.
455
696
  # * Any string - this allows you to hardcode remote address to any value
456
- # you wish. Because puma never uses this field anyway, it's
697
+ # you wish. Because Puma never uses this field anyway, it's
457
698
  # format is entirely in your hands.
458
699
  def set_remote_address(val=:socket)
459
700
  case val
@@ -468,7 +709,7 @@ module Puma
468
709
  when Hash
469
710
  if hdr = val[:header]
470
711
  @options[:remote_address] = :header
471
- @options[:remote_address_header] = "HTTP_" + hdr.upcase.gsub("-", "_")
712
+ @options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
472
713
  else
473
714
  raise "Invalid value for set_remote_address - #{val.inspect}"
474
715
  end
@@ -477,10 +718,5 @@ module Puma
477
718
  end
478
719
  end
479
720
 
480
- private
481
-
482
- def _ary(key)
483
- (@options.cur[key] ||= [])
484
- end
485
721
  end
486
722
  end