puma 2.2.0 → 8.0.2

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 (136) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +3334 -0
  3. data/LICENSE +23 -20
  4. data/README.md +387 -100
  5. data/bin/puma-wild +25 -0
  6. data/bin/pumactl +1 -1
  7. data/docs/5.0-Upgrade.md +98 -0
  8. data/docs/6.0-Upgrade.md +56 -0
  9. data/docs/7.0-Upgrade.md +52 -0
  10. data/docs/8.0-Upgrade.md +100 -0
  11. data/docs/architecture.md +74 -0
  12. data/docs/compile_options.md +55 -0
  13. data/docs/deployment.md +137 -0
  14. data/docs/fork_worker.md +41 -0
  15. data/docs/grpc.md +62 -0
  16. data/docs/images/favicon.svg +1 -0
  17. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  18. data/docs/images/puma-connection-flow.png +0 -0
  19. data/docs/images/puma-general-arch.png +0 -0
  20. data/docs/images/running-puma.svg +1 -0
  21. data/docs/images/standard-logo.svg +1 -0
  22. data/docs/java_options.md +54 -0
  23. data/docs/jungle/README.md +9 -0
  24. data/docs/jungle/rc.d/README.md +74 -0
  25. data/docs/jungle/rc.d/puma +61 -0
  26. data/docs/jungle/rc.d/puma.conf +10 -0
  27. data/docs/kubernetes.md +73 -0
  28. data/docs/nginx.md +5 -5
  29. data/docs/plugins.md +42 -0
  30. data/docs/rails_dev_mode.md +28 -0
  31. data/docs/restart.md +65 -0
  32. data/docs/signals.md +98 -0
  33. data/docs/stats.md +148 -0
  34. data/docs/systemd.md +253 -0
  35. data/docs/testing_benchmarks_local_files.md +150 -0
  36. data/docs/testing_test_rackup_ci_files.md +36 -0
  37. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  38. data/ext/puma_http11/extconf.rb +59 -2
  39. data/ext/puma_http11/http11_parser.c +319 -487
  40. data/ext/puma_http11/http11_parser.h +15 -13
  41. data/ext/puma_http11/http11_parser.java.rl +64 -94
  42. data/ext/puma_http11/http11_parser.rl +27 -24
  43. data/ext/puma_http11/http11_parser_common.rl +8 -8
  44. data/ext/puma_http11/mini_ssl.c +696 -38
  45. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  46. data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
  47. data/ext/puma_http11/org/jruby/puma/Http11.java +241 -145
  48. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +174 -221
  49. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +413 -193
  50. data/ext/puma_http11/puma_http11.c +183 -175
  51. data/lib/puma/app/status.rb +77 -29
  52. data/lib/puma/binder.rb +349 -119
  53. data/lib/puma/cli.rb +163 -801
  54. data/lib/puma/client.rb +627 -144
  55. data/lib/puma/client_env.rb +171 -0
  56. data/lib/puma/cluster/worker.rb +183 -0
  57. data/lib/puma/cluster/worker_handle.rb +127 -0
  58. data/lib/puma/cluster.rb +634 -0
  59. data/lib/puma/cluster_accept_loop_delay.rb +91 -0
  60. data/lib/puma/commonlogger.rb +115 -0
  61. data/lib/puma/configuration.rb +420 -198
  62. data/lib/puma/const.rb +266 -115
  63. data/lib/puma/control_cli.rb +219 -120
  64. data/lib/puma/detect.rb +56 -2
  65. data/lib/puma/dsl.rb +1562 -0
  66. data/lib/puma/error_logger.rb +115 -0
  67. data/lib/puma/events.rb +44 -60
  68. data/lib/puma/io_buffer.rb +48 -5
  69. data/lib/puma/jruby_restart.rb +2 -51
  70. data/lib/puma/json_serialization.rb +96 -0
  71. data/lib/puma/launcher/bundle_pruner.rb +102 -0
  72. data/lib/puma/launcher.rb +501 -0
  73. data/lib/puma/log_writer.rb +153 -0
  74. data/lib/puma/minissl/context_builder.rb +96 -0
  75. data/lib/puma/minissl.rb +355 -37
  76. data/lib/puma/null_io.rb +79 -12
  77. data/lib/puma/plugin/systemd.rb +90 -0
  78. data/lib/puma/plugin/tmp_restart.rb +36 -0
  79. data/lib/puma/plugin.rb +111 -0
  80. data/lib/puma/rack/builder.rb +297 -0
  81. data/lib/puma/rack/urlmap.rb +93 -0
  82. data/lib/puma/rack_default.rb +21 -4
  83. data/lib/puma/reactor.rb +102 -133
  84. data/lib/puma/response.rb +532 -0
  85. data/lib/puma/runner.rb +211 -0
  86. data/lib/puma/sd_notify.rb +146 -0
  87. data/lib/puma/server.rb +582 -445
  88. data/lib/puma/server_plugin_control.rb +32 -0
  89. data/lib/puma/single.rb +72 -0
  90. data/lib/puma/state_file.rb +69 -0
  91. data/lib/puma/thread_pool.rb +389 -57
  92. data/lib/puma/util.rb +125 -0
  93. data/lib/puma.rb +80 -6
  94. data/lib/rack/handler/puma.rb +125 -47
  95. data/tools/Dockerfile +26 -0
  96. data/tools/trickletest.rb +44 -0
  97. metadata +88 -148
  98. data/COPYING +0 -55
  99. data/Gemfile +0 -10
  100. data/History.txt +0 -302
  101. data/Manifest.txt +0 -60
  102. data/Rakefile +0 -165
  103. data/TODO +0 -5
  104. data/docs/config.md +0 -0
  105. data/ext/puma_http11/ext_help.h +0 -15
  106. data/ext/puma_http11/io_buffer.c +0 -154
  107. data/lib/puma/accept_nonblock.rb +0 -23
  108. data/lib/puma/capistrano.rb +0 -34
  109. data/lib/puma/compat.rb +0 -11
  110. data/lib/puma/daemon_ext.rb +0 -20
  111. data/lib/puma/delegation.rb +0 -11
  112. data/lib/puma/java_io_buffer.rb +0 -45
  113. data/lib/puma/rack_patch.rb +0 -25
  114. data/puma.gemspec +0 -45
  115. data/test/test_app_status.rb +0 -88
  116. data/test/test_cli.rb +0 -171
  117. data/test/test_config.rb +0 -16
  118. data/test/test_http10.rb +0 -27
  119. data/test/test_http11.rb +0 -126
  120. data/test/test_integration.rb +0 -154
  121. data/test/test_iobuffer.rb +0 -38
  122. data/test/test_minissl.rb +0 -25
  123. data/test/test_null_io.rb +0 -31
  124. data/test/test_persistent.rb +0 -238
  125. data/test/test_puma_server.rb +0 -224
  126. data/test/test_rack_handler.rb +0 -10
  127. data/test/test_rack_server.rb +0 -141
  128. data/test/test_thread_pool.rb +0 -146
  129. data/test/test_unix_socket.rb +0 -39
  130. data/test/test_ws.rb +0 -89
  131. data/tools/jungle/init.d/README.md +0 -54
  132. data/tools/jungle/init.d/puma +0 -332
  133. data/tools/jungle/init.d/run-puma +0 -3
  134. data/tools/jungle/upstart/README.md +0 -61
  135. data/tools/jungle/upstart/puma-manager.conf +0 -31
  136. data/tools/jungle/upstart/puma.conf +0 -52
@@ -0,0 +1,96 @@
1
+ module Puma
2
+ module MiniSSL
3
+ class ContextBuilder
4
+ def initialize(params, log_writer)
5
+ @params = params
6
+ @log_writer = log_writer
7
+ end
8
+
9
+ def context
10
+ ctx = MiniSSL::Context.new
11
+
12
+ if defined?(JRUBY_VERSION)
13
+ unless params['keystore']
14
+ log_writer.error "Please specify the Java keystore via 'keystore='"
15
+ end
16
+
17
+ ctx.keystore = params['keystore']
18
+
19
+ unless params['keystore-pass']
20
+ log_writer.error "Please specify the Java keystore password via 'keystore-pass='"
21
+ end
22
+
23
+ ctx.keystore_pass = params['keystore-pass']
24
+ ctx.keystore_type = params['keystore-type']
25
+
26
+ if truststore = params['truststore']
27
+ ctx.truststore = truststore.eql?('default') ? :default : truststore
28
+ ctx.truststore_pass = params['truststore-pass']
29
+ ctx.truststore_type = params['truststore-type']
30
+ end
31
+
32
+ ctx.cipher_suites = params['cipher_suites'] || params['ssl_cipher_list']
33
+ ctx.protocols = params['protocols'] if params['protocols']
34
+ else
35
+ if params['key'].nil? && params['key_pem'].nil?
36
+ log_writer.error "Please specify the SSL key via 'key=' or 'key_pem='"
37
+ end
38
+
39
+ ctx.key = params['key'] if params['key']
40
+ ctx.key_pem = params['key_pem'] if params['key_pem']
41
+ ctx.key_password_command = params['key_password_command'] if params['key_password_command']
42
+
43
+ if params['cert'].nil? && params['cert_pem'].nil?
44
+ log_writer.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
45
+ end
46
+
47
+ ctx.cert = params['cert'] if params['cert']
48
+ ctx.cert_pem = params['cert_pem'] if params['cert_pem']
49
+
50
+ if ['peer', 'force_peer'].include?(params['verify_mode'])
51
+ unless params['ca']
52
+ log_writer.error "Please specify the SSL ca via 'ca='"
53
+ end
54
+ # needed for Puma::MiniSSL::Socket#peercert, env['puma.peercert']
55
+ require 'openssl'
56
+ end
57
+
58
+ ctx.ca = params['ca'] if params['ca']
59
+ ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
60
+ ctx.ssl_ciphersuites = params['ssl_ciphersuites'] if params['ssl_ciphersuites'] && HAS_TLS1_3
61
+
62
+ ctx.reuse = params['reuse'] if params['reuse']
63
+ end
64
+
65
+ ctx.no_tlsv1 = params['no_tlsv1'] == 'true'
66
+ ctx.no_tlsv1_1 = params['no_tlsv1_1'] == 'true'
67
+
68
+ if params['verify_mode']
69
+ ctx.verify_mode = case params['verify_mode']
70
+ when "peer"
71
+ MiniSSL::VERIFY_PEER
72
+ when "force_peer"
73
+ MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
74
+ when "none"
75
+ MiniSSL::VERIFY_NONE
76
+ else
77
+ log_writer.error "Please specify a valid verify_mode="
78
+ MiniSSL::VERIFY_NONE
79
+ end
80
+ end
81
+
82
+ if params['verification_flags']
83
+ ctx.verification_flags = params['verification_flags'].split(',').
84
+ map { |flag| MiniSSL::VERIFICATION_FLAGS.fetch(flag) }.
85
+ inject { |sum, flag| sum ? sum | flag : flag }
86
+ end
87
+
88
+ ctx
89
+ end
90
+
91
+ private
92
+
93
+ attr_reader :params, :log_writer
94
+ end
95
+ end
96
+ end
data/lib/puma/minissl.rb CHANGED
@@ -1,15 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
5
+ rescue LoadError
6
+ end
7
+
8
+ require 'open3'
9
+ # need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
10
+ # use require, see https://github.com/puma/puma/pull/2381
11
+ require 'puma/puma_http11'
12
+
1
13
  module Puma
2
14
  module MiniSSL
15
+ # Define constant at runtime, as it's easy to determine at built time,
16
+ # but Puma could (it shouldn't) be loaded with an older OpenSSL version
17
+ # @version 5.0.0
18
+ HAS_TLS1_3 = IS_JRUBY ||
19
+ ((OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
20
+ (OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1)
21
+
3
22
  class Socket
4
23
  def initialize(socket, engine)
5
24
  @socket = socket
6
25
  @engine = engine
26
+ @peercert = nil
27
+ @reuse = nil
7
28
  end
8
29
 
30
+ # @!attribute [r] to_io
9
31
  def to_io
10
32
  @socket
11
33
  end
12
34
 
35
+ def closed?
36
+ @socket.closed?
37
+ end
38
+
39
+ # Returns a two element array,
40
+ # first is protocol version (SSL_get_version),
41
+ # second is 'handshake' state (SSL_state_string)
42
+ #
43
+ # Used for dropping tcp connections to ssl.
44
+ # See OpenSSL ssl/ssl_stat.c SSL_state_string for info
45
+ # @!attribute [r] ssl_version_state
46
+ # @version 5.0.0
47
+ #
48
+ def ssl_version_state
49
+ IS_JRUBY ? [nil, nil] : @engine.ssl_vers_st
50
+ end
51
+
52
+ # Used to check the handshake status, in particular when a TCP connection
53
+ # is made with TLSv1.3 as an available protocol
54
+ # @version 5.0.0
55
+ def bad_tlsv1_3?
56
+ HAS_TLS1_3 && ssl_version_state == ['TLSv1.3', 'SSLERR']
57
+ end
58
+ private :bad_tlsv1_3?
59
+
13
60
  def readpartial(size)
14
61
  while true
15
62
  output = @engine.read
@@ -27,15 +74,40 @@ module Puma
27
74
  end
28
75
  end
29
76
 
30
- def read_nonblock(size)
77
+ def engine_read_all
78
+ output = @engine.read
79
+ while output and additional_output = @engine.read
80
+ output << additional_output
81
+ end
82
+ output
83
+ end
84
+
85
+ def read_nonblock(size, *_)
86
+ # *_ is to deal with keyword args that were added
87
+ # at some point (and being used in the wild)
31
88
  while true
32
- output = @engine.read
89
+ output = engine_read_all
33
90
  return output if output
34
91
 
35
- data = @socket.read_nonblock(size)
92
+ data = @socket.read_nonblock(size, exception: false)
93
+ if data == :wait_readable || data == :wait_writable
94
+ # It would make more sense to let @socket.read_nonblock raise
95
+ # EAGAIN if necessary but it seems like it'll misbehave on Windows.
96
+ # I don't have a Windows machine to debug this so I can't explain
97
+ # exactly whats happening in that OS. Please let me know if you
98
+ # find out!
99
+ #
100
+ # In the meantime, we can emulate the correct behavior by
101
+ # capturing :wait_readable & :wait_writable and raising EAGAIN
102
+ # ourselves.
103
+ raise IO::EAGAINWaitReadable
104
+ elsif data.nil?
105
+ raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
106
+ return nil
107
+ end
36
108
 
37
109
  @engine.inject(data)
38
- output = @engine.read
110
+ output = engine_read_all
39
111
 
40
112
  return output if output
41
113
 
@@ -46,94 +118,340 @@ module Puma
46
118
  end
47
119
 
48
120
  def write(data)
49
- need = data.bytesize
121
+ return 0 if data.empty?
122
+
123
+ data_size = data.bytesize
124
+ need = data_size
50
125
 
51
126
  while true
52
127
  wrote = @engine.write data
53
- enc = @engine.extract
54
128
 
55
- while enc
56
- @socket.write enc
57
- enc = @engine.extract
129
+ enc_wr = +''
130
+ while (enc = @engine.extract)
131
+ enc_wr << enc
58
132
  end
133
+ @socket.write enc_wr unless enc_wr.empty?
59
134
 
60
135
  need -= wrote
61
136
 
62
- return data.bytesize if need == 0
137
+ return data_size if need == 0
63
138
 
64
- data = data[need..-1]
139
+ data = data.byteslice(wrote..-1)
65
140
  end
66
141
  end
67
142
 
68
143
  alias_method :syswrite, :write
144
+ alias_method :<<, :write
145
+
146
+ # This is a temporary fix to deal with websockets code using
147
+ # write_nonblock.
148
+
149
+ # The problem with implementing it properly
150
+ # is that it means we'd have to have the ability to rewind
151
+ # an engine because after we write+extract, the socket
152
+ # write_nonblock call might raise an exception and later
153
+ # code would pass the same data in, but the engine would think
154
+ # it had already written the data in.
155
+ #
156
+ # So for the time being (and since write blocking is quite rare),
157
+ # go ahead and actually block in write_nonblock.
158
+ #
159
+ def write_nonblock(data, *_)
160
+ write data
161
+ end
69
162
 
70
163
  def flush
71
164
  @socket.flush
72
165
  end
73
166
 
74
167
  def close
75
- @socket.close
168
+ begin
169
+ unless @engine.shutdown
170
+ while alert_data = @engine.extract
171
+ @socket.write alert_data
172
+ end
173
+ end
174
+ rescue IOError, SystemCallError
175
+ # nothing
176
+ ensure
177
+ @socket.close
178
+ end
76
179
  end
77
180
 
181
+ # @!attribute [r] peeraddr
78
182
  def peeraddr
79
183
  @socket.peeraddr
80
184
  end
185
+
186
+ # OpenSSL is loaded in `MiniSSL::ContextBuilder` when
187
+ # `MiniSSL::Context#verify_mode` is not `VERIFY_NONE`.
188
+ # When `VERIFY_NONE`, `MiniSSL::Engine#peercert` is nil, regardless of
189
+ # whether the client sends a cert.
190
+ # @return [OpenSSL::X509::Certificate, nil]
191
+ # @!attribute [r] peercert
192
+ def peercert
193
+ return @peercert if @peercert
194
+
195
+ raw = @engine.peercert
196
+ return nil unless raw
197
+
198
+ @peercert = OpenSSL::X509::Certificate.new raw
199
+ end
200
+ end
201
+
202
+ if IS_JRUBY
203
+ OPENSSL_NO_SSL3 = false
204
+ OPENSSL_NO_TLS1 = false
81
205
  end
82
206
 
83
207
  class Context
84
208
  attr_accessor :verify_mode
209
+ attr_reader :no_tlsv1, :no_tlsv1_1
210
+
211
+ def initialize
212
+ @no_tlsv1 = false
213
+ @no_tlsv1_1 = false
214
+ @key = nil
215
+ @cert = nil
216
+ @key_pem = nil
217
+ @cert_pem = nil
218
+ @reuse = nil
219
+ @reuse_cache_size = nil
220
+ @reuse_timeout = nil
221
+ end
222
+
223
+ def check_file(file, desc)
224
+ raise ArgumentError, "#{desc} file '#{file}' does not exist" unless File.exist? file
225
+ raise ArgumentError, "#{desc} file '#{file}' is not readable" unless File.readable? file
226
+ end
227
+
228
+ if IS_JRUBY
229
+ # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
230
+ attr_reader :keystore
231
+ attr_reader :keystore_type
232
+ attr_accessor :keystore_pass
233
+ attr_reader :truststore
234
+ attr_reader :truststore_type
235
+ attr_accessor :truststore_pass
236
+ attr_reader :cipher_suites
237
+ attr_reader :protocols
238
+
239
+ def keystore=(keystore)
240
+ check_file keystore, 'Keystore'
241
+ @keystore = keystore
242
+ end
243
+
244
+ def truststore=(truststore)
245
+ # NOTE: historically truststore was assumed the same as keystore, this is kept for backwards
246
+ # compatibility, to rely on JVM's trust defaults we allow setting `truststore = :default`
247
+ unless truststore.eql?(:default)
248
+ raise ArgumentError, "No such truststore file '#{truststore}'" unless File.exist?(truststore)
249
+ end
250
+ @truststore = truststore
251
+ end
252
+
253
+ def keystore_type=(type)
254
+ raise ArgumentError, "Invalid keystore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
255
+ @keystore_type = type
256
+ end
257
+
258
+ def truststore_type=(type)
259
+ raise ArgumentError, "Invalid truststore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
260
+ @truststore_type = type
261
+ end
262
+
263
+ def cipher_suites=(list)
264
+ list = list.split(',').map(&:strip) if list.is_a?(String)
265
+ @cipher_suites = list
266
+ end
267
+
268
+ # aliases for backwards compatibility
269
+ alias_method :ssl_cipher_list, :cipher_suites
270
+ alias_method :ssl_cipher_list=, :cipher_suites=
85
271
 
86
- attr_reader :key
87
- attr_reader :cert
272
+ def protocols=(list)
273
+ list = list.split(',').map(&:strip) if list.is_a?(String)
274
+ @protocols = list
275
+ end
276
+
277
+ def check
278
+ raise "Keystore not configured" unless @keystore
279
+ # @truststore defaults to @keystore due backwards compatibility
280
+ end
281
+
282
+ else
283
+ # non-jruby Context properties
284
+ attr_reader :key
285
+ attr_reader :key_password_command
286
+ attr_reader :cert
287
+ attr_reader :ca
288
+ attr_reader :cert_pem
289
+ attr_reader :key_pem
290
+ attr_accessor :ssl_cipher_filter
291
+ attr_accessor :ssl_ciphersuites
292
+ attr_accessor :verification_flags
293
+
294
+ attr_reader :reuse, :reuse_cache_size, :reuse_timeout
295
+
296
+ def key=(key)
297
+ check_file key, 'Key'
298
+ @key = key
299
+ end
300
+
301
+ def key_password_command=(key_password_command)
302
+ @key_password_command = key_password_command
303
+ end
304
+
305
+ def cert=(cert)
306
+ check_file cert, 'Cert'
307
+ @cert = cert
308
+ end
309
+
310
+ def ca=(ca)
311
+ check_file ca, 'ca'
312
+ @ca = ca
313
+ end
314
+
315
+ def cert_pem=(cert_pem)
316
+ raise ArgumentError, "'cert_pem' is not a String" unless cert_pem.is_a? String
317
+ @cert_pem = cert_pem
318
+ end
319
+
320
+ def key_pem=(key_pem)
321
+ raise ArgumentError, "'key_pem' is not a String" unless key_pem.is_a? String
322
+ @key_pem = key_pem
323
+ end
324
+
325
+ def check
326
+ raise "Key not configured" if @key.nil? && @key_pem.nil?
327
+ raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
328
+ end
329
+
330
+ # Executes the command to return the password needed to decrypt the key.
331
+ def key_password
332
+ raise "Key password command not configured" if @key_password_command.nil?
333
+
334
+ stdout_str, stderr_str, status = Open3.capture3(@key_password_command)
335
+
336
+ return stdout_str.chomp if status.success?
337
+
338
+ raise "Key password failed with code #{status.exitstatus}: #{stderr_str}"
339
+ end
340
+
341
+ # Controls session reuse. Allowed values are as follows:
342
+ # * 'off' - matches the behavior of Puma 5.6 and earlier. This is included
343
+ # in case reuse 'on' is made the default in future Puma versions.
344
+ # * 'dflt' - sets session reuse on, with OpenSSL default cache size of
345
+ # 20k and default timeout of 300 seconds.
346
+ # * 's,t' - where s and t are integer strings, for size and timeout.
347
+ # * 's' - where s is an integer strings for size.
348
+ # * ',t' - where t is an integer strings for timeout.
349
+ #
350
+ def reuse=(reuse_str)
351
+ case reuse_str
352
+ when 'off'
353
+ @reuse = nil
354
+ when 'dflt'
355
+ @reuse = true
356
+ when /\A\d+\z/
357
+ @reuse = true
358
+ @reuse_cache_size = reuse_str.to_i
359
+ when /\A\d+,\d+\z/
360
+ @reuse = true
361
+ size, time = reuse_str.split ','
362
+ @reuse_cache_size = size.to_i
363
+ @reuse_timeout = time.to_i
364
+ when /\A,\d+\z/
365
+ @reuse = true
366
+ @reuse_timeout = reuse_str.delete(',').to_i
367
+ end
368
+ end
369
+ end
88
370
 
89
- def key=(key)
90
- raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
91
- @key = key
371
+ # disables TLSv1
372
+ # @!attribute [w] no_tlsv1=
373
+ def no_tlsv1=(tlsv1)
374
+ raise ArgumentError, "Invalid value of no_tlsv1=" unless ['true', 'false', true, false].include?(tlsv1)
375
+ @no_tlsv1 = tlsv1
92
376
  end
93
377
 
94
- def cert=(cert)
95
- raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
96
- @cert = cert
378
+ # disables TLSv1 and TLSv1.1. Overrides `#no_tlsv1=`
379
+ # @!attribute [w] no_tlsv1_1=
380
+ def no_tlsv1_1=(tlsv1_1)
381
+ raise ArgumentError, "Invalid value of no_tlsv1_1=" unless ['true', 'false', true, false].include?(tlsv1_1)
382
+ @no_tlsv1_1 = tlsv1_1
97
383
  end
384
+
98
385
  end
99
386
 
100
387
  VERIFY_NONE = 0
101
388
  VERIFY_PEER = 1
102
-
103
- #if defined?(JRUBY_VERSION)
104
- #class Engine
105
- #def self.server(key, cert)
106
- #new(key, cert)
107
- #end
108
- #end
109
- #end
389
+ VERIFY_FAIL_IF_NO_PEER_CERT = 2
390
+
391
+ # https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
392
+ # /* Certificate verify flags */
393
+ VERIFICATION_FLAGS = {
394
+ "USE_CHECK_TIME" => 0x2,
395
+ "CRL_CHECK" => 0x4,
396
+ "CRL_CHECK_ALL" => 0x8,
397
+ "IGNORE_CRITICAL" => 0x10,
398
+ "X509_STRICT" => 0x20,
399
+ "ALLOW_PROXY_CERTS" => 0x40,
400
+ "POLICY_CHECK" => 0x80,
401
+ "EXPLICIT_POLICY" => 0x100,
402
+ "INHIBIT_ANY" => 0x200,
403
+ "INHIBIT_MAP" => 0x400,
404
+ "NOTIFY_POLICY" => 0x800,
405
+ "EXTENDED_CRL_SUPPORT" => 0x1000,
406
+ "USE_DELTAS" => 0x2000,
407
+ "CHECK_SS_SIGNATURE" => 0x4000,
408
+ "TRUSTED_FIRST" => 0x8000,
409
+ "SUITEB_128_LOS_ONLY" => 0x10000,
410
+ "SUITEB_192_LOS" => 0x20000,
411
+ "SUITEB_128_LOS" => 0x30000,
412
+ "PARTIAL_CHAIN" => 0x80000,
413
+ "NO_ALT_CHAINS" => 0x100000,
414
+ "NO_CHECK_TIME" => 0x200000
415
+ }.freeze
110
416
 
111
417
  class Server
112
418
  def initialize(socket, ctx)
113
419
  @socket = socket
114
420
  @ctx = ctx
115
- end
116
-
117
- def to_io
118
- @socket
421
+ @eng_ctx = IS_JRUBY ? @ctx : SSLContext.new(ctx)
119
422
  end
120
423
 
121
424
  def accept
425
+ @ctx.check
122
426
  io = @socket.accept
123
- engine = Engine.server @ctx.key, @ctx.cert
124
-
427
+ engine = Engine.server @eng_ctx
125
428
  Socket.new io, engine
126
429
  end
127
430
 
128
431
  def accept_nonblock
432
+ @ctx.check
129
433
  io = @socket.accept_nonblock
130
- engine = Engine.server @ctx.key, @ctx.cert
131
-
434
+ engine = Engine.server @eng_ctx
132
435
  Socket.new io, engine
133
436
  end
134
437
 
438
+ # @!attribute [r] to_io
439
+ def to_io
440
+ @socket
441
+ end
442
+
443
+ # @!attribute [r] addr
444
+ # @version 5.0.0
445
+ def addr
446
+ @socket.addr
447
+ end
448
+
135
449
  def close
136
- @socket.close
450
+ @socket.close unless @socket.closed? # closed? call is for Windows
451
+ end
452
+
453
+ def closed?
454
+ @socket.closed?
137
455
  end
138
456
  end
139
457
  end
data/lib/puma/null_io.rb CHANGED
@@ -1,34 +1,101 @@
1
- module Puma
1
+ # frozen_string_literal: true
2
2
 
3
+ module Puma
3
4
  # Provides an IO-like object that always appears to contain no data.
4
5
  # Used as the value for rack.input when the request has no body.
5
6
  #
6
7
  class NullIO
7
- # Always returns nil
8
- #
9
8
  def gets
10
9
  nil
11
10
  end
12
11
 
13
- # Never yields
14
- #
12
+ def string
13
+ ""
14
+ end
15
+
15
16
  def each
16
17
  end
17
18
 
18
- # Mimics IO#read with no data
19
- #
20
- def read(count=nil,buffer=nil)
21
- (count && count > 0) ? nil : ""
19
+ def pos
20
+ 0
22
21
  end
23
22
 
24
- # Does nothing
23
+ # Mimics IO#read with no data.
25
24
  #
25
+ def read(length = nil, buffer = nil)
26
+ if length.to_i < 0
27
+ raise ArgumentError, "(negative length #{length} given)"
28
+ end
29
+
30
+ buffer = if buffer.nil?
31
+ "".b
32
+ else
33
+ String.try_convert(buffer) or raise TypeError, "no implicit conversion of #{buffer.class} into String"
34
+ end
35
+ buffer.clear
36
+ if length.to_i > 0
37
+ nil
38
+ else
39
+ buffer
40
+ end
41
+ end
42
+
26
43
  def rewind
27
44
  end
28
45
 
29
- # Does nothing
30
- #
46
+ def seek(pos, whence = 0)
47
+ raise ArgumentError, "negative length #{pos} given" if pos.negative?
48
+ 0
49
+ end
50
+
31
51
  def close
32
52
  end
53
+
54
+ def size
55
+ 0
56
+ end
57
+
58
+ def eof?
59
+ true
60
+ end
61
+
62
+ def sync
63
+ true
64
+ end
65
+
66
+ def sync=(v)
67
+ end
68
+
69
+ def puts(*ary)
70
+ end
71
+
72
+ def write(*ary)
73
+ end
74
+
75
+ def flush
76
+ self
77
+ end
78
+
79
+ # This is used as singleton class, so can't have state.
80
+ def closed?
81
+ false
82
+ end
83
+
84
+ def set_encoding(enc)
85
+ self
86
+ end
87
+
88
+ # per rack spec
89
+ def external_encoding
90
+ Encoding::ASCII_8BIT
91
+ end
92
+
93
+ def binmode
94
+ self
95
+ end
96
+
97
+ def binmode?
98
+ true
99
+ end
33
100
  end
34
101
  end