jun-puma 1.0.0-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 (87) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +2897 -0
  3. data/LICENSE +29 -0
  4. data/README.md +475 -0
  5. data/bin/puma +10 -0
  6. data/bin/puma-wild +25 -0
  7. data/bin/pumactl +12 -0
  8. data/docs/architecture.md +74 -0
  9. data/docs/compile_options.md +55 -0
  10. data/docs/deployment.md +102 -0
  11. data/docs/fork_worker.md +35 -0
  12. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  13. data/docs/images/puma-connection-flow.png +0 -0
  14. data/docs/images/puma-general-arch.png +0 -0
  15. data/docs/jungle/README.md +9 -0
  16. data/docs/jungle/rc.d/README.md +74 -0
  17. data/docs/jungle/rc.d/puma +61 -0
  18. data/docs/jungle/rc.d/puma.conf +10 -0
  19. data/docs/kubernetes.md +78 -0
  20. data/docs/nginx.md +80 -0
  21. data/docs/plugins.md +38 -0
  22. data/docs/rails_dev_mode.md +28 -0
  23. data/docs/restart.md +65 -0
  24. data/docs/signals.md +98 -0
  25. data/docs/stats.md +142 -0
  26. data/docs/systemd.md +253 -0
  27. data/docs/testing_benchmarks_local_files.md +150 -0
  28. data/docs/testing_test_rackup_ci_files.md +36 -0
  29. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  30. data/ext/puma_http11/ext_help.h +15 -0
  31. data/ext/puma_http11/extconf.rb +80 -0
  32. data/ext/puma_http11/http11_parser.c +1057 -0
  33. data/ext/puma_http11/http11_parser.h +65 -0
  34. data/ext/puma_http11/http11_parser.java.rl +145 -0
  35. data/ext/puma_http11/http11_parser.rl +149 -0
  36. data/ext/puma_http11/http11_parser_common.rl +54 -0
  37. data/ext/puma_http11/mini_ssl.c +842 -0
  38. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  39. data/ext/puma_http11/org/jruby/puma/Http11.java +228 -0
  40. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +455 -0
  41. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +509 -0
  42. data/ext/puma_http11/puma_http11.c +495 -0
  43. data/lib/puma/app/status.rb +96 -0
  44. data/lib/puma/binder.rb +502 -0
  45. data/lib/puma/cli.rb +247 -0
  46. data/lib/puma/client.rb +682 -0
  47. data/lib/puma/cluster/worker.rb +180 -0
  48. data/lib/puma/cluster/worker_handle.rb +96 -0
  49. data/lib/puma/cluster.rb +616 -0
  50. data/lib/puma/commonlogger.rb +115 -0
  51. data/lib/puma/configuration.rb +390 -0
  52. data/lib/puma/const.rb +307 -0
  53. data/lib/puma/control_cli.rb +316 -0
  54. data/lib/puma/detect.rb +45 -0
  55. data/lib/puma/dsl.rb +1425 -0
  56. data/lib/puma/error_logger.rb +113 -0
  57. data/lib/puma/events.rb +57 -0
  58. data/lib/puma/io_buffer.rb +46 -0
  59. data/lib/puma/jruby_restart.rb +11 -0
  60. data/lib/puma/json_serialization.rb +96 -0
  61. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  62. data/lib/puma/launcher.rb +488 -0
  63. data/lib/puma/log_writer.rb +147 -0
  64. data/lib/puma/minissl/context_builder.rb +96 -0
  65. data/lib/puma/minissl.rb +459 -0
  66. data/lib/puma/null_io.rb +84 -0
  67. data/lib/puma/plugin/systemd.rb +90 -0
  68. data/lib/puma/plugin/tmp_restart.rb +36 -0
  69. data/lib/puma/plugin.rb +111 -0
  70. data/lib/puma/puma_http11.jar +0 -0
  71. data/lib/puma/rack/builder.rb +297 -0
  72. data/lib/puma/rack/urlmap.rb +93 -0
  73. data/lib/puma/rack_default.rb +24 -0
  74. data/lib/puma/reactor.rb +125 -0
  75. data/lib/puma/request.rb +688 -0
  76. data/lib/puma/runner.rb +213 -0
  77. data/lib/puma/sd_notify.rb +149 -0
  78. data/lib/puma/server.rb +680 -0
  79. data/lib/puma/single.rb +69 -0
  80. data/lib/puma/state_file.rb +68 -0
  81. data/lib/puma/thread_pool.rb +434 -0
  82. data/lib/puma/util.rb +141 -0
  83. data/lib/puma.rb +78 -0
  84. data/lib/rack/handler/puma.rb +144 -0
  85. data/tools/Dockerfile +16 -0
  86. data/tools/trickletest.rb +44 -0
  87. metadata +153 -0
@@ -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
@@ -0,0 +1,459 @@
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
+
13
+ module Puma
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
+
22
+ class Socket
23
+ def initialize(socket, engine)
24
+ @socket = socket
25
+ @engine = engine
26
+ @peercert = nil
27
+ @reuse = nil
28
+ end
29
+
30
+ # @!attribute [r] to_io
31
+ def to_io
32
+ @socket
33
+ end
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
+
60
+ def readpartial(size)
61
+ while true
62
+ output = @engine.read
63
+ return output if output
64
+
65
+ data = @socket.readpartial(size)
66
+ @engine.inject(data)
67
+ output = @engine.read
68
+
69
+ return output if output
70
+
71
+ while neg_data = @engine.extract
72
+ @socket.write neg_data
73
+ end
74
+ end
75
+ end
76
+
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)
88
+ while true
89
+ output = engine_read_all
90
+ return output if output
91
+
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
108
+
109
+ @engine.inject(data)
110
+ output = engine_read_all
111
+
112
+ return output if output
113
+
114
+ while neg_data = @engine.extract
115
+ @socket.write neg_data
116
+ end
117
+ end
118
+ end
119
+
120
+ def write(data)
121
+ return 0 if data.empty?
122
+
123
+ data_size = data.bytesize
124
+ need = data_size
125
+
126
+ while true
127
+ wrote = @engine.write data
128
+
129
+ enc_wr = +''
130
+ while (enc = @engine.extract)
131
+ enc_wr << enc
132
+ end
133
+ @socket.write enc_wr unless enc_wr.empty?
134
+
135
+ need -= wrote
136
+
137
+ return data_size if need == 0
138
+
139
+ data = data.byteslice(wrote..-1)
140
+ end
141
+ end
142
+
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
162
+
163
+ def flush
164
+ @socket.flush
165
+ end
166
+
167
+ def 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
+ Puma::Util.purge_interrupt_queue
176
+ # nothing
177
+ ensure
178
+ @socket.close
179
+ end
180
+ end
181
+
182
+ # @!attribute [r] peeraddr
183
+ def peeraddr
184
+ @socket.peeraddr
185
+ end
186
+
187
+ # OpenSSL is loaded in `MiniSSL::ContextBuilder` when
188
+ # `MiniSSL::Context#verify_mode` is not `VERIFY_NONE`.
189
+ # When `VERIFY_NONE`, `MiniSSL::Engine#peercert` is nil, regardless of
190
+ # whether the client sends a cert.
191
+ # @return [OpenSSL::X509::Certificate, nil]
192
+ # @!attribute [r] peercert
193
+ def peercert
194
+ return @peercert if @peercert
195
+
196
+ raw = @engine.peercert
197
+ return nil unless raw
198
+
199
+ @peercert = OpenSSL::X509::Certificate.new raw
200
+ end
201
+ end
202
+
203
+ if IS_JRUBY
204
+ OPENSSL_NO_SSL3 = false
205
+ OPENSSL_NO_TLS1 = false
206
+ end
207
+
208
+ class Context
209
+ attr_accessor :verify_mode
210
+ attr_reader :no_tlsv1, :no_tlsv1_1
211
+
212
+ def initialize
213
+ @no_tlsv1 = false
214
+ @no_tlsv1_1 = false
215
+ @key = nil
216
+ @cert = nil
217
+ @key_pem = nil
218
+ @cert_pem = nil
219
+ @reuse = nil
220
+ @reuse_cache_size = nil
221
+ @reuse_timeout = nil
222
+ end
223
+
224
+ def check_file(file, desc)
225
+ raise ArgumentError, "#{desc} file '#{file}' does not exist" unless File.exist? file
226
+ raise ArgumentError, "#{desc} file '#{file}' is not readable" unless File.readable? file
227
+ end
228
+
229
+ if IS_JRUBY
230
+ # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
231
+ attr_reader :keystore
232
+ attr_reader :keystore_type
233
+ attr_accessor :keystore_pass
234
+ attr_reader :truststore
235
+ attr_reader :truststore_type
236
+ attr_accessor :truststore_pass
237
+ attr_reader :cipher_suites
238
+ attr_reader :protocols
239
+
240
+ def keystore=(keystore)
241
+ check_file keystore, 'Keystore'
242
+ @keystore = keystore
243
+ end
244
+
245
+ def truststore=(truststore)
246
+ # NOTE: historically truststore was assumed the same as keystore, this is kept for backwards
247
+ # compatibility, to rely on JVM's trust defaults we allow setting `truststore = :default`
248
+ unless truststore.eql?(:default)
249
+ raise ArgumentError, "No such truststore file '#{truststore}'" unless File.exist?(truststore)
250
+ end
251
+ @truststore = truststore
252
+ end
253
+
254
+ def keystore_type=(type)
255
+ raise ArgumentError, "Invalid keystore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
256
+ @keystore_type = type
257
+ end
258
+
259
+ def truststore_type=(type)
260
+ raise ArgumentError, "Invalid truststore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
261
+ @truststore_type = type
262
+ end
263
+
264
+ def cipher_suites=(list)
265
+ list = list.split(',').map(&:strip) if list.is_a?(String)
266
+ @cipher_suites = list
267
+ end
268
+
269
+ # aliases for backwards compatibility
270
+ alias_method :ssl_cipher_list, :cipher_suites
271
+ alias_method :ssl_cipher_list=, :cipher_suites=
272
+
273
+ def protocols=(list)
274
+ list = list.split(',').map(&:strip) if list.is_a?(String)
275
+ @protocols = list
276
+ end
277
+
278
+ def check
279
+ raise "Keystore not configured" unless @keystore
280
+ # @truststore defaults to @keystore due backwards compatibility
281
+ end
282
+
283
+ else
284
+ # non-jruby Context properties
285
+ attr_reader :key
286
+ attr_reader :key_password_command
287
+ attr_reader :cert
288
+ attr_reader :ca
289
+ attr_reader :cert_pem
290
+ attr_reader :key_pem
291
+ attr_accessor :ssl_cipher_filter
292
+ attr_accessor :ssl_ciphersuites
293
+ attr_accessor :verification_flags
294
+
295
+ attr_reader :reuse, :reuse_cache_size, :reuse_timeout
296
+
297
+ def key=(key)
298
+ check_file key, 'Key'
299
+ @key = key
300
+ end
301
+
302
+ def key_password_command=(key_password_command)
303
+ @key_password_command = key_password_command
304
+ end
305
+
306
+ def cert=(cert)
307
+ check_file cert, 'Cert'
308
+ @cert = cert
309
+ end
310
+
311
+ def ca=(ca)
312
+ check_file ca, 'ca'
313
+ @ca = ca
314
+ end
315
+
316
+ def cert_pem=(cert_pem)
317
+ raise ArgumentError, "'cert_pem' is not a String" unless cert_pem.is_a? String
318
+ @cert_pem = cert_pem
319
+ end
320
+
321
+ def key_pem=(key_pem)
322
+ raise ArgumentError, "'key_pem' is not a String" unless key_pem.is_a? String
323
+ @key_pem = key_pem
324
+ end
325
+
326
+ def check
327
+ raise "Key not configured" if @key.nil? && @key_pem.nil?
328
+ raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
329
+ end
330
+
331
+ # Executes the command to return the password needed to decrypt the key.
332
+ def key_password
333
+ raise "Key password command not configured" if @key_password_command.nil?
334
+
335
+ stdout_str, stderr_str, status = Open3.capture3(@key_password_command)
336
+
337
+ return stdout_str.chomp if status.success?
338
+
339
+ raise "Key password failed with code #{status.exitstatus}: #{stderr_str}"
340
+ end
341
+
342
+ # Controls session reuse. Allowed values are as follows:
343
+ # * 'off' - matches the behavior of Puma 5.6 and earlier. This is included
344
+ # in case reuse 'on' is made the default in future Puma versions.
345
+ # * 'dflt' - sets session reuse on, with OpenSSL default cache size of
346
+ # 20k and default timeout of 300 seconds.
347
+ # * 's,t' - where s and t are integer strings, for size and timeout.
348
+ # * 's' - where s is an integer strings for size.
349
+ # * ',t' - where t is an integer strings for timeout.
350
+ #
351
+ def reuse=(reuse_str)
352
+ case reuse_str
353
+ when 'off'
354
+ @reuse = nil
355
+ when 'dflt'
356
+ @reuse = true
357
+ when /\A\d+\z/
358
+ @reuse = true
359
+ @reuse_cache_size = reuse_str.to_i
360
+ when /\A\d+,\d+\z/
361
+ @reuse = true
362
+ size, time = reuse_str.split ','
363
+ @reuse_cache_size = size.to_i
364
+ @reuse_timeout = time.to_i
365
+ when /\A,\d+\z/
366
+ @reuse = true
367
+ @reuse_timeout = reuse_str.delete(',').to_i
368
+ end
369
+ end
370
+ end
371
+
372
+ # disables TLSv1
373
+ # @!attribute [w] no_tlsv1=
374
+ def no_tlsv1=(tlsv1)
375
+ raise ArgumentError, "Invalid value of no_tlsv1=" unless ['true', 'false', true, false].include?(tlsv1)
376
+ @no_tlsv1 = tlsv1
377
+ end
378
+
379
+ # disables TLSv1 and TLSv1.1. Overrides `#no_tlsv1=`
380
+ # @!attribute [w] no_tlsv1_1=
381
+ def no_tlsv1_1=(tlsv1_1)
382
+ raise ArgumentError, "Invalid value of no_tlsv1_1=" unless ['true', 'false', true, false].include?(tlsv1_1)
383
+ @no_tlsv1_1 = tlsv1_1
384
+ end
385
+
386
+ end
387
+
388
+ VERIFY_NONE = 0
389
+ VERIFY_PEER = 1
390
+ VERIFY_FAIL_IF_NO_PEER_CERT = 2
391
+
392
+ # https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
393
+ # /* Certificate verify flags */
394
+ VERIFICATION_FLAGS = {
395
+ "USE_CHECK_TIME" => 0x2,
396
+ "CRL_CHECK" => 0x4,
397
+ "CRL_CHECK_ALL" => 0x8,
398
+ "IGNORE_CRITICAL" => 0x10,
399
+ "X509_STRICT" => 0x20,
400
+ "ALLOW_PROXY_CERTS" => 0x40,
401
+ "POLICY_CHECK" => 0x80,
402
+ "EXPLICIT_POLICY" => 0x100,
403
+ "INHIBIT_ANY" => 0x200,
404
+ "INHIBIT_MAP" => 0x400,
405
+ "NOTIFY_POLICY" => 0x800,
406
+ "EXTENDED_CRL_SUPPORT" => 0x1000,
407
+ "USE_DELTAS" => 0x2000,
408
+ "CHECK_SS_SIGNATURE" => 0x4000,
409
+ "TRUSTED_FIRST" => 0x8000,
410
+ "SUITEB_128_LOS_ONLY" => 0x10000,
411
+ "SUITEB_192_LOS" => 0x20000,
412
+ "SUITEB_128_LOS" => 0x30000,
413
+ "PARTIAL_CHAIN" => 0x80000,
414
+ "NO_ALT_CHAINS" => 0x100000,
415
+ "NO_CHECK_TIME" => 0x200000
416
+ }.freeze
417
+
418
+ class Server
419
+ def initialize(socket, ctx)
420
+ @socket = socket
421
+ @ctx = ctx
422
+ @eng_ctx = IS_JRUBY ? @ctx : SSLContext.new(ctx)
423
+ end
424
+
425
+ def accept
426
+ @ctx.check
427
+ io = @socket.accept
428
+ engine = Engine.server @eng_ctx
429
+ Socket.new io, engine
430
+ end
431
+
432
+ def accept_nonblock
433
+ @ctx.check
434
+ io = @socket.accept_nonblock
435
+ engine = Engine.server @eng_ctx
436
+ Socket.new io, engine
437
+ end
438
+
439
+ # @!attribute [r] to_io
440
+ def to_io
441
+ @socket
442
+ end
443
+
444
+ # @!attribute [r] addr
445
+ # @version 5.0.0
446
+ def addr
447
+ @socket.addr
448
+ end
449
+
450
+ def close
451
+ @socket.close unless @socket.closed? # closed? call is for Windows
452
+ end
453
+
454
+ def closed?
455
+ @socket.closed?
456
+ end
457
+ end
458
+ end
459
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ # Provides an IO-like object that always appears to contain no data.
5
+ # Used as the value for rack.input when the request has no body.
6
+ #
7
+ class NullIO
8
+ def gets
9
+ nil
10
+ end
11
+
12
+ def string
13
+ ""
14
+ end
15
+
16
+ def each
17
+ end
18
+
19
+ def pos
20
+ 0
21
+ end
22
+
23
+ # Mimics IO#read with no data.
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
+
43
+ def rewind
44
+ end
45
+
46
+ def seek(pos, whence = 0)
47
+ raise ArgumentError, "negative length #{pos} given" if pos.negative?
48
+ 0
49
+ end
50
+
51
+ def close
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
+ end
84
+ end