puma 4.3.12 → 5.6.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1526 -524
  3. data/LICENSE +23 -20
  4. data/README.md +120 -36
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +63 -26
  7. data/docs/compile_options.md +21 -0
  8. data/docs/deployment.md +60 -69
  9. data/docs/fork_worker.md +33 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +9 -0
  14. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  15. data/{tools → docs}/jungle/rc.d/puma +2 -2
  16. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  17. data/docs/kubernetes.md +66 -0
  18. data/docs/nginx.md +1 -1
  19. data/docs/plugins.md +15 -15
  20. data/docs/rails_dev_mode.md +28 -0
  21. data/docs/restart.md +46 -23
  22. data/docs/signals.md +13 -11
  23. data/docs/stats.md +142 -0
  24. data/docs/systemd.md +85 -128
  25. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  26. data/ext/puma_http11/ext_help.h +1 -1
  27. data/ext/puma_http11/extconf.rb +44 -10
  28. data/ext/puma_http11/http11_parser.c +45 -47
  29. data/ext/puma_http11/http11_parser.h +1 -1
  30. data/ext/puma_http11/http11_parser.java.rl +1 -1
  31. data/ext/puma_http11/http11_parser.rl +1 -1
  32. data/ext/puma_http11/http11_parser_common.rl +0 -0
  33. data/ext/puma_http11/mini_ssl.c +225 -89
  34. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  35. data/ext/puma_http11/org/jruby/puma/Http11.java +5 -3
  36. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +3 -5
  37. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +109 -67
  38. data/ext/puma_http11/puma_http11.c +32 -51
  39. data/lib/puma/app/status.rb +50 -36
  40. data/lib/puma/binder.rb +225 -106
  41. data/lib/puma/cli.rb +24 -18
  42. data/lib/puma/client.rb +146 -84
  43. data/lib/puma/cluster/worker.rb +173 -0
  44. data/lib/puma/cluster/worker_handle.rb +94 -0
  45. data/lib/puma/cluster.rb +212 -220
  46. data/lib/puma/commonlogger.rb +2 -2
  47. data/lib/puma/configuration.rb +58 -49
  48. data/lib/puma/const.rb +22 -7
  49. data/lib/puma/control_cli.rb +99 -76
  50. data/lib/puma/detect.rb +29 -2
  51. data/lib/puma/dsl.rb +368 -96
  52. data/lib/puma/error_logger.rb +104 -0
  53. data/lib/puma/events.rb +55 -34
  54. data/lib/puma/io_buffer.rb +9 -2
  55. data/lib/puma/jruby_restart.rb +0 -58
  56. data/lib/puma/json_serialization.rb +96 -0
  57. data/lib/puma/launcher.rb +128 -46
  58. data/lib/puma/minissl/context_builder.rb +14 -9
  59. data/lib/puma/minissl.rb +137 -50
  60. data/lib/puma/null_io.rb +18 -1
  61. data/lib/puma/plugin/tmp_restart.rb +0 -0
  62. data/lib/puma/plugin.rb +3 -12
  63. data/lib/puma/queue_close.rb +26 -0
  64. data/lib/puma/rack/builder.rb +1 -5
  65. data/lib/puma/rack/urlmap.rb +0 -0
  66. data/lib/puma/rack_default.rb +0 -0
  67. data/lib/puma/reactor.rb +85 -369
  68. data/lib/puma/request.rb +489 -0
  69. data/lib/puma/runner.rb +46 -61
  70. data/lib/puma/server.rb +292 -763
  71. data/lib/puma/single.rb +9 -65
  72. data/lib/puma/state_file.rb +48 -8
  73. data/lib/puma/systemd.rb +46 -0
  74. data/lib/puma/thread_pool.rb +125 -57
  75. data/lib/puma/util.rb +32 -4
  76. data/lib/puma.rb +48 -0
  77. data/lib/rack/handler/puma.rb +2 -3
  78. data/lib/rack/version_restriction.rb +15 -0
  79. data/tools/{docker/Dockerfile → Dockerfile} +1 -1
  80. data/tools/trickletest.rb +0 -0
  81. metadata +29 -24
  82. data/docs/tcp_mode.md +0 -96
  83. data/ext/puma_http11/io_buffer.c +0 -155
  84. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  85. data/lib/puma/accept_nonblock.rb +0 -29
  86. data/lib/puma/tcp_logger.rb +0 -41
  87. data/tools/jungle/README.md +0 -19
  88. data/tools/jungle/init.d/README.md +0 -61
  89. data/tools/jungle/init.d/puma +0 -421
  90. data/tools/jungle/init.d/run-puma +0 -18
  91. data/tools/jungle/upstart/README.md +0 -61
  92. data/tools/jungle/upstart/puma-manager.conf +0 -31
  93. data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/binder.rb CHANGED
@@ -5,16 +5,32 @@ require 'socket'
5
5
 
6
6
  require 'puma/const'
7
7
  require 'puma/util'
8
- require 'puma/minissl/context_builder'
8
+ require 'puma/configuration'
9
9
 
10
10
  module Puma
11
+
12
+ if HAS_SSL
13
+ require 'puma/minissl'
14
+ require 'puma/minissl/context_builder'
15
+
16
+ # Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
17
+ # NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
18
+ # The bug was that it did not require openssl.
19
+ # @todo remove when Ruby 2.3 support is dropped
20
+ #
21
+ if windows? && RbConfig::CONFIG['ruby_version'] == '2.3.0'
22
+ require 'openssl'
23
+ end
24
+ end
25
+
11
26
  class Binder
12
27
  include Puma::Const
13
28
 
14
- RACK_VERSION = [1,3].freeze
29
+ RACK_VERSION = [1,6].freeze
15
30
 
16
- def initialize(events)
31
+ def initialize(events, conf = Configuration.new)
17
32
  @events = events
33
+ @conf = conf
18
34
  @listeners = []
19
35
  @inherited_fds = {}
20
36
  @activated_sockets = {}
@@ -23,9 +39,10 @@ module Puma
23
39
  @proto_env = {
24
40
  "rack.version".freeze => RACK_VERSION,
25
41
  "rack.errors".freeze => events.stderr,
26
- "rack.multithread".freeze => true,
27
- "rack.multiprocess".freeze => false,
42
+ "rack.multithread".freeze => conf.options[:max_threads] > 1,
43
+ "rack.multiprocess".freeze => conf.options[:workers] >= 1,
28
44
  "rack.run_once".freeze => false,
45
+ RACK_URL_SCHEME => conf.options[:rack_url_scheme],
29
46
  "SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
30
47
 
31
48
  # I'd like to set a default CONTENT_TYPE here but some things
@@ -41,10 +58,17 @@ module Puma
41
58
 
42
59
  @envs = {}
43
60
  @ios = []
61
+ localhost_authority
44
62
  end
45
63
 
46
64
  attr_reader :ios
47
65
 
66
+ # @version 5.0.0
67
+ attr_reader :activated_sockets, :envs, :inherited_fds, :listeners, :proto_env, :unix_paths
68
+
69
+ # @version 5.0.0
70
+ attr_writer :ios, :listeners
71
+
48
72
  def env(sock)
49
73
  @envs.fetch(sock, @proto_env)
50
74
  end
@@ -53,40 +77,82 @@ module Puma
53
77
  @ios.each { |i| i.close }
54
78
  end
55
79
 
56
- def import_from_env
57
- remove = []
58
-
59
- ENV.each do |k,v|
60
- if k =~ /PUMA_INHERIT_\d+/
61
- fd, url = v.split(":", 2)
62
- @inherited_fds[url] = fd.to_i
63
- remove << k
64
- elsif k == 'LISTEN_FDS' && ENV['LISTEN_PID'].to_i == $$
65
- v.to_i.times do |num|
66
- fd = num + 3
67
- sock = TCPServer.for_fd(fd)
68
- begin
69
- key = [ :unix, Socket.unpack_sockaddr_un(sock.getsockname) ]
70
- rescue ArgumentError
71
- port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
72
- if addr =~ /\:/
73
- addr = "[#{addr}]"
74
- end
75
- key = [ :tcp, addr, port ]
76
- end
77
- @activated_sockets[key] = sock
78
- @events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
79
- end
80
- remove << k << 'LISTEN_PID'
80
+ # @!attribute [r] connected_ports
81
+ # @version 5.0.0
82
+ def connected_ports
83
+ ios.map { |io| io.addr[1] }.uniq
84
+ end
85
+
86
+ # @version 5.0.0
87
+ def create_inherited_fds(env_hash)
88
+ env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
89
+ fd, url = v.split(":", 2)
90
+ @inherited_fds[url] = fd.to_i
91
+ end.keys # pass keys back for removal
92
+ end
93
+
94
+ # systemd socket activation.
95
+ # LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
96
+ # LISTEN_PID = PID of the service process, aka us
97
+ # @see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
98
+ # @version 5.0.0
99
+ #
100
+ def create_activated_fds(env_hash)
101
+ @events.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
102
+ return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
103
+ env_hash['LISTEN_FDS'].to_i.times do |index|
104
+ sock = TCPServer.for_fd(socket_activation_fd(index))
105
+ key = begin # Try to parse as a path
106
+ [:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
107
+ rescue ArgumentError # Try to parse as a port/ip
108
+ port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
109
+ addr = "[#{addr}]" if addr =~ /\:/
110
+ [:tcp, addr, port]
111
+ end
112
+ @activated_sockets[key] = sock
113
+ @events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
114
+ end
115
+ ["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
116
+ end
117
+
118
+ # Synthesize binds from systemd socket activation
119
+ #
120
+ # When systemd socket activation is enabled, it can be tedious to keep the
121
+ # binds in sync. This method can synthesize any binds based on the received
122
+ # activated sockets. Any existing matching binds will be respected.
123
+ #
124
+ # When only_matching is true in, all binds that do not match an activated
125
+ # socket is removed in place.
126
+ #
127
+ # It's a noop if no activated sockets were received.
128
+ def synthesize_binds_from_activated_fs(binds, only_matching)
129
+ return binds unless activated_sockets.any?
130
+
131
+ activated_binds = []
132
+
133
+ activated_sockets.keys.each do |proto, addr, port|
134
+ if port
135
+ tcp_url = "#{proto}://#{addr}:#{port}"
136
+ ssl_url = "ssl://#{addr}:#{port}"
137
+ ssl_url_prefix = "#{ssl_url}?"
138
+
139
+ existing = binds.find { |bind| bind == tcp_url || bind == ssl_url || bind.start_with?(ssl_url_prefix) }
140
+
141
+ activated_binds << (existing || tcp_url)
142
+ else
143
+ # TODO: can there be a SSL bind without a port?
144
+ activated_binds << "#{proto}://#{addr}"
81
145
  end
82
146
  end
83
147
 
84
- remove.each do |k|
85
- ENV.delete k
148
+ if only_matching
149
+ activated_binds
150
+ else
151
+ binds | activated_binds
86
152
  end
87
153
  end
88
154
 
89
- def parse(binds, logger)
155
+ def parse(binds, logger, log_msg = 'Listening')
90
156
  binds.each do |str|
91
157
  uri = URI.parse str
92
158
  case uri.scheme
@@ -98,33 +164,37 @@ module Puma
98
164
  io = inherit_tcp_listener uri.host, uri.port, sock
99
165
  logger.log "* Activated #{str}"
100
166
  else
167
+ ios_len = @ios.length
101
168
  params = Util.parse_query uri.query
102
169
 
103
- opt = params.key?('low_latency')
104
- bak = params.fetch('backlog', 1024).to_i
105
-
106
- io = add_tcp_listener uri.host, uri.port, opt, bak
170
+ opt = params.key?('low_latency') && params['low_latency'] != 'false'
171
+ backlog = params.fetch('backlog', 1024).to_i
107
172
 
108
- @ios.each do |i|
109
- next unless TCPServer === i
110
- addr = if i.local_address.ipv6?
111
- "[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
112
- else
113
- i.local_address.ip_unpack.join(':')
114
- end
173
+ io = add_tcp_listener uri.host, uri.port, opt, backlog
115
174
 
116
- logger.log "* Listening on tcp://#{addr}"
175
+ @ios[ios_len..-1].each do |i|
176
+ addr = loc_addr_str i
177
+ logger.log "* #{log_msg} on http://#{addr}"
117
178
  end
118
179
  end
119
180
 
120
181
  @listeners << [str, io] if io
121
182
  when "unix"
122
183
  path = "#{uri.host}#{uri.path}".gsub("%20", " ")
184
+ abstract = false
185
+ if str.start_with? 'unix://@'
186
+ raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
187
+ abstract = true
188
+ path = "@#{path}"
189
+ end
123
190
 
124
191
  if fd = @inherited_fds.delete(str)
192
+ @unix_paths << path unless abstract || File.exist?(path)
125
193
  io = inherit_unix_listener path, fd
126
194
  logger.log "* Inherited #{str}"
127
- elsif sock = @activated_sockets.delete([ :unix, path ])
195
+ elsif sock = @activated_sockets.delete([ :unix, path ]) ||
196
+ @activated_sockets.delete([ :unix, File.realdirpath(path) ])
197
+ @unix_paths << path unless abstract || File.exist?(path)
128
198
  io = inherit_unix_listener path, sock
129
199
  logger.log "* Activated #{str}"
130
200
  else
@@ -148,14 +218,36 @@ module Puma
148
218
  end
149
219
  end
150
220
 
221
+ @unix_paths << path unless abstract || File.exist?(path)
151
222
  io = add_unix_listener path, umask, mode, backlog
152
- logger.log "* Listening on #{str}"
223
+ logger.log "* #{log_msg} on #{str}"
153
224
  end
154
225
 
155
226
  @listeners << [str, io]
156
227
  when "ssl"
228
+
229
+ raise "Puma compiled without SSL support" unless HAS_SSL
230
+
157
231
  params = Util.parse_query uri.query
158
- ctx = MiniSSL::ContextBuilder.new(params, @events).context
232
+
233
+ # If key and certs are not defined and localhost gem is required.
234
+ # localhost gem will be used for self signed
235
+ # Load localhost authority if not loaded.
236
+ if params.values_at('cert', 'key').all? { |v| v.to_s.empty? }
237
+ ctx = localhost_authority && localhost_authority_context
238
+ end
239
+
240
+ ctx ||=
241
+ begin
242
+ # Extract cert_pem and key_pem from options[:store] if present
243
+ ['cert', 'key'].each do |v|
244
+ if params[v] && params[v].start_with?('store:')
245
+ index = Integer(params.delete(v).split('store:').last)
246
+ params["#{v}_pem"] = @conf.options[:store][index]
247
+ end
248
+ end
249
+ MiniSSL::ContextBuilder.new(params, @events).context
250
+ end
159
251
 
160
252
  if fd = @inherited_fds.delete(str)
161
253
  logger.log "* Inherited #{str}"
@@ -164,8 +256,14 @@ module Puma
164
256
  io = inherit_ssl_listener sock, ctx
165
257
  logger.log "* Activated #{str}"
166
258
  else
167
- io = add_ssl_listener uri.host, uri.port, ctx
168
- logger.log "* Listening on #{str}"
259
+ ios_len = @ios.length
260
+ backlog = params.fetch('backlog', 1024).to_i
261
+ io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
262
+
263
+ @ios[ios_len..-1].each do |i|
264
+ addr = loc_addr_str i
265
+ logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
266
+ end
169
267
  end
170
268
 
171
269
  @listeners << [str, io] if io
@@ -193,21 +291,35 @@ module Puma
193
291
  end
194
292
 
195
293
  # Also close any unused activated sockets
196
- @activated_sockets.each do |key, sock|
197
- logger.log "* Closing unused activated socket: #{key.join ':'}"
198
- begin
199
- sock.close
200
- rescue SystemCallError
294
+ unless @activated_sockets.empty?
295
+ fds = @ios.map(&:to_i)
296
+ @activated_sockets.each do |key, sock|
297
+ next if fds.include? sock.to_i
298
+ logger.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
299
+ begin
300
+ sock.close
301
+ rescue SystemCallError
302
+ end
303
+ # We have to unlink a unix socket path that's not being used
304
+ File.unlink key[1] if key.first == :unix
201
305
  end
202
- # We have to unlink a unix socket path that's not being used
203
- File.unlink key[1] if key[0] == :unix
204
306
  end
205
307
  end
206
308
 
207
- def loopback_addresses
208
- Socket.ip_address_list.select do |addrinfo|
209
- addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
210
- end.map { |addrinfo| addrinfo.ip_address }.uniq
309
+ def localhost_authority
310
+ @localhost_authority ||= Localhost::Authority.fetch if defined?(Localhost::Authority) && !Puma::IS_JRUBY
311
+ end
312
+
313
+ def localhost_authority_context
314
+ return unless localhost_authority
315
+
316
+ key_path, crt_path = if [:key_path, :certificate_path].all? { |m| localhost_authority.respond_to?(m) }
317
+ [localhost_authority.key_path, localhost_authority.certificate_path]
318
+ else
319
+ local_certificates_path = File.expand_path("~/.localhost")
320
+ [File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
321
+ end
322
+ MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @events).context
211
323
  end
212
324
 
213
325
  # Tell the server to listen on host +host+, port +port+.
@@ -226,26 +338,20 @@ module Puma
226
338
  end
227
339
 
228
340
  host = host[1..-2] if host and host[0..0] == '['
229
- s = TCPServer.new(host, port)
341
+ tcp_server = TCPServer.new(host, port)
342
+
230
343
  if optimize_for_latency
231
- s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
344
+ tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
232
345
  end
233
- s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
234
- s.listen backlog
235
- @connected_port = s.addr[1]
346
+ tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
347
+ tcp_server.listen backlog
236
348
 
237
- @ios << s
238
- s
349
+ @ios << tcp_server
350
+ tcp_server
239
351
  end
240
352
 
241
- attr_reader :connected_port
242
-
243
353
  def inherit_tcp_listener(host, port, fd)
244
- if fd.kind_of? TCPServer
245
- s = fd
246
- else
247
- s = TCPServer.for_fd(fd)
248
- end
354
+ s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
249
355
 
250
356
  @ios << s
251
357
  s
@@ -253,9 +359,10 @@ module Puma
253
359
 
254
360
  def add_ssl_listener(host, port, ctx,
255
361
  optimize_for_latency=true, backlog=1024)
256
- require 'puma/minissl'
257
362
 
258
- MiniSSL.check
363
+ raise "Puma compiled without SSL support" unless HAS_SSL
364
+ # Puma will try to use local authority context if context is supplied nil
365
+ ctx ||= localhost_authority_context
259
366
 
260
367
  if host == "localhost"
261
368
  loopback_addresses.each do |addr|
@@ -272,7 +379,6 @@ module Puma
272
379
  s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
273
380
  s.listen backlog
274
381
 
275
-
276
382
  ssl = MiniSSL::Server.new s, ctx
277
383
  env = @proto_env.dup
278
384
  env[HTTPS_KEY] = HTTPS
@@ -283,14 +389,12 @@ module Puma
283
389
  end
284
390
 
285
391
  def inherit_ssl_listener(fd, ctx)
286
- require 'puma/minissl'
287
- MiniSSL.check
392
+ raise "Puma compiled without SSL support" unless HAS_SSL
393
+ # Puma will try to use local authority context if context is supplied nil
394
+ ctx ||= localhost_authority_context
395
+
396
+ s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
288
397
 
289
- if fd.kind_of? TCPServer
290
- s = fd
291
- else
292
- s = TCPServer.for_fd(fd)
293
- end
294
398
  ssl = MiniSSL::Server.new(s, ctx)
295
399
 
296
400
  env = @proto_env.dup
@@ -305,8 +409,6 @@ module Puma
305
409
  # Tell the server to listen on +path+ as a UNIX domain socket.
306
410
  #
307
411
  def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
308
- @unix_paths << path unless File.exist? path
309
-
310
412
  # Let anyone connect by default
311
413
  umask ||= 0
312
414
 
@@ -323,8 +425,7 @@ module Puma
323
425
  raise "There is already a server bound to: #{path}"
324
426
  end
325
427
  end
326
-
327
- s = UNIXServer.new(path)
428
+ s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
328
429
  s.listen backlog
329
430
  @ios << s
330
431
  ensure
@@ -343,13 +444,8 @@ module Puma
343
444
  end
344
445
 
345
446
  def inherit_unix_listener(path, fd)
346
- @unix_paths << path unless File.exist? path
447
+ s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
347
448
 
348
- if fd.kind_of? TCPServer
349
- s = fd
350
- else
351
- s = UNIXServer.for_fd fd
352
- end
353
449
  @ios << s
354
450
 
355
451
  env = @proto_env.dup
@@ -361,25 +457,48 @@ module Puma
361
457
 
362
458
  def close_listeners
363
459
  @listeners.each do |l, io|
364
- io.close
365
- uri = URI.parse(l)
460
+ io.close unless io.closed?
461
+ uri = URI.parse l
366
462
  next unless uri.scheme == 'unix'
367
463
  unix_path = "#{uri.host}#{uri.path}"
368
- File.unlink unix_path if @unix_paths.include? unix_path
464
+ File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
369
465
  end
370
466
  end
371
467
 
372
- def close_unix_paths
373
- @unix_paths.each { |up| File.unlink(up) if File.exist? up }
468
+ def redirects_for_restart
469
+ redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
470
+ redirects[:close_others] = true
471
+ redirects
374
472
  end
375
473
 
376
- def redirects_for_restart
377
- redirects = {:close_others => true}
378
- @listeners.each_with_index do |(l, io), i|
379
- ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
380
- redirects[io.to_i] = io.to_i
474
+ # @version 5.0.0
475
+ def redirects_for_restart_env
476
+ @listeners.each_with_object({}).with_index do |(listen, memo), i|
477
+ memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
381
478
  end
382
- redirects
479
+ end
480
+
481
+ private
482
+
483
+ # @!attribute [r] loopback_addresses
484
+ def loopback_addresses
485
+ Socket.ip_address_list.select do |addrinfo|
486
+ addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
487
+ end.map { |addrinfo| addrinfo.ip_address }.uniq
488
+ end
489
+
490
+ def loc_addr_str(io)
491
+ loc_addr = io.to_io.local_address
492
+ if loc_addr.ipv6?
493
+ "[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
494
+ else
495
+ loc_addr.ip_unpack.join(':')
496
+ end
497
+ end
498
+
499
+ # @version 5.0.0
500
+ def socket_activation_fd(int)
501
+ int + 3 # 3 is the magic number you add to follow the SA protocol
383
502
  end
384
503
  end
385
504
  end
data/lib/puma/cli.rb CHANGED
@@ -11,16 +11,17 @@ require 'puma/events'
11
11
 
12
12
  module Puma
13
13
  class << self
14
- # The CLI exports its Puma::Configuration object here to allow
15
- # apps to pick it up. An app needs to use it conditionally though
16
- # since it is not set if the app is launched via another
17
- # mechanism than the CLI class.
14
+ # The CLI exports a Puma::Configuration instance here to allow
15
+ # apps to pick it up. An app must load this object conditionally
16
+ # because it is not set if the app is launched via any mechanism
17
+ # other than the CLI class.
18
18
  attr_accessor :cli_config
19
19
  end
20
20
 
21
21
  # Handles invoke a Puma::Server in a command line style.
22
22
  #
23
23
  class CLI
24
+ # @deprecated 6.0.0
24
25
  KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
25
26
 
26
27
  # Create a new CLI object using +argv+ as the command line
@@ -80,7 +81,7 @@ module Puma
80
81
  @launcher.run
81
82
  end
82
83
 
83
- private
84
+ private
84
85
  def unsupported(str)
85
86
  @events.error(str)
86
87
  raise UnsupportedOption
@@ -104,16 +105,20 @@ module Puma
104
105
  user_config.bind arg
105
106
  end
106
107
 
108
+ o.on "--bind-to-activated-sockets [only]", "Bind to all activated sockets" do |arg|
109
+ user_config.bind_to_activated_sockets(arg || true)
110
+ end
111
+
107
112
  o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
108
113
  file_config.load arg
109
114
  end
110
115
 
111
- o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
112
- configure_control_url(arg)
116
+ # Identical to supplying --config "-", but more semantic
117
+ o.on "--no-config", "Prevent Puma from searching for a config file" do |arg|
118
+ file_config.load "-"
113
119
  end
114
120
 
115
- # alias --control-url for backwards-compatibility
116
- o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
121
+ o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
117
122
  configure_control_url(arg)
118
123
  end
119
124
 
@@ -122,11 +127,6 @@ module Puma
122
127
  @control_options[:auth_token] = arg
123
128
  end
124
129
 
125
- o.on "-d", "--daemon", "Daemonize the server into the background" do
126
- user_config.daemonize
127
- user_config.quiet
128
- end
129
-
130
130
  o.on "--debug", "Log lowlevel debugging information" do
131
131
  user_config.debug
132
132
  end
@@ -140,6 +140,12 @@ module Puma
140
140
  user_config.environment arg
141
141
  end
142
142
 
143
+ o.on "-f", "--fork-worker=[REQUESTS]", OptionParser::DecimalInteger,
144
+ "Fork new workers from existing worker. Cluster mode only",
145
+ "Auto-refork after REQUESTS (default 1000)" do |*args|
146
+ user_config.fork_worker(*args.compact)
147
+ end
148
+
143
149
  o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
144
150
  $LOAD_PATH.unshift(*arg.split(':'))
145
151
  end
@@ -179,6 +185,10 @@ module Puma
179
185
  user_config.restart_command cmd
180
186
  end
181
187
 
188
+ o.on "-s", "--silent", "Do not log prompt messages other than errors" do
189
+ @events = Events.new NullIO.new, $stderr
190
+ end
191
+
182
192
  o.on "-S", "--state PATH", "Where to store the state details" do |arg|
183
193
  user_config.state_path arg
184
194
  end
@@ -192,10 +202,6 @@ module Puma
192
202
  end
193
203
  end
194
204
 
195
- o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
196
- user_config.tcp_mode!
197
- end
198
-
199
205
  o.on "--early-hints", "Enable early hints support" do
200
206
  user_config.early_hints
201
207
  end