puma 3.12.1 → 5.6.4

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1553 -447
  3. data/LICENSE +23 -20
  4. data/README.md +175 -63
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +59 -21
  7. data/docs/compile_options.md +21 -0
  8. data/docs/deployment.md +69 -58
  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 +22 -12
  20. data/docs/rails_dev_mode.md +28 -0
  21. data/docs/restart.md +47 -22
  22. data/docs/signals.md +13 -11
  23. data/docs/stats.md +142 -0
  24. data/docs/systemd.md +95 -120
  25. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  26. data/ext/puma_http11/ext_help.h +1 -1
  27. data/ext/puma_http11/extconf.rb +51 -1
  28. data/ext/puma_http11/http11_parser.c +105 -117
  29. data/ext/puma_http11/http11_parser.h +1 -1
  30. data/ext/puma_http11/http11_parser.java.rl +22 -38
  31. data/ext/puma_http11/http11_parser.rl +4 -2
  32. data/ext/puma_http11/http11_parser_common.rl +4 -4
  33. data/ext/puma_http11/mini_ssl.c +319 -96
  34. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  35. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  36. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
  37. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +120 -65
  38. data/ext/puma_http11/puma_http11.c +35 -51
  39. data/lib/puma/app/status.rb +68 -49
  40. data/lib/puma/binder.rb +234 -137
  41. data/lib/puma/cli.rb +28 -18
  42. data/lib/puma/client.rb +343 -230
  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 +247 -232
  46. data/lib/puma/commonlogger.rb +2 -2
  47. data/lib/puma/configuration.rb +61 -51
  48. data/lib/puma/const.rb +42 -21
  49. data/lib/puma/control_cli.rb +109 -67
  50. data/lib/puma/detect.rb +29 -2
  51. data/lib/puma/dsl.rb +615 -123
  52. data/lib/puma/error_logger.rb +104 -0
  53. data/lib/puma/events.rb +55 -31
  54. data/lib/puma/io_buffer.rb +7 -5
  55. data/lib/puma/jruby_restart.rb +0 -58
  56. data/lib/puma/json_serialization.rb +96 -0
  57. data/lib/puma/launcher.rb +182 -69
  58. data/lib/puma/minissl/context_builder.rb +81 -0
  59. data/lib/puma/minissl.rb +161 -61
  60. data/lib/puma/null_io.rb +13 -1
  61. data/lib/puma/plugin/tmp_restart.rb +2 -0
  62. data/lib/puma/plugin.rb +7 -13
  63. data/lib/puma/queue_close.rb +26 -0
  64. data/lib/puma/rack/builder.rb +3 -5
  65. data/lib/puma/rack/urlmap.rb +2 -0
  66. data/lib/puma/rack_default.rb +2 -0
  67. data/lib/puma/reactor.rb +85 -316
  68. data/lib/puma/request.rb +472 -0
  69. data/lib/puma/runner.rb +48 -55
  70. data/lib/puma/server.rb +303 -695
  71. data/lib/puma/single.rb +11 -67
  72. data/lib/puma/state_file.rb +47 -8
  73. data/lib/puma/systemd.rb +46 -0
  74. data/lib/puma/thread_pool.rb +132 -82
  75. data/lib/puma/util.rb +21 -7
  76. data/lib/puma.rb +54 -0
  77. data/lib/rack/handler/puma.rb +5 -6
  78. data/tools/Dockerfile +16 -0
  79. data/tools/trickletest.rb +0 -1
  80. metadata +45 -29
  81. data/ext/puma_http11/io_buffer.c +0 -155
  82. data/lib/puma/accept_nonblock.rb +0 -23
  83. data/lib/puma/compat.rb +0 -14
  84. data/lib/puma/convenient.rb +0 -25
  85. data/lib/puma/daemon_ext.rb +0 -33
  86. data/lib/puma/delegation.rb +0 -13
  87. data/lib/puma/java_io_buffer.rb +0 -47
  88. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  89. data/lib/puma/tcp_logger.rb +0 -41
  90. data/tools/jungle/README.md +0 -19
  91. data/tools/jungle/init.d/README.md +0 -61
  92. data/tools/jungle/init.d/puma +0 -421
  93. data/tools/jungle/init.d/run-puma +0 -18
  94. data/tools/jungle/upstart/README.md +0 -61
  95. data/tools/jungle/upstart/puma-manager.conf +0 -31
  96. data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/launcher.rb CHANGED
@@ -2,12 +2,9 @@
2
2
 
3
3
  require 'puma/events'
4
4
  require 'puma/detect'
5
-
6
5
  require 'puma/cluster'
7
6
  require 'puma/single'
8
-
9
7
  require 'puma/const'
10
-
11
8
  require 'puma/binder'
12
9
 
13
10
  module Puma
@@ -18,6 +15,7 @@ module Puma
18
15
  # It is responsible for either launching a cluster of Puma workers or a single
19
16
  # puma server.
20
17
  class Launcher
18
+ # @deprecated 6.0.0
21
19
  KEYS_NOT_TO_PERSIST_IN_STATE = [
22
20
  :logger, :lowlevel_error_handler,
23
21
  :before_worker_shutdown, :before_worker_boot, :before_worker_fork,
@@ -50,8 +48,9 @@ module Puma
50
48
  @original_argv = @argv.dup
51
49
  @config = conf
52
50
 
53
- @binder = Binder.new(@events)
54
- @binder.import_from_env
51
+ @binder = Binder.new(@events, conf)
52
+ @binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
53
+ @binder.create_activated_fds(ENV).each { |k| ENV.delete k }
55
54
 
56
55
  @environment = conf.environment
57
56
 
@@ -60,19 +59,25 @@ module Puma
60
59
 
61
60
  @config.load
62
61
 
62
+ if @config.options[:bind_to_activated_sockets]
63
+ @config.options[:binds] = @binder.synthesize_binds_from_activated_fs(
64
+ @config.options[:binds],
65
+ @config.options[:bind_to_activated_sockets] == 'only'
66
+ )
67
+ end
68
+
63
69
  @options = @config.options
64
70
  @config.clamp
65
71
 
72
+ @events.formatter = Events::PidFormatter.new if clustered?
73
+ @events.formatter = options[:log_formatter] if @options[:log_formatter]
74
+
66
75
  generate_restart_data
67
76
 
68
- if clustered? && !Process.respond_to?(:fork)
77
+ if clustered? && !Puma.forkable?
69
78
  unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
70
79
  end
71
80
 
72
- if @options[:daemon] && Puma.windows?
73
- unsupported 'daemon mode not supported on Windows'
74
- end
75
-
76
81
  Dir.chdir(@restart_dir)
77
82
 
78
83
  prune_bundler if prune_bundler?
@@ -81,7 +86,6 @@ module Puma
81
86
  set_rack_environment
82
87
 
83
88
  if clustered?
84
- @events.formatter = Events::PidFormatter.new
85
89
  @options[:logger] = @events
86
90
 
87
91
  @runner = Cluster.new(self, @events)
@@ -91,6 +95,8 @@ module Puma
91
95
  Puma.stats_object = @runner
92
96
 
93
97
  @status = :run
98
+
99
+ log_config if ENV['PUMA_LOG_CONFIG']
94
100
  end
95
101
 
96
102
  attr_reader :binder, :events, :config, :options, :restart_dir
@@ -106,6 +112,7 @@ module Puma
106
112
  write_pid
107
113
 
108
114
  path = @options[:state]
115
+ permission = @options[:state_permission]
109
116
  return unless path
110
117
 
111
118
  require 'puma/state_file'
@@ -114,8 +121,9 @@ module Puma
114
121
  sf.pid = Process.pid
115
122
  sf.control_url = @options[:control_url]
116
123
  sf.control_auth_token = @options[:control_auth_token]
124
+ sf.running_from = File.expand_path('.')
117
125
 
118
- sf.save path
126
+ sf.save path, permission
119
127
  end
120
128
 
121
129
  # Delete the configured pidfile
@@ -124,19 +132,6 @@ module Puma
124
132
  File.unlink(path) if path && File.exist?(path)
125
133
  end
126
134
 
127
- # If configured, write the pid of the current process out
128
- # to a file.
129
- def write_pid
130
- path = @options[:pidfile]
131
- return unless path
132
-
133
- File.open(path, 'w') { |f| f.puts Process.pid }
134
- cur = Process.pid
135
- at_exit do
136
- delete_pidfile if cur == Process.pid
137
- end
138
- end
139
-
140
135
  # Begin async shutdown of the server
141
136
  def halt
142
137
  @status = :halt
@@ -183,28 +178,34 @@ module Puma
183
178
 
184
179
  setup_signals
185
180
  set_process_title
181
+ integrate_with_systemd
186
182
  @runner.run
187
183
 
188
184
  case @status
189
185
  when :halt
190
186
  log "* Stopping immediately!"
187
+ @runner.stop_control
191
188
  when :run, :stop
192
189
  graceful_stop
193
190
  when :restart
194
191
  log "* Restarting..."
195
192
  ENV.replace(previous_env)
196
- @runner.before_restart
193
+ @runner.stop_control
197
194
  restart!
198
195
  when :exit
199
196
  # nothing
200
197
  end
198
+ close_binder_listeners unless @status == :restart
201
199
  end
202
200
 
203
- # Return which tcp port the launcher is using, if it's using TCP
204
- def connected_port
205
- @binder.connected_port
201
+ # Return all tcp ports the launcher may be using, TCP or SSL
202
+ # @!attribute [r] connected_ports
203
+ # @version 5.0.0
204
+ def connected_ports
205
+ @binder.connected_ports
206
206
  end
207
207
 
208
+ # @!attribute [r] restart_args
208
209
  def restart_args
209
210
  cmd = @options[:restart_cmd]
210
211
  if cmd
@@ -214,14 +215,49 @@ module Puma
214
215
  end
215
216
  end
216
217
 
218
+ def close_binder_listeners
219
+ @runner.close_control_listeners
220
+ @binder.close_listeners
221
+ unless @status == :restart
222
+ log "=== puma shutdown: #{Time.now} ==="
223
+ log "- Goodbye!"
224
+ end
225
+ end
226
+
227
+ # @!attribute [r] thread_status
228
+ # @version 5.0.0
229
+ def thread_status
230
+ Thread.list.each do |thread|
231
+ name = "Thread: TID-#{thread.object_id.to_s(36)}"
232
+ name += " #{thread['label']}" if thread['label']
233
+ name += " #{thread.name}" if thread.respond_to?(:name) && thread.name
234
+ backtrace = thread.backtrace || ["<no backtrace available>"]
235
+
236
+ yield name, backtrace
237
+ end
238
+ end
239
+
217
240
  private
218
241
 
242
+ # If configured, write the pid of the current process out
243
+ # to a file.
244
+ def write_pid
245
+ path = @options[:pidfile]
246
+ return unless path
247
+ cur_pid = Process.pid
248
+ File.write path, cur_pid, mode: 'wb:UTF-8'
249
+ at_exit do
250
+ delete_pidfile if cur_pid == Process.pid
251
+ end
252
+ end
253
+
219
254
  def reload_worker_directory
220
255
  @runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
221
256
  end
222
257
 
223
258
  def restart!
224
- @config.run_hooks :on_restart, self
259
+ @events.fire_on_restart!
260
+ @config.run_hooks :on_restart, self, @events
225
261
 
226
262
  if Puma.jruby?
227
263
  close_binder_listeners
@@ -235,48 +271,100 @@ module Puma
235
271
  Dir.chdir(@restart_dir)
236
272
  Kernel.exec(*argv)
237
273
  else
238
- redirects = {:close_others => true}
239
- @binder.listeners.each_with_index do |(l, io), i|
240
- ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
241
- redirects[io.to_i] = io.to_i
242
- end
243
-
244
274
  argv = restart_args
245
275
  Dir.chdir(@restart_dir)
246
- argv += [redirects] if RUBY_VERSION >= '1.9'
276
+ ENV.update(@binder.redirects_for_restart_env)
277
+ argv += [@binder.redirects_for_restart]
247
278
  Kernel.exec(*argv)
248
279
  end
249
280
  end
250
281
 
251
- def prune_bundler
252
- return unless defined?(Bundler)
253
- puma = Bundler.rubygems.loaded_specs("puma")
254
- dirs = puma.require_paths.map { |x| File.join(puma.full_gem_path, x) }
282
+ # @!attribute [r] files_to_require_after_prune
283
+ def files_to_require_after_prune
284
+ puma = spec_for_gem("puma")
285
+
286
+ require_paths_for_gem(puma) + extra_runtime_deps_directories
287
+ end
288
+
289
+ # @!attribute [r] extra_runtime_deps_directories
290
+ def extra_runtime_deps_directories
291
+ Array(@options[:extra_runtime_dependencies]).map do |d_name|
292
+ if (spec = spec_for_gem(d_name))
293
+ require_paths_for_gem(spec)
294
+ else
295
+ log "* Could not load extra dependency: #{d_name}"
296
+ nil
297
+ end
298
+ end.flatten.compact
299
+ end
300
+
301
+ # @!attribute [r] puma_wild_location
302
+ def puma_wild_location
303
+ puma = spec_for_gem("puma")
304
+ dirs = require_paths_for_gem(puma)
255
305
  puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }
306
+ File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
307
+ end
256
308
 
257
- unless puma_lib_dir
309
+ def prune_bundler
310
+ return if ENV['PUMA_BUNDLER_PRUNED']
311
+ return unless defined?(Bundler)
312
+ require_rubygems_min_version!(Gem::Version.new("2.2"), "prune_bundler")
313
+ unless puma_wild_location
258
314
  log "! Unable to prune Bundler environment, continuing"
259
315
  return
260
316
  end
261
317
 
262
- deps = puma.runtime_dependencies.map do |d|
263
- spec = Bundler.rubygems.loaded_specs(d.name)
264
- "#{d.name}:#{spec.version.to_s}"
265
- end
318
+ dirs = files_to_require_after_prune
266
319
 
267
320
  log '* Pruning Bundler environment'
268
321
  home = ENV['GEM_HOME']
269
- Bundler.with_clean_env do
322
+ bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
323
+ bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
324
+ with_unbundled_env do
270
325
  ENV['GEM_HOME'] = home
326
+ ENV['BUNDLE_GEMFILE'] = bundle_gemfile
271
327
  ENV['PUMA_BUNDLER_PRUNED'] = '1'
272
- wild = File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
273
- args = [Gem.ruby, wild, '-I', dirs.join(':'), deps.join(',')] + @original_argv
328
+ ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
329
+ args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':')] + @original_argv
274
330
  # Ruby 2.0+ defaults to true which breaks socket activation
275
- args += [{:close_others => false}] if RUBY_VERSION >= '2.0'
331
+ args += [{:close_others => false}]
276
332
  Kernel.exec(*args)
277
333
  end
278
334
  end
279
335
 
336
+ #
337
+ # Puma's systemd integration allows Puma to inform systemd:
338
+ # 1. when it has successfully started
339
+ # 2. when it is starting shutdown
340
+ # 3. periodically for a liveness check with a watchdog thread
341
+ #
342
+
343
+ def integrate_with_systemd
344
+ return unless ENV["NOTIFY_SOCKET"]
345
+
346
+ begin
347
+ require 'puma/systemd'
348
+ rescue LoadError
349
+ log "Systemd integration failed. It looks like you're trying to use systemd notify but don't have sd_notify gem installed"
350
+ return
351
+ end
352
+
353
+ log "* Enabling systemd notification integration"
354
+
355
+ systemd = Systemd.new(@events)
356
+ systemd.hook_events
357
+ systemd.start_watchdog
358
+ end
359
+
360
+ def spec_for_gem(gem_name)
361
+ Bundler.rubygems.loaded_specs(gem_name)
362
+ end
363
+
364
+ def require_paths_for_gem(gem_spec)
365
+ gem_spec.full_require_paths
366
+ end
367
+
280
368
  def log(str)
281
369
  @events.log str
282
370
  end
@@ -291,15 +379,15 @@ module Puma
291
379
  end
292
380
 
293
381
  def graceful_stop
382
+ @events.fire_on_stopped!
294
383
  @runner.stop_blocked
295
- log "=== puma shutdown: #{Time.now} ==="
296
- log "- Goodbye!"
297
384
  end
298
385
 
299
386
  def set_process_title
300
387
  Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
301
388
  end
302
389
 
390
+ # @!attribute [r] title
303
391
  def title
304
392
  buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
305
393
  buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
@@ -311,6 +399,7 @@ module Puma
311
399
  ENV['RACK_ENV'] = environment
312
400
  end
313
401
 
402
+ # @!attribute [r] environment
314
403
  def environment
315
404
  @environment
316
405
  end
@@ -319,16 +408,6 @@ module Puma
319
408
  @options[:prune_bundler] && clustered? && !@options[:preload_app]
320
409
  end
321
410
 
322
- def close_binder_listeners
323
- @binder.listeners.each do |l, io|
324
- io.close
325
- uri = URI.parse(l)
326
- next unless uri.scheme == 'unix'
327
- File.unlink("#{uri.host}#{uri.path}")
328
- end
329
- end
330
-
331
-
332
411
  def generate_restart_data
333
412
  if dir = @options[:directory]
334
413
  @restart_dir = dir
@@ -397,7 +476,7 @@ module Puma
397
476
  Signal.trap "SIGTERM" do
398
477
  graceful_stop
399
478
 
400
- raise SignalException, "SIGTERM"
479
+ raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
401
480
  end
402
481
  rescue Exception
403
482
  log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
@@ -405,12 +484,6 @@ module Puma
405
484
 
406
485
  begin
407
486
  Signal.trap "SIGINT" do
408
- if Puma.jruby?
409
- @status = :exit
410
- graceful_stop
411
- exit
412
- end
413
-
414
487
  stop
415
488
  end
416
489
  rescue Exception
@@ -428,6 +501,46 @@ module Puma
428
501
  rescue Exception
429
502
  log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
430
503
  end
504
+
505
+ begin
506
+ unless Puma.jruby? # INFO in use by JVM already
507
+ Signal.trap "SIGINFO" do
508
+ thread_status do |name, backtrace|
509
+ @events.log name
510
+ @events.log backtrace.map { |bt| " #{bt}" }
511
+ end
512
+ end
513
+ end
514
+ rescue Exception
515
+ # Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
516
+ # to see this constantly on Linux.
517
+ end
518
+ end
519
+
520
+ def require_rubygems_min_version!(min_version, feature)
521
+ return if min_version <= Gem::Version.new(Gem::VERSION)
522
+
523
+ raise "#{feature} is not supported on your version of RubyGems. " \
524
+ "You must have RubyGems #{min_version}+ to use this feature."
525
+ end
526
+
527
+ # @version 5.0.0
528
+ def with_unbundled_env
529
+ bundler_ver = Gem::Version.new(Bundler::VERSION)
530
+ if bundler_ver < Gem::Version.new('2.1.0')
531
+ Bundler.with_clean_env { yield }
532
+ else
533
+ Bundler.with_unbundled_env { yield }
534
+ end
535
+ end
536
+
537
+ def log_config
538
+ log "Configuration:"
539
+
540
+ @config.final_options
541
+ .each { |config_key, value| log "- #{config_key}: #{value}" }
542
+
543
+ log "\n"
431
544
  end
432
545
  end
433
546
  end
@@ -0,0 +1,81 @@
1
+ module Puma
2
+ module MiniSSL
3
+ class ContextBuilder
4
+ def initialize(params, events)
5
+ @params = params
6
+ @events = events
7
+ end
8
+
9
+ def context
10
+ ctx = MiniSSL::Context.new
11
+
12
+ if defined?(JRUBY_VERSION)
13
+ unless params['keystore']
14
+ events.error "Please specify the Java keystore via 'keystore='"
15
+ end
16
+
17
+ ctx.keystore = params['keystore']
18
+
19
+ unless params['keystore-pass']
20
+ events.error "Please specify the Java keystore password via 'keystore-pass='"
21
+ end
22
+
23
+ ctx.keystore_pass = params['keystore-pass']
24
+ ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
25
+ else
26
+ if params['key'].nil? && params['key_pem'].nil?
27
+ events.error "Please specify the SSL key via 'key=' or 'key_pem='"
28
+ end
29
+
30
+ ctx.key = params['key'] if params['key']
31
+ ctx.key_pem = params['key_pem'] if params['key_pem']
32
+
33
+ if params['cert'].nil? && params['cert_pem'].nil?
34
+ events.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
35
+ end
36
+
37
+ ctx.cert = params['cert'] if params['cert']
38
+ ctx.cert_pem = params['cert_pem'] if params['cert_pem']
39
+
40
+ if ['peer', 'force_peer'].include?(params['verify_mode'])
41
+ unless params['ca']
42
+ events.error "Please specify the SSL ca via 'ca='"
43
+ end
44
+ end
45
+
46
+ ctx.ca = params['ca'] if params['ca']
47
+ ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
48
+ end
49
+
50
+ ctx.no_tlsv1 = true if params['no_tlsv1'] == 'true'
51
+ ctx.no_tlsv1_1 = true if params['no_tlsv1_1'] == 'true'
52
+
53
+ if params['verify_mode']
54
+ ctx.verify_mode = case params['verify_mode']
55
+ when "peer"
56
+ MiniSSL::VERIFY_PEER
57
+ when "force_peer"
58
+ MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
59
+ when "none"
60
+ MiniSSL::VERIFY_NONE
61
+ else
62
+ events.error "Please specify a valid verify_mode="
63
+ MiniSSL::VERIFY_NONE
64
+ end
65
+ end
66
+
67
+ if params['verification_flags']
68
+ ctx.verification_flags = params['verification_flags'].split(',').
69
+ map { |flag| MiniSSL::VERIFICATION_FLAGS.fetch(flag) }.
70
+ inject { |sum, flag| sum ? sum | flag : flag }
71
+ end
72
+
73
+ ctx
74
+ end
75
+
76
+ private
77
+
78
+ attr_reader :params, :events
79
+ end
80
+ end
81
+ end