puma 5.5.1-java → 5.6.2-java

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +67 -0
  3. data/LICENSE +0 -0
  4. data/README.md +5 -0
  5. data/bin/puma-wild +0 -0
  6. data/docs/architecture.md +4 -4
  7. data/docs/compile_options.md +0 -0
  8. data/docs/deployment.md +0 -0
  9. data/docs/fork_worker.md +0 -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 +0 -0
  15. data/docs/jungle/rc.d/puma.conf +0 -0
  16. data/docs/kubernetes.md +0 -0
  17. data/docs/nginx.md +0 -0
  18. data/docs/plugins.md +0 -0
  19. data/docs/rails_dev_mode.md +0 -0
  20. data/docs/restart.md +0 -0
  21. data/docs/signals.md +1 -0
  22. data/docs/stats.md +0 -0
  23. data/docs/systemd.md +0 -0
  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 +12 -6
  27. data/ext/puma_http11/http11_parser.c +22 -16
  28. data/ext/puma_http11/http11_parser.h +0 -0
  29. data/ext/puma_http11/http11_parser.java.rl +0 -0
  30. data/ext/puma_http11/http11_parser.rl +0 -0
  31. data/ext/puma_http11/http11_parser_common.rl +1 -1
  32. data/ext/puma_http11/mini_ssl.c +54 -9
  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 +49 -47
  36. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +28 -43
  37. data/ext/puma_http11/puma_http11.c +1 -1
  38. data/lib/puma/app/status.rb +0 -0
  39. data/lib/puma/binder.rb +19 -5
  40. data/lib/puma/cli.rb +9 -4
  41. data/lib/puma/client.rb +1 -1
  42. data/lib/puma/cluster/worker.rb +5 -5
  43. data/lib/puma/cluster/worker_handle.rb +4 -0
  44. data/lib/puma/cluster.rb +29 -11
  45. data/lib/puma/commonlogger.rb +0 -0
  46. data/lib/puma/configuration.rb +3 -0
  47. data/lib/puma/const.rb +2 -5
  48. data/lib/puma/control_cli.rb +0 -0
  49. data/lib/puma/detect.rb +8 -2
  50. data/lib/puma/dsl.rb +85 -8
  51. data/lib/puma/error_logger.rb +0 -0
  52. data/lib/puma/events.rb +0 -0
  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 +0 -0
  56. data/lib/puma/launcher.rb +2 -1
  57. data/lib/puma/minissl/context_builder.rb +8 -6
  58. data/lib/puma/minissl.rb +18 -2
  59. data/lib/puma/null_io.rb +0 -0
  60. data/lib/puma/plugin/tmp_restart.rb +0 -0
  61. data/lib/puma/plugin.rb +1 -1
  62. data/lib/puma/puma_http11.jar +0 -0
  63. data/lib/puma/queue_close.rb +0 -0
  64. data/lib/puma/rack/builder.rb +0 -0
  65. data/lib/puma/rack/urlmap.rb +0 -0
  66. data/lib/puma/rack_default.rb +0 -0
  67. data/lib/puma/reactor.rb +0 -0
  68. data/lib/puma/request.rb +10 -5
  69. data/lib/puma/runner.rb +3 -2
  70. data/lib/puma/server.rb +18 -24
  71. data/lib/puma/single.rb +0 -0
  72. data/lib/puma/state_file.rb +41 -7
  73. data/lib/puma/systemd.rb +0 -0
  74. data/lib/puma/thread_pool.rb +2 -2
  75. data/lib/puma/util.rb +0 -0
  76. data/lib/puma.rb +0 -0
  77. data/lib/rack/handler/puma.rb +0 -0
  78. data/tools/Dockerfile +1 -1
  79. data/tools/trickletest.rb +0 -0
  80. metadata +3 -3
@@ -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
  }
@@ -451,7 +451,7 @@ VALUE HttpParser_body(VALUE self) {
451
451
  void Init_mini_ssl(VALUE mod);
452
452
  #endif
453
453
 
454
- void Init_puma_http11()
454
+ void Init_puma_http11(void)
455
455
  {
456
456
 
457
457
  VALUE mPuma = rb_define_module("Puma");
File without changes
data/lib/puma/binder.rb CHANGED
@@ -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 = {}
@@ -167,9 +168,9 @@ module Puma
167
168
  params = Util.parse_query uri.query
168
169
 
169
170
  opt = params.key?('low_latency') && params['low_latency'] != 'false'
170
- bak = params.fetch('backlog', 1024).to_i
171
+ backlog = params.fetch('backlog', 1024).to_i
171
172
 
172
- io = add_tcp_listener uri.host, uri.port, opt, bak
173
+ io = add_tcp_listener uri.host, uri.port, opt, backlog
173
174
 
174
175
  @ios[ios_len..-1].each do |i|
175
176
  addr = loc_addr_str i
@@ -232,9 +233,21 @@ module Puma
232
233
  # If key and certs are not defined and localhost gem is required.
233
234
  # localhost gem will be used for self signed
234
235
  # Load localhost authority if not loaded.
235
- ctx = localhost_authority && localhost_authority_context if params.empty?
236
+ if params.values_at('cert', 'key').all? { |v| v.to_s.empty? }
237
+ ctx = localhost_authority && localhost_authority_context
238
+ end
236
239
 
237
- ctx ||= MiniSSL::ContextBuilder.new(params, @events).context
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
238
251
 
239
252
  if fd = @inherited_fds.delete(str)
240
253
  logger.log "* Inherited #{str}"
@@ -244,7 +257,8 @@ module Puma
244
257
  logger.log "* Activated #{str}"
245
258
  else
246
259
  ios_len = @ios.length
247
- io = add_ssl_listener uri.host, uri.port, ctx
260
+ backlog = params.fetch('backlog', 1024).to_i
261
+ io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
248
262
 
249
263
  @ios[ios_len..-1].each do |i|
250
264
  addr = loc_addr_str i
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
@@ -184,6 +185,10 @@ module Puma
184
185
  user_config.restart_command cmd
185
186
  end
186
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
+
187
192
  o.on "-S", "--state PATH", "Where to store the state details" do |arg|
188
193
  user_config.state_path arg
189
194
  end
data/lib/puma/client.rb CHANGED
@@ -161,7 +161,7 @@ module Puma
161
161
  def close
162
162
  begin
163
163
  @io.close
164
- rescue IOError
164
+ rescue IOError, Errno::EBADF
165
165
  Puma::Util.purge_interrupt_queue
166
166
  end
167
167
  end
@@ -33,8 +33,8 @@ module Puma
33
33
  Signal.trap "SIGINT", "IGNORE"
34
34
  Signal.trap "SIGCHLD", "DEFAULT"
35
35
 
36
- Thread.new do
37
- Puma.set_thread_name "worker check pipe"
36
+ Thread.new do
37
+ Puma.set_thread_name "wrkr check"
38
38
  @check_pipe.wait_readable
39
39
  log "! Detected parent died, dying"
40
40
  exit! 1
@@ -76,7 +76,7 @@ module Puma
76
76
  end
77
77
 
78
78
  Thread.new do
79
- Puma.set_thread_name "worker fork pipe"
79
+ Puma.set_thread_name "wrkr fork"
80
80
  while (idx = @fork_pipe.gets)
81
81
  idx = idx.to_i
82
82
  if idx == -1 # stop server
@@ -114,7 +114,7 @@ module Puma
114
114
  while restart_server.pop
115
115
  server_thread = server.run
116
116
  stat_thread ||= Thread.new(@worker_write) do |io|
117
- Puma.set_thread_name "stat payload"
117
+ Puma.set_thread_name "stat pld"
118
118
  base_payload = "p#{Process.pid}"
119
119
 
120
120
  while true
@@ -130,7 +130,7 @@ module Puma
130
130
  Puma::Util.purge_interrupt_queue
131
131
  break
132
132
  end
133
- sleep Const::WORKER_CHECK_INTERVAL
133
+ sleep @options[:worker_check_interval]
134
134
  end
135
135
  end
136
136
  server_thread.join
@@ -40,6 +40,10 @@ module Puma
40
40
  @stage = :booted
41
41
  end
42
42
 
43
+ def term!
44
+ @term = true
45
+ end
46
+
43
47
  def term?
44
48
  @term
45
49
  end
data/lib/puma/cluster.rb CHANGED
@@ -108,24 +108,42 @@ module Puma
108
108
  def cull_workers
109
109
  diff = @workers.size - @options[:workers]
110
110
  return if diff < 1
111
+ debug "Culling #{diff} workers"
111
112
 
112
- debug "Culling #{diff.inspect} workers"
113
+ workers = workers_to_cull(diff)
114
+ debug "Workers to cull: #{workers.inspect}"
113
115
 
114
- workers_to_cull = @workers[-diff,diff]
115
- debug "Workers to cull: #{workers_to_cull.inspect}"
116
-
117
- workers_to_cull.each do |worker|
116
+ workers.each do |worker|
118
117
  log "- Worker #{worker.index} (PID: #{worker.pid}) terminating"
119
118
  worker.term
120
119
  end
121
120
  end
122
121
 
122
+ def workers_to_cull(diff)
123
+ workers = @workers.sort_by(&:started_at)
124
+
125
+ # In fork_worker mode, worker 0 acts as our master process.
126
+ # We should avoid culling it to preserve copy-on-write memory gains.
127
+ workers.reject! { |w| w.index == 0 } if @options[:fork_worker]
128
+
129
+ workers[cull_start_index(diff), diff]
130
+ end
131
+
132
+ def cull_start_index(diff)
133
+ case @options[:worker_culling_strategy]
134
+ when :oldest
135
+ 0
136
+ else # :youngest
137
+ -diff
138
+ end
139
+ end
140
+
123
141
  # @!attribute [r] next_worker_index
124
142
  def next_worker_index
125
- all_positions = 0...@options[:workers]
126
- occupied_positions = @workers.map { |w| w.index }
127
- available_positions = all_positions.to_a - occupied_positions
128
- available_positions.first
143
+ occupied_positions = @workers.map(&:index)
144
+ idx = 0
145
+ idx += 1 until !occupied_positions.include?(idx)
146
+ idx
129
147
  end
130
148
 
131
149
  def all_workers_booted?
@@ -135,7 +153,7 @@ module Puma
135
153
  def check_workers
136
154
  return if @next_check >= Time.now
137
155
 
138
- @next_check = Time.now + Const::WORKER_CHECK_INTERVAL
156
+ @next_check = Time.now + @options[:worker_check_interval]
139
157
 
140
158
  timeout_workers
141
159
  wait_workers
@@ -440,7 +458,7 @@ module Puma
440
458
  workers_not_booted -= 1
441
459
  when "e"
442
460
  # external term, see worker method, Signal.trap "SIGTERM"
443
- w.instance_variable_set :@term, true
461
+ w.term!
444
462
  when "t"
445
463
  w.term unless w.term?
446
464
  when "p"
File without changes
@@ -11,6 +11,7 @@ module Puma
11
11
 
12
12
  DefaultTCPHost = "0.0.0.0"
13
13
  DefaultTCPPort = 9292
14
+ DefaultWorkerCheckInterval = 5
14
15
  DefaultWorkerTimeout = 60
15
16
  DefaultWorkerShutdownTimeout = 30
16
17
  end
@@ -195,9 +196,11 @@ module Puma
195
196
  :workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
196
197
  :silence_single_worker_warning => false,
197
198
  :mode => :http,
199
+ :worker_check_interval => DefaultWorkerCheckInterval,
198
200
  :worker_timeout => DefaultWorkerTimeout,
199
201
  :worker_boot_timeout => DefaultWorkerTimeout,
200
202
  :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
203
+ :worker_culling_strategy => :youngest,
201
204
  :remote_address => :socket,
202
205
  :tag => method(:infer_tag),
203
206
  :environment => -> { ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' },
data/lib/puma/const.rb CHANGED
@@ -100,8 +100,8 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "5.5.1".freeze
104
- CODE_NAME = "Zawgyi".freeze
103
+ PUMA_VERSION = VERSION = "5.6.2".freeze
104
+ CODE_NAME = "Birdie's Version".freeze
105
105
 
106
106
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
107
107
 
@@ -235,9 +235,6 @@ module Puma
235
235
 
236
236
  EARLY_HINTS = "rack.early_hints".freeze
237
237
 
238
- # Minimum interval to checks worker health
239
- WORKER_CHECK_INTERVAL = 5
240
-
241
238
  # Illegal character in the key or value of response header
242
239
  DQUOTE = "\"".freeze
243
240
  HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
File without changes
data/lib/puma/detect.rb CHANGED
@@ -10,8 +10,10 @@ module Puma
10
10
 
11
11
  IS_JRUBY = Object.const_defined? :JRUBY_VERSION
12
12
 
13
- IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/ ||
14
- IS_JRUBY && RUBY_DESCRIPTION =~ /mswin/)
13
+ IS_OSX = RUBY_PLATFORM.include? 'darwin'
14
+
15
+ IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/) ||
16
+ IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
15
17
 
16
18
  # @version 5.2.0
17
19
  IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
@@ -20,6 +22,10 @@ module Puma
20
22
  IS_JRUBY
21
23
  end
22
24
 
25
+ def self.osx?
26
+ IS_OSX
27
+ end
28
+
23
29
  def self.windows?
24
30
  IS_WINDOWS
25
31
  end
data/lib/puma/dsl.rb CHANGED
@@ -48,6 +48,8 @@ module Puma
48
48
 
49
49
  ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
50
50
 
51
+ backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
52
+
51
53
  if defined?(JRUBY_VERSION)
52
54
  ssl_cipher_list = opts[:ssl_cipher_list] ?
53
55
  "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil
@@ -55,7 +57,7 @@ module Puma
55
57
  keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
56
58
 
57
59
  "ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
58
- "&verify_mode=#{verify}#{tls_str}#{ca_additions}"
60
+ "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
59
61
  else
60
62
  ssl_cipher_filter = opts[:ssl_cipher_filter] ?
61
63
  "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
@@ -64,7 +66,7 @@ module Puma
64
66
  "&verification_flags=#{Array(ary).join ','}" : nil
65
67
 
66
68
  "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
67
- "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
69
+ "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
68
70
  end
69
71
  end
70
72
 
@@ -191,7 +193,7 @@ module Puma
191
193
  end
192
194
 
193
195
  # Bind the server to +url+. "tcp://", "unix://" and "ssl://" are the only
194
- # accepted protocols. Multiple urls can be bound to, calling `bind` does
196
+ # accepted protocols. Multiple urls can be bound to, calling +bind+ does
195
197
  # not overwrite previous bindings.
196
198
  #
197
199
  # The default is "tcp://0.0.0.0:9292".
@@ -436,8 +438,15 @@ module Puma
436
438
  @options[:max_threads] = max
437
439
  end
438
440
 
439
- # Instead of `bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'` you
440
- # can also use the this method.
441
+ # Instead of using +bind+ and manually constructing a URI like:
442
+ #
443
+ # bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'
444
+ #
445
+ # you can use the this method.
446
+ #
447
+ # When binding on localhost you don't need to specify +cert+ and +key+,
448
+ # Puma will assume you are using the +localhost+ gem and try to load the
449
+ # appropriate files.
441
450
  #
442
451
  # @example
443
452
  # ssl_bind '127.0.0.1', '9292', {
@@ -447,14 +456,25 @@ module Puma
447
456
  # verify_mode: verify_mode, # default 'none'
448
457
  # verification_flags: flags, # optional, not supported by JRuby
449
458
  # }
450
- # @example For JRuby, two keys are required: keystore & keystore_pass.
459
+ #
460
+ # @example Using self-signed certificate with the +localhost+ gem:
461
+ # ssl_bind '127.0.0.1', '9292'
462
+ #
463
+ # @example Alternatively, you can provide +cert_pem+ and +key_pem+:
464
+ # ssl_bind '127.0.0.1', '9292', {
465
+ # cert_pem: File.read(path_to_cert),
466
+ # key_pem: File.read(path_to_key),
467
+ # }
468
+ #
469
+ # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
451
470
  # ssl_bind '127.0.0.1', '9292', {
452
471
  # keystore: path_to_keystore,
453
472
  # keystore_pass: password,
454
473
  # ssl_cipher_list: cipher_list, # optional
455
474
  # verify_mode: verify_mode # default 'none'
456
475
  # }
457
- def ssl_bind(host, port, opts)
476
+ def ssl_bind(host, port, opts = {})
477
+ add_pem_values_to_options_store(opts)
458
478
  bind self.class.ssl_bind_str(host, port, opts)
459
479
  end
460
480
 
@@ -727,6 +747,19 @@ module Puma
727
747
  @options[:tag] = string.to_s
728
748
  end
729
749
 
750
+ # Change the default interval for checking workers.
751
+ #
752
+ # The default value is 5 seconds.
753
+ #
754
+ # @note Cluster mode only.
755
+ # @example
756
+ # worker_check_interval 5
757
+ # @see Puma::Cluster#check_workers
758
+ #
759
+ def worker_check_interval(interval)
760
+ @options[:worker_check_interval] = Integer(interval)
761
+ end
762
+
730
763
  # Verifies that all workers have checked in to the master process within
731
764
  # the given timeout. If not the worker process will be restarted. This is
732
765
  # not a request timeout, it is to protect against a hung or dead process.
@@ -741,7 +774,7 @@ module Puma
741
774
  #
742
775
  def worker_timeout(timeout)
743
776
  timeout = Integer(timeout)
744
- min = Const::WORKER_CHECK_INTERVAL
777
+ min = @options.fetch(:worker_check_interval, Puma::ConfigDefault::DefaultWorkerCheckInterval)
745
778
 
746
779
  if timeout <= min
747
780
  raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
@@ -773,6 +806,30 @@ module Puma
773
806
  @options[:worker_shutdown_timeout] = Integer(timeout)
774
807
  end
775
808
 
809
+ # Set the strategy for worker culling.
810
+ #
811
+ # There are two possible values:
812
+ #
813
+ # 1. **:youngest** - the youngest workers (i.e. the workers that were
814
+ # the most recently started) will be culled.
815
+ # 2. **:oldest** - the oldest workers (i.e. the workers that were started
816
+ # the longest time ago) will be culled.
817
+ #
818
+ # @note Cluster mode only.
819
+ # @example
820
+ # worker_culling_strategy :oldest
821
+ # @see Puma::Cluster#cull_workers
822
+ #
823
+ def worker_culling_strategy(strategy)
824
+ stategy = strategy.to_sym
825
+
826
+ if ![:youngest, :oldest].include?(strategy)
827
+ raise "Invalid value for worker_culling_strategy - #{stategy}"
828
+ end
829
+
830
+ @options[:worker_culling_strategy] = strategy
831
+ end
832
+
776
833
  # When set to true (the default), workers accept all requests
777
834
  # and queue them before passing them to the handlers.
778
835
  # When set to false, each worker process accepts exactly as
@@ -927,5 +984,25 @@ module Puma
927
984
  def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
928
985
  @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
929
986
  end
987
+
988
+ private
989
+
990
+ # To avoid adding cert_pem and key_pem as URI params, we store them on the
991
+ # options[:store] from where Puma binder knows how to find and extract them.
992
+ def add_pem_values_to_options_store(opts)
993
+ return if defined?(JRUBY_VERSION)
994
+
995
+ @options[:store] ||= []
996
+
997
+ # Store cert_pem and key_pem to options[:store] if present
998
+ [:cert, :key].each do |v|
999
+ opt_key = :"#{v}_pem"
1000
+ if opts[opt_key]
1001
+ index = @options[:store].length
1002
+ @options[:store] << opts[opt_key]
1003
+ opts[v] = "store:#{index}"
1004
+ end
1005
+ end
1006
+ end
930
1007
  end
931
1008
  end
File without changes
data/lib/puma/events.rb CHANGED
File without changes
File without changes
File without changes
File without changes
data/lib/puma/launcher.rb CHANGED
@@ -15,6 +15,7 @@ module Puma
15
15
  # It is responsible for either launching a cluster of Puma workers or a single
16
16
  # puma server.
17
17
  class Launcher
18
+ # @deprecated 6.0.0
18
19
  KEYS_NOT_TO_PERSIST_IN_STATE = [
19
20
  :logger, :lowlevel_error_handler,
20
21
  :before_worker_shutdown, :before_worker_boot, :before_worker_fork,
@@ -73,7 +74,7 @@ module Puma
73
74
 
74
75
  generate_restart_data
75
76
 
76
- if clustered? && !Process.respond_to?(:fork)
77
+ if clustered? && !Puma.forkable?
77
78
  unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
78
79
  end
79
80
 
@@ -23,17 +23,19 @@ module Puma
23
23
  ctx.keystore_pass = params['keystore-pass']
24
24
  ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
25
25
  else
26
- unless params['key']
27
- events.error "Please specify the SSL key via 'key='"
26
+ if params['key'].nil? && params['key_pem'].nil?
27
+ events.error "Please specify the SSL key via 'key=' or 'key_pem='"
28
28
  end
29
29
 
30
- ctx.key = params['key']
30
+ ctx.key = params['key'] if params['key']
31
+ ctx.key_pem = params['key_pem'] if params['key_pem']
31
32
 
32
- unless params['cert']
33
- events.error "Please specify the SSL cert via 'cert='"
33
+ if params['cert'].nil? && params['cert_pem'].nil?
34
+ events.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
34
35
  end
35
36
 
36
- ctx.cert = params['cert']
37
+ ctx.cert = params['cert'] if params['cert']
38
+ ctx.cert_pem = params['cert_pem'] if params['cert_pem']
37
39
 
38
40
  if ['peer', 'force_peer'].include?(params['verify_mode'])
39
41
  unless params['ca']