puma 4.3.12 → 5.6.6

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1511 -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 +3 -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 +104 -76
  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 +13 -6
  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 +476 -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 +28 -23
  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