puma 2.16.0 → 3.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +5 -5
  2. data/{History.txt → History.md} +489 -70
  3. data/README.md +143 -174
  4. data/docs/architecture.md +36 -0
  5. data/{DEPLOYMENT.md → docs/deployment.md} +1 -1
  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/nginx.md +2 -2
  10. data/docs/plugins.md +28 -0
  11. data/docs/restart.md +39 -0
  12. data/docs/signals.md +56 -3
  13. data/docs/systemd.md +272 -0
  14. data/ext/puma_http11/extconf.rb +2 -0
  15. data/ext/puma_http11/http11_parser.c +291 -447
  16. data/ext/puma_http11/http11_parser.h +1 -0
  17. data/ext/puma_http11/http11_parser.java.rl +5 -5
  18. data/ext/puma_http11/http11_parser.rl +10 -9
  19. data/ext/puma_http11/http11_parser_common.rl +1 -1
  20. data/ext/puma_http11/io_buffer.c +8 -8
  21. data/ext/puma_http11/mini_ssl.c +64 -6
  22. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +113 -131
  23. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +9 -2
  24. data/ext/puma_http11/puma_http11.c +1 -0
  25. data/lib/puma/app/status.rb +9 -1
  26. data/lib/puma/binder.rb +90 -38
  27. data/lib/puma/cli.rb +134 -491
  28. data/lib/puma/client.rb +142 -4
  29. data/lib/puma/cluster.rb +132 -76
  30. data/lib/puma/commonlogger.rb +19 -20
  31. data/lib/puma/compat.rb +3 -7
  32. data/lib/puma/configuration.rb +206 -67
  33. data/lib/puma/const.rb +21 -31
  34. data/lib/puma/control_cli.rb +92 -103
  35. data/lib/puma/convenient.rb +23 -0
  36. data/lib/puma/daemon_ext.rb +6 -0
  37. data/lib/puma/detect.rb +10 -1
  38. data/lib/puma/dsl.rb +203 -45
  39. data/lib/puma/events.rb +22 -13
  40. data/lib/puma/io_buffer.rb +1 -1
  41. data/lib/puma/jruby_restart.rb +1 -2
  42. data/lib/puma/launcher.rb +431 -0
  43. data/lib/puma/minissl.rb +83 -4
  44. data/lib/puma/null_io.rb +19 -11
  45. data/lib/puma/plugin/tmp_restart.rb +34 -0
  46. data/lib/puma/plugin.rb +115 -0
  47. data/lib/puma/rack/backports/uri/common_193.rb +17 -13
  48. data/lib/puma/rack/builder.rb +3 -0
  49. data/lib/puma/rack/urlmap.rb +9 -8
  50. data/lib/puma/reactor.rb +18 -0
  51. data/lib/puma/runner.rb +43 -15
  52. data/lib/puma/server.rb +141 -35
  53. data/lib/puma/single.rb +16 -6
  54. data/lib/puma/state_file.rb +29 -0
  55. data/lib/puma/tcp_logger.rb +8 -1
  56. data/lib/puma/thread_pool.rb +60 -10
  57. data/lib/puma/util.rb +1 -5
  58. data/lib/puma.rb +13 -4
  59. data/lib/rack/handler/puma.rb +76 -29
  60. data/tools/jungle/README.md +12 -2
  61. data/tools/jungle/init.d/README.md +9 -2
  62. data/tools/jungle/init.d/puma +86 -59
  63. data/tools/jungle/init.d/run-puma +16 -1
  64. data/tools/jungle/rc.d/README.md +74 -0
  65. data/tools/jungle/rc.d/puma +61 -0
  66. data/tools/jungle/rc.d/puma.conf +10 -0
  67. data/tools/jungle/upstart/puma.conf +1 -1
  68. data/tools/trickletest.rb +1 -1
  69. metadata +28 -95
  70. data/COPYING +0 -55
  71. data/Gemfile +0 -13
  72. data/Manifest.txt +0 -74
  73. data/Rakefile +0 -158
  74. data/docs/config.md +0 -0
  75. data/lib/puma/capistrano.rb +0 -94
  76. data/lib/puma/rack/backports/uri/common_18.rb +0 -56
  77. data/lib/puma/rack/backports/uri/common_192.rb +0 -52
  78. data/puma.gemspec +0 -52
data/lib/puma/dsl.rb CHANGED
@@ -1,23 +1,72 @@
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, path)
8
- new(options).tap do |obj|
9
- obj._load_from(path)
31
+ def initialize(options, config)
32
+ @config = config
33
+ @options = options
34
+
35
+ @plugins = []
36
+ end
37
+
38
+ def _load_from(path)
39
+ if path
40
+ @path = path
41
+ instance_eval(File.read(path), path, 1)
42
+ end
43
+ ensure
44
+ _offer_plugins
45
+ end
46
+
47
+ def _offer_plugins
48
+ @plugins.each do |o|
49
+ if o.respond_to? :config
50
+ @options.shift
51
+ o.config self
52
+ end
10
53
  end
11
54
 
12
- options
55
+ @plugins.clear
13
56
  end
14
57
 
15
- def initialize(options)
16
- @options = options
58
+ def inject(&blk)
59
+ instance_eval(&blk)
17
60
  end
18
61
 
19
- def _load_from(path)
20
- instance_eval(File.read(path), path, 1) if path
62
+ def get(key,default=nil)
63
+ @options[key.to_sym] || default
64
+ end
65
+
66
+ # Load the named plugin for use by this configuration
67
+ #
68
+ def plugin(name)
69
+ @plugins << @config.load_plugin(name)
21
70
  end
22
71
 
23
72
  # Use +obj+ or +block+ as the Rack app. This allows a config file to
@@ -34,29 +83,76 @@ module Puma
34
83
  # Start the Puma control rack app on +url+. This app can be communicated
35
84
  # with to control the main server.
36
85
  #
37
- def activate_control_app(url="auto", opts=nil)
38
- @options[:control_url] = url
86
+ def activate_control_app(url="auto", opts={})
87
+ if url == "auto"
88
+ path = Configuration.temp_path
89
+ @options[:control_url] = "unix://#{path}"
90
+ @options[:control_url_temp] = path
91
+ else
92
+ @options[:control_url] = url
93
+ end
39
94
 
40
- if opts
95
+ if opts[:no_token]
96
+ auth_token = :none
97
+ else
41
98
  auth_token = opts[:auth_token]
42
- @options[:control_auth_token] = auth_token if auth_token
43
-
44
- @options[:control_auth_token] = :none if opts[:no_token]
45
- @options[:control_url_umask] = opts[:umask] if opts[:umask]
99
+ auth_token ||= Configuration.random_token
46
100
  end
101
+
102
+ @options[:control_auth_token] = auth_token
103
+ @options[:control_url_umask] = opts[:umask] if opts[:umask]
104
+ end
105
+
106
+ # Load additional configuration from a file
107
+ # Files get loaded later via Configuration#load
108
+ def load(file)
109
+ @options[:config_files] ||= []
110
+ @options[:config_files] << file
47
111
  end
48
112
 
49
- # Bind the server to +url+. tcp:// and unix:// are the only accepted
50
- # 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.
117
+ #
118
+ # @example Explicitly the socket backlog depth (default is 1024)
119
+ # bind('unix:///var/run/puma.sock?backlog=2048')
51
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')
52
129
  def bind(url)
130
+ @options[:binds] ||= []
53
131
  @options[:binds] << url
54
132
  end
55
133
 
134
+ def clear_binds!
135
+ @options[:binds] = []
136
+ end
137
+
56
138
  # Define the TCP port to bind to. Use +bind+ for more advanced options.
57
139
  #
58
- def port(port)
59
- @options[:binds] << "tcp://#{Configuration::DefaultTCPHost}:#{port}"
140
+ def port(port, host=nil)
141
+ host ||= Configuration::DefaultTCPHost
142
+ bind "tcp://#{host}:#{port}"
143
+ end
144
+
145
+ # Define how long persistent connections can be idle before puma closes
146
+ # them
147
+ #
148
+ def 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)
60
156
  end
61
157
 
62
158
  # Work around leaky apps that leave garbage in Thread locals
@@ -73,7 +169,7 @@ module Puma
73
169
  end
74
170
 
75
171
  # When shutting down, drain the accept socket of pending
76
- # connections and proces them. This loops over the accept
172
+ # connections and process them. This loops over the accept
77
173
  # socket until there are no more read events and then stops
78
174
  # looking and waits for the requests to finish.
79
175
  def drain_on_shutdown(which=true)
@@ -85,12 +181,33 @@ module Puma
85
181
  @options[:environment] = environment
86
182
  end
87
183
 
184
+ # How long to wait for threads to stop when shutting them
185
+ # down. Defaults to :forever. Specifying :immediately will cause
186
+ # Puma to kill the threads immediately. Otherwise the value
187
+ # is the number of seconds to wait.
188
+ #
189
+ # Puma always waits a few seconds after killing a thread for it to try
190
+ # to finish up it's work, even in :immediately mode.
191
+ def force_shutdown_after(val=:forever)
192
+ i = case val
193
+ when :forever
194
+ -1
195
+ when :immediately
196
+ 0
197
+ else
198
+ Integer(val)
199
+ end
200
+
201
+ @options[:force_shutdown_after] = i
202
+ end
203
+
88
204
  # Code to run before doing a restart. This code should
89
205
  # close logfiles, database connections, etc.
90
206
  #
91
207
  # This can be called multiple times to add code each time.
92
208
  #
93
209
  def on_restart(&block)
210
+ @options[:on_restart] ||= []
94
211
  @options[:on_restart] << block
95
212
  end
96
213
 
@@ -99,18 +216,30 @@ module Puma
99
216
  # to puma, as those are the same as the original process.
100
217
  #
101
218
  def restart_command(cmd)
102
- @options[:restart_cmd] = cmd
219
+ @options[:restart_cmd] = cmd.to_s
103
220
  end
104
221
 
105
222
  # Store the pid of the server in the file at +path+.
106
223
  def pidfile(path)
107
- @options[:pidfile] = path
224
+ @options[:pidfile] = path.to_s
108
225
  end
109
226
 
110
227
  # Disable request logging.
111
228
  #
112
- def quiet
113
- @options[:quiet] = true
229
+ def quiet(which=true)
230
+ @options[:log_requests] = !which
231
+ end
232
+
233
+ # Enable request logging
234
+ #
235
+ def log_requests(which=true)
236
+ @options[:log_requests] = which
237
+ end
238
+
239
+ # Show debugging info
240
+ #
241
+ def debug
242
+ @options[:debug] = true
114
243
  end
115
244
 
116
245
  # Load +path+ as a rackup file.
@@ -119,6 +248,16 @@ module Puma
119
248
  @options[:rackup] = path.to_s
120
249
  end
121
250
 
251
+ # Run Puma in TCP mode
252
+ #
253
+ def tcp_mode!
254
+ @options[:mode] = :tcp
255
+ end
256
+
257
+ def early_hints(answer=true)
258
+ @options[:early_hints] = answer
259
+ end
260
+
122
261
  # Redirect STDOUT and STDERR to files specified.
123
262
  def stdout_redirect(stdout=nil, stderr=nil, append=false)
124
263
  @options[:redirect_stdout] = stdout
@@ -136,16 +275,22 @@ module Puma
136
275
  raise "The minimum (#{min}) number of threads must be less than or equal to the max (#{max})"
137
276
  end
138
277
 
278
+ if max < 1
279
+ raise "The maximum number of threads (#{max}) must be greater than 0"
280
+ end
281
+
139
282
  @options[:min_threads] = min
140
283
  @options[:max_threads] = max
141
284
  end
142
285
 
143
286
  def ssl_bind(host, port, opts)
287
+ verify = opts.fetch(:verify_mode, 'none')
288
+
144
289
  if defined?(JRUBY_VERSION)
145
290
  keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
146
- @options[:binds] << "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}"
291
+ bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}"
147
292
  else
148
- @options[:binds] << "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}"
293
+ bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&verify_mode=#{verify}"
149
294
  end
150
295
  end
151
296
 
@@ -172,9 +317,20 @@ module Puma
172
317
  # This can be called multiple times to add hooks.
173
318
  #
174
319
  def before_fork(&block)
320
+ @options[:before_fork] ||= []
175
321
  @options[:before_fork] << block
176
322
  end
177
323
 
324
+ # *Cluster mode only* Code to run in a worker when it boots to setup
325
+ # the process before booting the app.
326
+ #
327
+ # This can be called multiple times to add hooks.
328
+ #
329
+ def on_worker_boot(&block)
330
+ @options[:before_worker_boot] ||= []
331
+ @options[:before_worker_boot] << block
332
+ end
333
+
178
334
  # *Cluster mode only* Code to run immediately before a worker shuts
179
335
  # down (after it has finished processing HTTP requests). These hooks
180
336
  # can block if necessary to wait for background operations unknown
@@ -183,40 +339,41 @@ module Puma
183
339
  # This can be called multiple times to add hooks.
184
340
  #
185
341
  def on_worker_shutdown(&block)
342
+ @options[:before_worker_shutdown] ||= []
186
343
  @options[:before_worker_shutdown] << block
187
344
  end
188
345
 
189
- # *Cluster mode only* Code to run when a worker boots to setup
190
- # the process before booting the app.
191
- #
192
- # This can be called multiple times to add hooks.
193
- #
194
- def on_worker_boot(&block)
195
- @options[:before_worker_boot] << block
196
- end
197
-
198
- # *Cluster mode only* Code to run when a master process is
346
+ # *Cluster mode only* Code to run in the master when it is
199
347
  # about to create the worker by forking itself.
200
348
  #
201
349
  # This can be called multiple times to add hooks.
202
350
  #
203
351
  def on_worker_fork(&block)
352
+ @options[:before_worker_fork] ||= []
204
353
  @options[:before_worker_fork] << block
205
354
  end
206
355
 
207
- # *Cluster mode only* Code to run when a worker boots to setup
208
- # the process after booting the app.
356
+ # *Cluster mode only* Code to run in the master after it starts
357
+ # a worker.
209
358
  #
210
359
  # This can be called multiple times to add hooks.
211
360
  #
212
- def after_worker_boot(&block)
213
- @options[:after_worker_boot] << block
361
+ def after_worker_fork(&block)
362
+ @options[:after_worker_fork] ||= []
363
+ @options[:after_worker_fork] = block
214
364
  end
215
365
 
366
+ alias_method :after_worker_boot, :after_worker_fork
367
+
216
368
  # The directory to operate out of.
217
369
  def directory(dir)
218
370
  @options[:directory] = dir.to_s
219
- @options[:worker_directory] = dir.to_s
371
+ end
372
+
373
+ # DEPRECATED: The directory to operate out of.
374
+ def worker_directory(dir)
375
+ $stderr.puts "worker_directory is deprecated. Please use `directory`"
376
+ directory dir
220
377
  end
221
378
 
222
379
  # Run the app as a raw TCP app instead of an HTTP rack app
@@ -259,7 +416,7 @@ module Puma
259
416
 
260
417
  # Additional text to display in process listing
261
418
  def tag(string)
262
- @options[:tag] = string
419
+ @options[:tag] = string.to_s
263
420
  end
264
421
 
265
422
  # *Cluster mode only* Set the timeout for workers in seconds
@@ -267,17 +424,17 @@ module Puma
267
424
  # that have not checked in within the given +timeout+.
268
425
  # This mitigates hung processes. Default value is 60 seconds.
269
426
  def worker_timeout(timeout)
270
- @options[:worker_timeout] = timeout
427
+ @options[:worker_timeout] = Integer(timeout)
271
428
  end
272
429
 
273
430
  # *Cluster mode only* Set the timeout for workers to boot
274
431
  def worker_boot_timeout(timeout)
275
- @options[:worker_boot_timeout] = timeout
432
+ @options[:worker_boot_timeout] = Integer(timeout)
276
433
  end
277
434
 
278
435
  # *Cluster mode only* Set the timeout for worker shutdown
279
436
  def worker_shutdown_timeout(timeout)
280
- @options[:worker_shutdown_timeout] = timeout
437
+ @options[:worker_shutdown_timeout] = Integer(timeout)
281
438
  end
282
439
 
283
440
  # When set to true (the default), workers accept all requests
@@ -344,5 +501,6 @@ module Puma
344
501
  raise "Invalid value for set_remote_address - #{val}"
345
502
  end
346
503
  end
504
+
347
505
  end
348
506
  end
data/lib/puma/events.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'puma/const'
2
+ require "puma/null_io"
2
3
  require 'stringio'
3
4
 
4
5
  module Puma
@@ -34,8 +35,6 @@ module Puma
34
35
 
35
36
  @debug = ENV.key? 'PUMA_DEBUG'
36
37
 
37
- @on_booted = []
38
-
39
38
  @hooks = Hash.new { |h,k| h[k] = [] }
40
39
  end
41
40
 
@@ -48,7 +47,7 @@ module Puma
48
47
  @hooks[hook].each { |t| t.call(*args) }
49
48
  end
50
49
 
51
- # Register a callbock for a given hook
50
+ # Register a callback for a given hook
52
51
  #
53
52
  def register(hook, obj=nil, &blk)
54
53
  if obj and blk
@@ -92,8 +91,7 @@ module Puma
92
91
  # parsing exception.
93
92
  #
94
93
  def parse_error(server, env, error)
95
- @stderr.puts "#{Time.now}: HTTP parse error, malformed request (#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}): #{error.inspect}"
96
- @stderr.puts "#{Time.now}: ENV: #{env.inspect}\n---\n"
94
+ @stderr.puts "#{Time.now}: HTTP parse error, malformed request (#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}): #{error.inspect}\n---\n"
97
95
  end
98
96
 
99
97
  # An SSL error has occurred.
@@ -106,24 +104,30 @@ module Puma
106
104
  end
107
105
 
108
106
  # An unknown error has occurred.
109
- # +server+ is the Server object, +env+ the request, +error+ an exception
110
- # object, and +kind+ some additional info.
107
+ # +server+ is the Server object, +error+ an exception object,
108
+ # +kind+ some additional info, and +env+ the request.
111
109
  #
112
- def unknown_error(server, error, kind="Unknown")
110
+ def unknown_error(server, error, kind="Unknown", env=nil)
113
111
  if error.respond_to? :render
114
112
  error.render "#{Time.now}: #{kind} error", @stderr
115
113
  else
116
- @stderr.puts "#{Time.now}: #{kind} error: #{error.inspect}"
117
- @stderr.puts error.backtrace.join("\n")
114
+ if env
115
+ string_block = [ "#{Time.now}: #{kind} error handling request { #{env['REQUEST_METHOD']} #{env['PATH_INFO']} }" ]
116
+ string_block << error.inspect
117
+ else
118
+ string_block = [ "#{Time.now}: #{kind} error: #{error.inspect}" ]
119
+ end
120
+ string_block << error.backtrace
121
+ @stderr.puts string_block.join("\n")
118
122
  end
119
123
  end
120
124
 
121
- def on_booted(&b)
122
- @on_booted << b
125
+ def on_booted(&block)
126
+ register(:on_booted, &block)
123
127
  end
124
128
 
125
129
  def fire_on_booted!
126
- @on_booted.each { |b| b.call }
130
+ fire(:on_booted)
127
131
  end
128
132
 
129
133
  DEFAULT = new(STDOUT, STDERR)
@@ -138,5 +142,10 @@ module Puma
138
142
  def self.stdio
139
143
  Events.new $stdout, $stderr
140
144
  end
145
+
146
+ def self.null
147
+ n = NullIO.new
148
+ Events.new n, n
149
+ end
141
150
  end
142
151
  end
@@ -1,6 +1,6 @@
1
1
  require 'puma/detect'
2
2
 
3
- if Puma::IS_JRUBY
3
+ if Puma.jruby?
4
4
  require 'puma/java_io_buffer'
5
5
  else
6
6
  require 'puma/puma_http11'
@@ -61,7 +61,7 @@ module Puma
61
61
  end
62
62
 
63
63
  def self.daemon_start(dir, argv)
64
- ENV['PUMA_DAEMON_RESTART'] = Process.pid.to_s
64
+ ENV[RestartKey] = Process.pid.to_s
65
65
 
66
66
  if k = ENV['PUMA_JRUBY_DAEMON_OPTS']
67
67
  ENV['JRUBY_OPTS'] = k
@@ -80,4 +80,3 @@ module Puma
80
80
  end
81
81
  end
82
82
  end
83
-