puma 5.0.4 → 5.6.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.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +322 -48
  3. data/LICENSE +0 -0
  4. data/README.md +95 -24
  5. data/bin/puma-wild +0 -0
  6. data/docs/architecture.md +57 -20
  7. data/docs/compile_options.md +21 -0
  8. data/docs/deployment.md +53 -67
  9. data/docs/fork_worker.md +2 -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 +0 -0
  14. data/docs/jungle/rc.d/README.md +1 -1
  15. data/docs/jungle/rc.d/puma.conf +0 -0
  16. data/docs/kubernetes.md +66 -0
  17. data/docs/nginx.md +0 -0
  18. data/docs/plugins.md +15 -15
  19. data/docs/rails_dev_mode.md +28 -0
  20. data/docs/restart.md +7 -7
  21. data/docs/signals.md +11 -10
  22. data/docs/stats.md +142 -0
  23. data/docs/systemd.md +85 -66
  24. data/ext/puma_http11/PumaHttp11Service.java +0 -0
  25. data/ext/puma_http11/ext_help.h +0 -0
  26. data/ext/puma_http11/extconf.rb +42 -6
  27. data/ext/puma_http11/http11_parser.c +68 -57
  28. data/ext/puma_http11/http11_parser.h +1 -1
  29. data/ext/puma_http11/http11_parser.java.rl +1 -1
  30. data/ext/puma_http11/http11_parser.rl +1 -1
  31. data/ext/puma_http11/http11_parser_common.rl +1 -1
  32. data/ext/puma_http11/mini_ssl.c +226 -88
  33. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
  34. data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
  35. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +51 -51
  36. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +28 -43
  37. data/ext/puma_http11/puma_http11.c +9 -3
  38. data/lib/puma/app/status.rb +4 -7
  39. data/lib/puma/binder.rb +138 -49
  40. data/lib/puma/cli.rb +18 -4
  41. data/lib/puma/client.rb +113 -31
  42. data/lib/puma/cluster/worker.rb +22 -19
  43. data/lib/puma/cluster/worker_handle.rb +13 -2
  44. data/lib/puma/cluster.rb +75 -33
  45. data/lib/puma/commonlogger.rb +0 -0
  46. data/lib/puma/configuration.rb +21 -2
  47. data/lib/puma/const.rb +17 -8
  48. data/lib/puma/control_cli.rb +76 -71
  49. data/lib/puma/detect.rb +19 -9
  50. data/lib/puma/dsl.rb +225 -31
  51. data/lib/puma/error_logger.rb +12 -5
  52. data/lib/puma/events.rb +18 -3
  53. data/lib/puma/io_buffer.rb +0 -0
  54. data/lib/puma/jruby_restart.rb +0 -0
  55. data/lib/puma/json_serialization.rb +96 -0
  56. data/lib/puma/launcher.rb +56 -7
  57. data/lib/puma/minissl/context_builder.rb +14 -6
  58. data/lib/puma/minissl.rb +72 -40
  59. data/lib/puma/null_io.rb +12 -0
  60. data/lib/puma/plugin/tmp_restart.rb +0 -0
  61. data/lib/puma/plugin.rb +2 -2
  62. data/lib/puma/queue_close.rb +7 -7
  63. data/lib/puma/rack/builder.rb +1 -1
  64. data/lib/puma/rack/urlmap.rb +0 -0
  65. data/lib/puma/rack_default.rb +0 -0
  66. data/lib/puma/reactor.rb +19 -12
  67. data/lib/puma/request.rb +55 -21
  68. data/lib/puma/runner.rb +39 -13
  69. data/lib/puma/server.rb +78 -142
  70. data/lib/puma/single.rb +0 -0
  71. data/lib/puma/state_file.rb +45 -9
  72. data/lib/puma/systemd.rb +46 -0
  73. data/lib/puma/thread_pool.rb +11 -8
  74. data/lib/puma/util.rb +8 -1
  75. data/lib/puma.rb +36 -10
  76. data/lib/rack/handler/puma.rb +1 -0
  77. data/tools/Dockerfile +1 -1
  78. data/tools/trickletest.rb +0 -0
  79. metadata +15 -9
@@ -6,6 +6,7 @@ import org.jruby.RubyModule;
6
6
  import org.jruby.RubyObject;
7
7
  import org.jruby.RubyString;
8
8
  import org.jruby.anno.JRubyMethod;
9
+ import org.jruby.exceptions.RaiseException;
9
10
  import org.jruby.javasupport.JavaEmbedUtils;
10
11
  import org.jruby.runtime.Block;
11
12
  import org.jruby.runtime.ObjectAllocator;
@@ -80,11 +81,11 @@ public class MiniSSL extends RubyObject {
80
81
  /**
81
82
  * Writes bytes to the buffer after ensuring there's room
82
83
  */
83
- public void put(byte[] bytes) {
84
- if (buffer.remaining() < bytes.length) {
85
- resize(buffer.limit() + bytes.length);
84
+ private void put(byte[] bytes, final int offset, final int length) {
85
+ if (buffer.remaining() < length) {
86
+ resize(buffer.limit() + length);
86
87
  }
87
- buffer.put(bytes);
88
+ buffer.put(bytes, offset, length);
88
89
  }
89
90
 
90
91
  /**
@@ -115,7 +116,7 @@ public class MiniSSL extends RubyObject {
115
116
 
116
117
  buffer.get(bss);
117
118
  buffer.clear();
118
- return new ByteList(bss);
119
+ return new ByteList(bss, false);
119
120
  }
120
121
 
121
122
  @Override
@@ -174,8 +175,6 @@ public class MiniSSL extends RubyObject {
174
175
  @JRubyMethod
175
176
  public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
176
177
  throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
177
- KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
178
- KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
179
178
 
180
179
  String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
181
180
  KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
@@ -230,14 +229,9 @@ public class MiniSSL extends RubyObject {
230
229
 
231
230
  @JRubyMethod
232
231
  public IRubyObject inject(IRubyObject arg) {
233
- try {
234
- byte[] bytes = arg.convertToString().getBytes();
235
- inboundNetData.put(bytes);
236
- return this;
237
- } catch (Exception e) {
238
- e.printStackTrace();
239
- throw new RuntimeException(e);
240
- }
232
+ ByteList bytes = arg.convertToString().getByteList();
233
+ inboundNetData.put(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize());
234
+ return this;
241
235
  }
242
236
 
243
237
  private enum SSLOperation {
@@ -297,7 +291,7 @@ public class MiniSSL extends RubyObject {
297
291
  }
298
292
 
299
293
  @JRubyMethod
300
- public IRubyObject read() throws Exception {
294
+ public IRubyObject read() {
301
295
  try {
302
296
  inboundNetData.flip();
303
297
 
@@ -342,55 +336,46 @@ public class MiniSSL extends RubyObject {
342
336
  return getRuntime().getNil();
343
337
  }
344
338
 
345
- RubyString str = getRuntime().newString("");
346
- str.setValue(appDataByteList);
347
- return str;
348
- } catch (Exception e) {
349
- throw getRuntime().newEOFError(e.getMessage());
339
+ return RubyString.newString(getRuntime(), appDataByteList);
340
+ } catch (SSLException e) {
341
+ RaiseException re = getRuntime().newEOFError(e.getMessage());
342
+ re.initCause(e);
343
+ throw re;
350
344
  }
351
345
  }
352
346
 
353
347
  @JRubyMethod
354
348
  public IRubyObject write(IRubyObject arg) {
355
- try {
356
- byte[] bls = arg.convertToString().getBytes();
357
- outboundAppData = new MiniSSLBuffer(bls);
349
+ byte[] bls = arg.convertToString().getBytes();
350
+ outboundAppData = new MiniSSLBuffer(bls);
358
351
 
359
- return getRuntime().newFixnum(bls.length);
360
- } catch (Exception e) {
361
- e.printStackTrace();
362
- throw new RuntimeException(e);
363
- }
352
+ return getRuntime().newFixnum(bls.length);
364
353
  }
365
354
 
366
355
  @JRubyMethod
367
- public IRubyObject extract() throws SSLException {
356
+ public IRubyObject extract(ThreadContext context) {
368
357
  try {
369
358
  ByteList dataByteList = outboundNetData.asByteList();
370
359
  if (dataByteList != null) {
371
- RubyString str = getRuntime().newString("");
372
- str.setValue(dataByteList);
373
- return str;
360
+ return RubyString.newString(context.runtime, dataByteList);
374
361
  }
375
362
 
376
363
  if (!outboundAppData.hasRemaining()) {
377
- return getRuntime().getNil();
364
+ return context.nil;
378
365
  }
379
366
 
380
367
  outboundNetData.clear();
381
368
  doOp(SSLOperation.WRAP, outboundAppData, outboundNetData);
382
369
  dataByteList = outboundNetData.asByteList();
383
370
  if (dataByteList == null) {
384
- return getRuntime().getNil();
371
+ return context.nil;
385
372
  }
386
373
 
387
- RubyString str = getRuntime().newString("");
388
- str.setValue(dataByteList);
389
-
390
- return str;
391
- } catch (Exception e) {
392
- e.printStackTrace();
393
- throw new RuntimeException(e);
374
+ return RubyString.newString(context.runtime, dataByteList);
375
+ } catch (SSLException e) {
376
+ RaiseException ex = context.runtime.newRuntimeError(e.toString());
377
+ ex.initCause(e);
378
+ throw ex;
394
379
  }
395
380
  }
396
381
 
@@ -398,7 +383,7 @@ public class MiniSSL extends RubyObject {
398
383
  public IRubyObject peercert() throws CertificateEncodingException {
399
384
  try {
400
385
  return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded());
401
- } catch (SSLPeerUnverifiedException ex) {
386
+ } catch (SSLPeerUnverifiedException e) {
402
387
  return getRuntime().getNil();
403
388
  }
404
389
  }
@@ -40,7 +40,9 @@ static VALUE global_http_version;
40
40
  static VALUE global_request_path;
41
41
 
42
42
  /** Defines common length and error messages for input length validation. */
43
- #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length (was %d)"
43
+ #define QUOTE(s) #s
44
+ #define EXPLAIN_MAX_LENGTH_VALUE(s) QUOTE(s)
45
+ #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " EXPLAIN_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
44
46
 
45
47
  /** Validates the max length of given input and throws an HttpParserError exception if over. */
46
48
  #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
@@ -50,12 +52,16 @@ static VALUE global_request_path;
50
52
 
51
53
 
52
54
  /* Defines the maximum allowed lengths for various input elements.*/
55
+ #ifndef PUMA_QUERY_STRING_MAX_LENGTH
56
+ #define PUMA_QUERY_STRING_MAX_LENGTH (1024 * 10)
57
+ #endif
58
+
53
59
  DEF_MAX_LENGTH(FIELD_NAME, 256);
54
60
  DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
55
61
  DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
56
62
  DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
57
63
  DEF_MAX_LENGTH(REQUEST_PATH, 8192);
58
- DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
64
+ DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
59
65
  DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
60
66
 
61
67
  struct common_field {
@@ -445,7 +451,7 @@ VALUE HttpParser_body(VALUE self) {
445
451
  void Init_mini_ssl(VALUE mod);
446
452
  #endif
447
453
 
448
- void Init_puma_http11()
454
+ void Init_puma_http11(void)
449
455
  {
450
456
 
451
457
  VALUE mPuma = rb_define_module("Puma");
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'puma/json_serialization'
2
3
 
3
4
  module Puma
4
5
  module App
@@ -22,10 +23,6 @@ module Puma
22
23
  return rack_response(403, 'Invalid auth token', 'text/plain')
23
24
  end
24
25
 
25
- if env['PATH_INFO'] =~ /\/(gc-stats|stats|thread-backtraces)$/
26
- require 'json'
27
- end
28
-
29
26
  # resp_type is processed by following case statement, return
30
27
  # is a number (status) or a string used as the body of a 200 response
31
28
  resp_type =
@@ -49,17 +46,17 @@ module Puma
49
46
  GC.start ; 200
50
47
 
51
48
  when 'gc-stats'
52
- GC.stat.to_json
49
+ Puma::JSONSerialization.generate GC.stat
53
50
 
54
51
  when 'stats'
55
- @launcher.stats.to_json
52
+ Puma::JSONSerialization.generate @launcher.stats
56
53
 
57
54
  when 'thread-backtraces'
58
55
  backtraces = []
59
56
  @launcher.thread_status do |name, backtrace|
60
57
  backtraces << { name: name, backtrace: backtrace }
61
58
  end
62
- backtraces.to_json
59
+ Puma::JSONSerialization.generate backtraces
63
60
 
64
61
  else
65
62
  return rack_response(404, "Unsupported action", 'text/plain')
data/lib/puma/binder.rb CHANGED
@@ -13,7 +13,7 @@ module Puma
13
13
  require 'puma/minissl'
14
14
  require 'puma/minissl/context_builder'
15
15
 
16
- # Odd bug in 'pure Ruby' nio4r verion 2.5.2, which installs with Ruby 2.3.
16
+ # Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
17
17
  # NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
18
18
  # The bug was that it did not require openssl.
19
19
  # @todo remove when Ruby 2.3 support is dropped
@@ -30,6 +30,7 @@ module Puma
30
30
 
31
31
  def initialize(events, conf = Configuration.new)
32
32
  @events = events
33
+ @conf = conf
33
34
  @listeners = []
34
35
  @inherited_fds = {}
35
36
  @activated_sockets = {}
@@ -41,6 +42,7 @@ module Puma
41
42
  "rack.multithread".freeze => conf.options[:max_threads] > 1,
42
43
  "rack.multiprocess".freeze => conf.options[:workers] >= 1,
43
44
  "rack.run_once".freeze => false,
45
+ RACK_URL_SCHEME => conf.options[:rack_url_scheme],
44
46
  "SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
45
47
 
46
48
  # I'd like to set a default CONTENT_TYPE here but some things
@@ -56,6 +58,7 @@ module Puma
56
58
 
57
59
  @envs = {}
58
60
  @ios = []
61
+ localhost_authority
59
62
  end
60
63
 
61
64
  attr_reader :ios
@@ -95,6 +98,7 @@ module Puma
95
98
  # @version 5.0.0
96
99
  #
97
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}"
98
102
  return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
99
103
  env_hash['LISTEN_FDS'].to_i.times do |index|
100
104
  sock = TCPServer.for_fd(socket_activation_fd(index))
@@ -111,6 +115,43 @@ module Puma
111
115
  ["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
112
116
  end
113
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}"
145
+ end
146
+ end
147
+
148
+ if only_matching
149
+ activated_binds
150
+ else
151
+ binds | activated_binds
152
+ end
153
+ end
154
+
114
155
  def parse(binds, logger, log_msg = 'Listening')
115
156
  binds.each do |str|
116
157
  uri = URI.parse str
@@ -123,21 +164,16 @@ module Puma
123
164
  io = inherit_tcp_listener uri.host, uri.port, sock
124
165
  logger.log "* Activated #{str}"
125
166
  else
167
+ ios_len = @ios.length
126
168
  params = Util.parse_query uri.query
127
169
 
128
- opt = params.key?('low_latency')
129
- bak = params.fetch('backlog', 1024).to_i
170
+ opt = params.key?('low_latency') && params['low_latency'] != 'false'
171
+ backlog = params.fetch('backlog', 1024).to_i
130
172
 
131
- io = add_tcp_listener uri.host, uri.port, opt, bak
132
-
133
- @ios.each do |i|
134
- next unless TCPServer === i
135
- addr = if i.local_address.ipv6?
136
- "[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
137
- else
138
- i.local_address.ip_unpack.join(':')
139
- end
173
+ io = add_tcp_listener uri.host, uri.port, opt, backlog
140
174
 
175
+ @ios[ios_len..-1].each do |i|
176
+ addr = loc_addr_str i
141
177
  logger.log "* #{log_msg} on http://#{addr}"
142
178
  end
143
179
  end
@@ -145,11 +181,20 @@ module Puma
145
181
  @listeners << [str, io] if io
146
182
  when "unix"
147
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
148
190
 
149
191
  if fd = @inherited_fds.delete(str)
192
+ @unix_paths << path unless abstract
150
193
  io = inherit_unix_listener path, fd
151
194
  logger.log "* Inherited #{str}"
152
- 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)
153
198
  io = inherit_unix_listener path, sock
154
199
  logger.log "* Activated #{str}"
155
200
  else
@@ -173,6 +218,7 @@ module Puma
173
218
  end
174
219
  end
175
220
 
221
+ @unix_paths << path unless abstract || File.exist?(path)
176
222
  io = add_unix_listener path, umask, mode, backlog
177
223
  logger.log "* #{log_msg} on #{str}"
178
224
  end
@@ -183,7 +229,25 @@ module Puma
183
229
  raise "Puma compiled without SSL support" unless HAS_SSL
184
230
 
185
231
  params = Util.parse_query uri.query
186
- 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
187
251
 
188
252
  if fd = @inherited_fds.delete(str)
189
253
  logger.log "* Inherited #{str}"
@@ -192,8 +256,14 @@ module Puma
192
256
  io = inherit_ssl_listener sock, ctx
193
257
  logger.log "* Activated #{str}"
194
258
  else
195
- io = add_ssl_listener uri.host, uri.port, ctx
196
- 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
197
267
  end
198
268
 
199
269
  @listeners << [str, io] if io
@@ -221,17 +291,37 @@ module Puma
221
291
  end
222
292
 
223
293
  # Also close any unused activated sockets
224
- @activated_sockets.each do |key, sock|
225
- logger.log "* Closing unused activated socket: #{key.join ':'}"
226
- begin
227
- sock.close
228
- 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
229
305
  end
230
- # We have to unlink a unix socket path that's not being used
231
- File.unlink key[1] if key[0] == :unix
232
306
  end
233
307
  end
234
308
 
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
323
+ end
324
+
235
325
  # Tell the server to listen on host +host+, port +port+.
236
326
  # If +optimize_for_latency+ is true (the default) then clients connecting
237
327
  # will be optimized for latency over throughput.
@@ -249,6 +339,7 @@ module Puma
249
339
 
250
340
  host = host[1..-2] if host and host[0..0] == '['
251
341
  tcp_server = TCPServer.new(host, port)
342
+
252
343
  if optimize_for_latency
253
344
  tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
254
345
  end
@@ -260,11 +351,7 @@ module Puma
260
351
  end
261
352
 
262
353
  def inherit_tcp_listener(host, port, fd)
263
- if fd.kind_of? TCPServer
264
- s = fd
265
- else
266
- s = TCPServer.for_fd(fd)
267
- end
354
+ s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
268
355
 
269
356
  @ios << s
270
357
  s
@@ -274,6 +361,8 @@ module Puma
274
361
  optimize_for_latency=true, backlog=1024)
275
362
 
276
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
277
366
 
278
367
  if host == "localhost"
279
368
  loopback_addresses.each do |addr|
@@ -301,12 +390,11 @@ module Puma
301
390
 
302
391
  def inherit_ssl_listener(fd, ctx)
303
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)
304
397
 
305
- if fd.kind_of? TCPServer
306
- s = fd
307
- else
308
- s = TCPServer.for_fd(fd)
309
- end
310
398
  ssl = MiniSSL::Server.new(s, ctx)
311
399
 
312
400
  env = @proto_env.dup
@@ -321,8 +409,6 @@ module Puma
321
409
  # Tell the server to listen on +path+ as a UNIX domain socket.
322
410
  #
323
411
  def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
324
- @unix_paths << path unless File.exist? path
325
-
326
412
  # Let anyone connect by default
327
413
  umask ||= 0
328
414
 
@@ -339,8 +425,7 @@ module Puma
339
425
  raise "There is already a server bound to: #{path}"
340
426
  end
341
427
  end
342
-
343
- s = UNIXServer.new(path)
428
+ s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
344
429
  s.listen backlog
345
430
  @ios << s
346
431
  ensure
@@ -359,13 +444,8 @@ module Puma
359
444
  end
360
445
 
361
446
  def inherit_unix_listener(path, fd)
362
- @unix_paths << path unless File.exist? path
447
+ s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
363
448
 
364
- if fd.kind_of? TCPServer
365
- s = fd
366
- else
367
- s = UNIXServer.for_fd fd
368
- end
369
449
  @ios << s
370
450
 
371
451
  env = @proto_env.dup
@@ -376,24 +456,24 @@ module Puma
376
456
  end
377
457
 
378
458
  def close_listeners
379
- listeners.each do |l, io|
380
- io.close unless io.closed? # Ruby 2.2 issue
381
- uri = URI.parse(l)
459
+ @listeners.each do |l, io|
460
+ io.close unless io.closed?
461
+ uri = URI.parse l
382
462
  next unless uri.scheme == 'unix'
383
463
  unix_path = "#{uri.host}#{uri.path}"
384
- 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)
385
465
  end
386
466
  end
387
467
 
388
468
  def redirects_for_restart
389
- redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
469
+ redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
390
470
  redirects[:close_others] = true
391
471
  redirects
392
472
  end
393
473
 
394
474
  # @version 5.0.0
395
475
  def redirects_for_restart_env
396
- listeners.each_with_object({}).with_index do |(listen, memo), i|
476
+ @listeners.each_with_object({}).with_index do |(listen, memo), i|
397
477
  memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
398
478
  end
399
479
  end
@@ -407,6 +487,15 @@ module Puma
407
487
  end.map { |addrinfo| addrinfo.ip_address }.uniq
408
488
  end
409
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
+
410
499
  # @version 5.0.0
411
500
  def socket_activation_fd(int)
412
501
  int + 3 # 3 is the magic number you add to follow the SA protocol
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
@@ -104,10 +105,19 @@ 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
 
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 "-"
119
+ end
120
+
111
121
  o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
112
122
  configure_control_url(arg)
113
123
  end
@@ -175,6 +185,10 @@ module Puma
175
185
  user_config.restart_command cmd
176
186
  end
177
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
+
178
192
  o.on "-S", "--state PATH", "Where to store the state details" do |arg|
179
193
  user_config.state_path arg
180
194
  end