puma 3.8.2 → 3.12.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 (67) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +153 -0
  3. data/README.md +140 -230
  4. data/docs/architecture.md +36 -0
  5. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  6. data/docs/images/puma-connection-flow.png +0 -0
  7. data/docs/images/puma-general-arch.png +0 -0
  8. data/docs/plugins.md +28 -0
  9. data/docs/restart.md +39 -0
  10. data/docs/signals.md +56 -3
  11. data/docs/systemd.md +112 -37
  12. data/ext/puma_http11/http11_parser.c +87 -85
  13. data/ext/puma_http11/http11_parser.rl +12 -10
  14. data/ext/puma_http11/mini_ssl.c +31 -5
  15. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
  16. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -2
  17. data/lib/puma/app/status.rb +8 -0
  18. data/lib/puma/binder.rb +22 -17
  19. data/lib/puma/cli.rb +22 -7
  20. data/lib/puma/client.rb +41 -2
  21. data/lib/puma/cluster.rb +28 -7
  22. data/lib/puma/commonlogger.rb +2 -0
  23. data/lib/puma/configuration.rb +21 -14
  24. data/lib/puma/const.rb +17 -2
  25. data/lib/puma/control_cli.rb +16 -14
  26. data/lib/puma/convenient.rb +2 -0
  27. data/lib/puma/daemon_ext.rb +2 -0
  28. data/lib/puma/delegation.rb +2 -0
  29. data/lib/puma/detect.rb +2 -0
  30. data/lib/puma/dsl.rb +46 -9
  31. data/lib/puma/events.rb +3 -2
  32. data/lib/puma/io_buffer.rb +2 -0
  33. data/lib/puma/java_io_buffer.rb +2 -0
  34. data/lib/puma/jruby_restart.rb +2 -1
  35. data/lib/puma/launcher.rb +42 -20
  36. data/lib/puma/minissl.rb +67 -28
  37. data/lib/puma/null_io.rb +2 -0
  38. data/lib/puma/plugin/tmp_restart.rb +0 -1
  39. data/lib/puma/plugin.rb +2 -0
  40. data/lib/puma/rack/builder.rb +2 -1
  41. data/lib/puma/reactor.rb +137 -0
  42. data/lib/puma/runner.rb +16 -3
  43. data/lib/puma/server.rb +145 -29
  44. data/lib/puma/single.rb +14 -3
  45. data/lib/puma/state_file.rb +2 -0
  46. data/lib/puma/tcp_logger.rb +2 -0
  47. data/lib/puma/thread_pool.rb +55 -6
  48. data/lib/puma/util.rb +1 -0
  49. data/lib/puma.rb +8 -0
  50. data/lib/rack/handler/puma.rb +13 -2
  51. data/tools/jungle/README.md +12 -2
  52. data/tools/jungle/init.d/README.md +2 -0
  53. data/tools/jungle/init.d/puma +2 -2
  54. data/tools/jungle/init.d/run-puma +1 -1
  55. data/tools/jungle/rc.d/README.md +74 -0
  56. data/tools/jungle/rc.d/puma +61 -0
  57. data/tools/jungle/rc.d/puma.conf +10 -0
  58. data/tools/trickletest.rb +1 -1
  59. metadata +21 -95
  60. data/.github/issue_template.md +0 -20
  61. data/Gemfile +0 -12
  62. data/Manifest.txt +0 -78
  63. data/Rakefile +0 -158
  64. data/Release.md +0 -9
  65. data/gemfiles/2.1-Gemfile +0 -12
  66. data/puma.gemspec +0 -52
  67. /data/{DEPLOYMENT.md → docs/deployment.md} +0 -0
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
- require 'puma/state_file'
3
- require 'puma/const'
4
- require 'puma/detect'
5
- require 'puma/configuration'
4
+ require_relative 'state_file'
5
+ require_relative 'const'
6
+ require_relative 'detect'
7
+ require_relative 'configuration'
6
8
  require 'uri'
7
9
  require 'socket'
8
10
 
9
11
  module Puma
10
12
  class ControlCLI
11
13
 
12
- COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory}
14
+ COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats}
13
15
 
14
16
  def initialize(argv, stdout=STDOUT, stderr=STDERR)
15
17
  @state = nil
@@ -69,6 +71,7 @@ module Puma
69
71
  end
70
72
 
71
73
  opts.order!(argv) { |a| opts.terminate a }
74
+ opts.parse!
72
75
 
73
76
  @command = argv.shift
74
77
 
@@ -128,7 +131,7 @@ module Puma
128
131
  uri = URI.parse @control_url
129
132
 
130
133
  # create server object by scheme
131
- @server = case uri.scheme
134
+ server = case uri.scheme
132
135
  when "tcp"
133
136
  TCPSocket.new uri.host, uri.port
134
137
  when "unix"
@@ -146,9 +149,9 @@ module Puma
146
149
  url = url + "?token=#{@control_auth_token}"
147
150
  end
148
151
 
149
- @server << "GET #{url} HTTP/1.0\r\n\r\n"
152
+ server << "GET #{url} HTTP/1.0\r\n\r\n"
150
153
 
151
- unless data = @server.read
154
+ unless data = server.read
152
155
  raise "Server closed connection before responding"
153
156
  end
154
157
 
@@ -169,10 +172,10 @@ module Puma
169
172
  end
170
173
 
171
174
  message "Command #{@command} sent success"
172
- message response.last if @command == "stats"
175
+ message response.last if @command == "stats" || @command == "gc-stats"
173
176
  end
174
-
175
- @server.close
177
+ ensure
178
+ server.close if server && !server.closed?
176
179
  end
177
180
 
178
181
  def send_signal
@@ -204,7 +207,6 @@ module Puma
204
207
  Process.kill "SIGUSR1", @pid
205
208
 
206
209
  else
207
- message "Puma is started"
208
210
  return
209
211
  end
210
212
 
@@ -220,7 +222,7 @@ module Puma
220
222
  end
221
223
 
222
224
  def run
223
- start if @command == "start"
225
+ return start if @command == "start"
224
226
 
225
227
  prepare_configuration
226
228
 
@@ -245,7 +247,7 @@ module Puma
245
247
  run_args += ["-S", @state] if @state
246
248
  run_args += ["-q"] if @quiet
247
249
  run_args += ["--pidfile", @pidfile] if @pidfile
248
- run_args += ["--control", @control_url] if @control_url
250
+ run_args += ["--control-url", @control_url] if @control_url
249
251
  run_args += ["--control-token", @control_auth_token] if @control_auth_token
250
252
  run_args += ["-C", @config_file] if @config_file
251
253
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/launcher'
2
4
  require 'puma/configuration'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Process
2
4
 
3
5
  # This overrides the default version because it is broken if it
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  module Delegation
3
5
  def forward(what, who)
data/lib/puma/detect.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  IS_JRUBY = defined?(JRUBY_VERSION)
3
5
 
data/lib/puma/dsl.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  # The methods that are available for use inside the config file.
3
5
  # These same methods are used in Puma cli and the rack handler
@@ -55,6 +57,14 @@ module Puma
55
57
  @plugins.clear
56
58
  end
57
59
 
60
+ def set_default_host(host)
61
+ @options[:default_host] = host
62
+ end
63
+
64
+ def default_host
65
+ @options[:default_host] || Configuration::DefaultTCPHost
66
+ end
67
+
58
68
  def inject(&blk)
59
69
  instance_eval(&blk)
60
70
  end
@@ -110,18 +120,35 @@ module Puma
110
120
  @options[:config_files] << file
111
121
  end
112
122
 
113
- # Bind the server to +url+. tcp:// and unix:// are the only accepted
114
- # protocols.
123
+ # Adds a binding for the server to +url+. tcp://, unix://, and ssl:// are the only accepted
124
+ # protocols. Use query parameters within the url to specify options.
125
+ #
126
+ # @note multiple urls can be bound to, calling `bind` does not overwrite previous bindings.
127
+ #
128
+ # @example Explicitly the socket backlog depth (default is 1024)
129
+ # bind('unix:///var/run/puma.sock?backlog=2048')
130
+ #
131
+ # @example Set up ssl cert
132
+ # bind('ssl://127.0.0.1:9292?key=key.key&cert=cert.pem')
115
133
  #
134
+ # @example Prefer low-latency over higher throughput (via `Socket::TCP_NODELAY`)
135
+ # bind('tcp://0.0.0.0:9292?low_latency=true')
136
+ #
137
+ # @example Set socket permissions
138
+ # bind('unix:///var/run/puma.sock?umask=0111')
116
139
  def bind(url)
117
140
  @options[:binds] ||= []
118
141
  @options[:binds] << url
119
142
  end
120
143
 
144
+ def clear_binds!
145
+ @options[:binds] = []
146
+ end
147
+
121
148
  # Define the TCP port to bind to. Use +bind+ for more advanced options.
122
149
  #
123
150
  def port(port, host=nil)
124
- host ||= Configuration::DefaultTCPHost
151
+ host ||= default_host
125
152
  bind "tcp://#{host}:#{port}"
126
153
  end
127
154
 
@@ -129,7 +156,13 @@ module Puma
129
156
  # them
130
157
  #
131
158
  def persistent_timeout(seconds)
132
- @options[:persistent_timeout] = seconds
159
+ @options[:persistent_timeout] = Integer(seconds)
160
+ end
161
+
162
+ # Define how long the tcp socket stays open, if no data has been received
163
+ #
164
+ def first_data_timeout(seconds)
165
+ @options[:first_data_timeout] = Integer(seconds)
133
166
  end
134
167
 
135
168
  # Work around leaky apps that leave garbage in Thread locals
@@ -146,7 +179,7 @@ module Puma
146
179
  end
147
180
 
148
181
  # When shutting down, drain the accept socket of pending
149
- # connections and proces them. This loops over the accept
182
+ # connections and process them. This loops over the accept
150
183
  # socket until there are no more read events and then stops
151
184
  # looking and waits for the requests to finish.
152
185
  def drain_on_shutdown(which=true)
@@ -231,6 +264,10 @@ module Puma
231
264
  @options[:mode] = :tcp
232
265
  end
233
266
 
267
+ def early_hints(answer=true)
268
+ @options[:early_hints] = answer
269
+ end
270
+
234
271
  # Redirect STDOUT and STDERR to files specified.
235
272
  def stdout_redirect(stdout=nil, stderr=nil, append=false)
236
273
  @options[:redirect_stdout] = stdout
@@ -397,17 +434,17 @@ module Puma
397
434
  # that have not checked in within the given +timeout+.
398
435
  # This mitigates hung processes. Default value is 60 seconds.
399
436
  def worker_timeout(timeout)
400
- @options[:worker_timeout] = timeout
437
+ @options[:worker_timeout] = Integer(timeout)
401
438
  end
402
439
 
403
440
  # *Cluster mode only* Set the timeout for workers to boot
404
441
  def worker_boot_timeout(timeout)
405
- @options[:worker_boot_timeout] = timeout
442
+ @options[:worker_boot_timeout] = Integer(timeout)
406
443
  end
407
444
 
408
445
  # *Cluster mode only* Set the timeout for worker shutdown
409
446
  def worker_shutdown_timeout(timeout)
410
- @options[:worker_shutdown_timeout] = timeout
447
+ @options[:worker_shutdown_timeout] = Integer(timeout)
411
448
  end
412
449
 
413
450
  # When set to true (the default), workers accept all requests
@@ -466,7 +503,7 @@ module Puma
466
503
  when Hash
467
504
  if hdr = val[:header]
468
505
  @options[:remote_address] = :header
469
- @options[:remote_address_header] = "HTTP_" + hdr.upcase.gsub("-", "_")
506
+ @options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
470
507
  else
471
508
  raise "Invalid value for set_remote_address - #{val.inspect}"
472
509
  end
data/lib/puma/events.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/const'
2
4
  require "puma/null_io"
3
5
  require 'stringio'
@@ -91,8 +93,7 @@ module Puma
91
93
  # parsing exception.
92
94
  #
93
95
  def parse_error(server, env, error)
94
- @stderr.puts "#{Time.now}: HTTP parse error, malformed request (#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}): #{error.inspect}"
95
- @stderr.puts "#{Time.now}: ENV: #{env.inspect}\n---\n"
96
+ @stderr.puts "#{Time.now}: HTTP parse error, malformed request (#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}): #{error.inspect}\n---\n"
96
97
  end
97
98
 
98
99
  # An SSL error has occurred.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/detect'
2
4
 
3
5
  if Puma.jruby?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'java'
2
4
 
3
5
  # Conservative native JRuby/Java implementation of IOBuffer
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ffi'
2
4
 
3
5
  module Puma
@@ -80,4 +82,3 @@ module Puma
80
82
  end
81
83
  end
82
84
  end
83
-
data/lib/puma/launcher.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/events'
2
4
  require 'puma/detect'
3
5
 
@@ -40,7 +42,7 @@ module Puma
40
42
  # [200, {}, ["hello world"]]
41
43
  # end
42
44
  # end
43
- # Puma::Launcher.new(conf, argv: Puma::Events.stdio).run
45
+ # Puma::Launcher.new(conf, events: Puma::Events.stdio).run
44
46
  def initialize(conf, launcher_args={})
45
47
  @runner = nil
46
48
  @events = launcher_args[:events] || Events::DEFAULT
@@ -63,8 +65,8 @@ module Puma
63
65
 
64
66
  generate_restart_data
65
67
 
66
- if clustered? && (Puma.jruby? || Puma.windows?)
67
- unsupported 'worker mode not supported on JRuby or Windows'
68
+ if clustered? && !Process.respond_to?(:fork)
69
+ unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
68
70
  end
69
71
 
70
72
  if @options[:daemon] && Puma.windows?
@@ -86,6 +88,7 @@ module Puma
86
88
  else
87
89
  @runner = Single.new(self, @events)
88
90
  end
91
+ Puma.stats_object = @runner
89
92
 
90
93
  @status = :run
91
94
  end
@@ -163,6 +166,17 @@ module Puma
163
166
 
164
167
  # Run the server. This blocks until the server is stopped
165
168
  def run
169
+ previous_env =
170
+ if defined?(Bundler)
171
+ env = Bundler::ORIGINAL_ENV.dup
172
+ # add -rbundler/setup so we load from Gemfile when restarting
173
+ bundle = "-rbundler/setup"
174
+ env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
175
+ env
176
+ else
177
+ ENV.to_h
178
+ end
179
+
166
180
  @config.clamp
167
181
 
168
182
  @config.plugins.fire_starts self
@@ -178,6 +192,7 @@ module Puma
178
192
  graceful_stop
179
193
  when :restart
180
194
  log "* Restarting..."
195
+ ENV.replace(previous_env)
181
196
  @runner.before_restart
182
197
  restart!
183
198
  when :exit
@@ -222,8 +237,8 @@ module Puma
222
237
  else
223
238
  redirects = {:close_others => true}
224
239
  @binder.listeners.each_with_index do |(l, io), i|
225
- ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
226
- redirects[io.to_i] = io.to_i
240
+ ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
241
+ redirects[io.to_i] = io.to_i
227
242
  end
228
243
 
229
244
  argv = restart_args
@@ -286,8 +301,8 @@ module Puma
286
301
  end
287
302
 
288
303
  def title
289
- buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
290
- buffer << " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
304
+ buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
305
+ buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
291
306
  buffer
292
307
  end
293
308
 
@@ -337,8 +352,6 @@ module Puma
337
352
 
338
353
  @restart_dir ||= Dir.pwd
339
354
 
340
- require 'rubygems'
341
-
342
355
  # if $0 is a file in the current directory, then restart
343
356
  # it the same, otherwise add -S on there because it was
344
357
  # picked up in PATH.
@@ -349,9 +362,10 @@ module Puma
349
362
  arg0 = [Gem.ruby, "-S", $0]
350
363
  end
351
364
 
352
- # Detect and reinject -Ilib from the command line
365
+ # Detect and reinject -Ilib from the command line, used for testing without bundler
366
+ # cruby has an expanded path, jruby has just "lib"
353
367
  lib = File.expand_path "lib"
354
- arg0[1,0] = ["-I", lib] if $:[0] == lib
368
+ arg0[1,0] = ["-I", lib] if [lib, "lib"].include?($LOAD_PATH[0])
355
369
 
356
370
  if defined? Puma::WILD_ARGS
357
371
  @restart_argv = arg0 + Puma::WILD_ARGS + @original_argv
@@ -381,12 +395,28 @@ module Puma
381
395
 
382
396
  begin
383
397
  Signal.trap "SIGTERM" do
384
- stop
398
+ graceful_stop
399
+
400
+ raise SignalException, "SIGTERM"
385
401
  end
386
402
  rescue Exception
387
403
  log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
388
404
  end
389
405
 
406
+ begin
407
+ Signal.trap "SIGINT" do
408
+ if Puma.jruby?
409
+ @status = :exit
410
+ graceful_stop
411
+ exit
412
+ end
413
+
414
+ stop
415
+ end
416
+ rescue Exception
417
+ log "*** SIGINT not implemented, signal based gracefully stopping unavailable!"
418
+ end
419
+
390
420
  begin
391
421
  Signal.trap "SIGHUP" do
392
422
  if @runner.redirected_io?
@@ -398,14 +428,6 @@ module Puma
398
428
  rescue Exception
399
429
  log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
400
430
  end
401
-
402
- if Puma.jruby?
403
- Signal.trap("INT") do
404
- @status = :exit
405
- graceful_stop
406
- exit
407
- end
408
- end
409
431
  end
410
432
  end
411
433
  end
data/lib/puma/minissl.rb CHANGED
@@ -1,3 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'io/wait'
5
+ rescue LoadError
6
+ end
7
+
1
8
  module Puma
2
9
  module MiniSSL
3
10
  class Socket
@@ -11,6 +18,10 @@ module Puma
11
18
  @socket
12
19
  end
13
20
 
21
+ def closed?
22
+ @socket.closed?
23
+ end
24
+
14
25
  def readpartial(size)
15
26
  while true
16
27
  output = @engine.read
@@ -36,12 +47,29 @@ module Puma
36
47
  output
37
48
  end
38
49
 
39
- def read_nonblock(size)
50
+ def read_nonblock(size, *_)
51
+ # *_ is to deal with keyword args that were added
52
+ # at some point (and being used in the wild)
40
53
  while true
41
54
  output = engine_read_all
42
55
  return output if output
43
56
 
44
- data = @socket.read_nonblock(size)
57
+ begin
58
+ data = @socket.read_nonblock(size, exception: false)
59
+ if data == :wait_readable || data == :wait_writable
60
+ if @socket.to_io.respond_to?(data)
61
+ @socket.to_io.__send__(data)
62
+ elsif data == :wait_readable
63
+ IO.select([@socket.to_io])
64
+ else
65
+ IO.select(nil, [@socket.to_io])
66
+ end
67
+ elsif !data
68
+ return nil
69
+ else
70
+ break
71
+ end
72
+ end while true
45
73
 
46
74
  @engine.inject(data)
47
75
  output = engine_read_all
@@ -55,6 +83,8 @@ module Puma
55
83
  end
56
84
 
57
85
  def write(data)
86
+ return 0 if data.empty?
87
+
58
88
  need = data.bytesize
59
89
 
60
90
  while true
@@ -77,39 +107,46 @@ module Puma
77
107
  alias_method :syswrite, :write
78
108
  alias_method :<<, :write
79
109
 
110
+ # This is a temporary fix to deal with websockets code using
111
+ # write_nonblock. The problem with implementing it properly
112
+ # is that it means we'd have to have the ability to rewind
113
+ # an engine because after we write+extract, the socket
114
+ # write_nonblock call might raise an exception and later
115
+ # code would pass the same data in, but the engine would think
116
+ # it had already written the data in. So for the time being
117
+ # (and since write blocking is quite rare), go ahead and actually
118
+ # block in write_nonblock.
119
+ def write_nonblock(data, *_)
120
+ write data
121
+ end
122
+
80
123
  def flush
81
124
  @socket.flush
82
125
  end
83
126
 
84
- def close
85
- begin
86
- # Try to setup (so that we can then close them) any
87
- # partially initialized sockets.
88
- while @engine.init?
89
- # Don't let this socket hold this loop forever.
90
- # If it can't send more packets within 1s, then
91
- # give up.
92
- return unless IO.select([@socket], nil, nil, 1)
93
- begin
94
- read_nonblock(1024)
95
- rescue Errno::EAGAIN
96
- end
97
- end
98
-
99
- done = @engine.shutdown
100
-
101
- while true
102
- enc = @engine.extract
103
- @socket.write enc
104
-
105
- notify = @socket.sysread(1024)
127
+ def read_and_drop(timeout = 1)
128
+ return :timeout unless IO.select([@socket], nil, nil, timeout)
129
+ return :eof unless read_nonblock(1024)
130
+ :drop
131
+ rescue Errno::EAGAIN
132
+ # do nothing
133
+ :eagain
134
+ end
106
135
 
107
- @engine.inject notify
108
- done = @engine.shutdown
136
+ def should_drop_bytes?
137
+ @engine.init? || !@engine.shutdown
138
+ end
109
139
 
110
- break if done
140
+ def close
141
+ begin
142
+ # Read any drop any partially initialized sockets and any received bytes during shutdown.
143
+ # Don't let this socket hold this loop forever.
144
+ # If it can't send more packets within 1s, then give up.
145
+ while should_drop_bytes?
146
+ return if [:timeout, :eof].include?(read_and_drop(1))
111
147
  end
112
148
  rescue IOError, SystemCallError
149
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
113
150
  # nothing
114
151
  ensure
115
152
  @socket.close
@@ -145,6 +182,7 @@ module Puma
145
182
  # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
146
183
  attr_reader :keystore
147
184
  attr_accessor :keystore_pass
185
+ attr_accessor :ssl_cipher_list
148
186
 
149
187
  def keystore=(keystore)
150
188
  raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
@@ -160,6 +198,7 @@ module Puma
160
198
  attr_reader :key
161
199
  attr_reader :cert
162
200
  attr_reader :ca
201
+ attr_accessor :ssl_cipher_filter
163
202
 
164
203
  def key=(key)
165
204
  raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
@@ -214,7 +253,7 @@ module Puma
214
253
  end
215
254
 
216
255
  def close
217
- @socket.close
256
+ @socket.close unless @socket.closed? # closed? call is for Windows
218
257
  end
219
258
  end
220
259
  end
data/lib/puma/null_io.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  # Provides an IO-like object that always appears to contain no data.
3
5
  # Used as the value for rack.input when the request has no body.
@@ -32,4 +32,3 @@ Puma::Plugin.create do
32
32
  end
33
33
  end
34
34
  end
35
-
data/lib/puma/plugin.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  class UnknownPlugin < RuntimeError; end
3
5
 
@@ -110,7 +110,8 @@ module Puma::Rack
110
110
 
111
111
  has_options = false
112
112
  server.valid_options.each do |name, description|
113
- next if name.to_s.match(/^(Host|Port)[^a-zA-Z]/) # ignore handler's host and port options, we do our own.
113
+ next if name.to_s =~ /^(Host|Port)[^a-zA-Z]/ # ignore handler's host and port options, we do our own.
114
+
114
115
  info << " -O %-21s %s" % [name, description]
115
116
  has_options = true
116
117
  end