nio4r 2.3.1 → 2.4.0

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.
@@ -44,7 +44,9 @@ module NIO
44
44
  # * :w - is the IO writeable?
45
45
  # * :rw - is the IO either readable or writeable?
46
46
  def register(io, interest)
47
- io = IO.try_convert(io)
47
+ unless io.is_a? OpenSSL::SSL::SSLSocket
48
+ io = IO.try_convert(io)
49
+ end
48
50
 
49
51
  @lock.synchronize do
50
52
  raise IOError, "selector is closed" if closed?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NIO
4
- VERSION = "2.3.1".freeze
4
+ VERSION = "2.4.0"
5
5
  end
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
  spec.version = NIO::VERSION
22
22
 
23
- spec.required_ruby_version = ">= 2.2.2"
23
+ spec.required_ruby_version = ">= 2.3"
24
24
 
25
25
  if defined? JRUBY_VERSION
26
26
  spec.files << "lib/nio4r_ext.jar"
@@ -4,6 +4,8 @@ if defined? JRUBY_VERSION
4
4
  require "rake/javaextensiontask"
5
5
  Rake::JavaExtensionTask.new("nio4r_ext") do |ext|
6
6
  ext.ext_dir = "ext/nio4r"
7
+ ext.source_version = "1.8"
8
+ ext.target_version = "1.8"
7
9
  end
8
10
  else
9
11
  require "rake/extensiontask"
File without changes
File without changes
@@ -17,16 +17,14 @@ RSpec.describe "NIO acceptables" do
17
17
  end
18
18
 
19
19
  describe TCPServer do
20
- let(:port) { next_available_tcp_port }
21
-
22
20
  let :acceptable_subject do
23
- server = TCPServer.new("127.0.0.1", port)
24
- TCPSocket.open("127.0.0.1", port)
21
+ server = TCPServer.new("127.0.0.1", 0)
22
+ TCPSocket.open("127.0.0.1", server.local_address.ip_port)
25
23
  server
26
24
  end
27
25
 
28
26
  let :unacceptable_subject do
29
- TCPServer.new("127.0.0.1", port + 1)
27
+ TCPServer.new("127.0.0.1", 0)
30
28
  end
31
29
 
32
30
  it_behaves_like "an NIO acceptable"
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
- # rubocop:disable Metrics/BlockLength
6
5
  RSpec.describe NIO::ByteBuffer do
7
6
  let(:capacity) { 256 }
8
7
  let(:example_string) { "Testing 1 2 3..." }
@@ -288,8 +287,8 @@ RSpec.describe NIO::ByteBuffer do
288
287
 
289
288
  context "I/O" do
290
289
  let(:addr) { "127.0.0.1" }
291
- let(:port) { next_available_tcp_port }
292
- let(:server) { TCPServer.new(addr, port) }
290
+ let(:server) { TCPServer.new(addr, 0) }
291
+ let(:port) { server.local_address.ip_port }
293
292
  let(:client) { TCPSocket.new(addr, port) }
294
293
  let(:peer) { server_thread.value }
295
294
 
@@ -5,9 +5,9 @@ require "socket"
5
5
 
6
6
  RSpec.describe NIO::Monitor do
7
7
  let(:addr) { "127.0.0.1" }
8
- let(:port) { next_available_tcp_port }
9
8
 
10
- let(:reader) { TCPServer.new(addr, port) }
9
+ let(:reader) { TCPServer.new(addr, 0) }
10
+ let(:port) { reader.local_address.ip_port }
11
11
  let(:writer) { TCPSocket.new(addr, port) }
12
12
 
13
13
  let(:selector) { NIO::Selector.new }
@@ -4,10 +4,13 @@ require "spec_helper"
4
4
  require "openssl"
5
5
 
6
6
  RSpec.describe OpenSSL::SSL::SSLSocket do
7
+ before(:all) do
8
+ @tls = []
9
+ end
10
+
7
11
  let(:addr) { "127.0.0.1" }
8
- let(:port) { next_available_tcp_port }
9
12
 
10
- let(:ssl_key) { OpenSSL::PKey::RSA.new(1024) }
13
+ let(:ssl_key) { OpenSSL::PKey::RSA.new(2048) }
11
14
 
12
15
  let(:ssl_cert) do
13
16
  name = OpenSSL::X509::Name.new([%w[CN 127.0.0.1]])
@@ -28,12 +31,19 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
28
31
  OpenSSL::SSL::SSLContext.new.tap do |ctx|
29
32
  ctx.cert = ssl_cert
30
33
  ctx.key = ssl_key
34
+ unless @tls.empty?
35
+ if ctx.respond_to? :set_minmax_proto_version, true
36
+ ctx.max_version = @tls[0]
37
+ else
38
+ ctx.ssl_version = @tls[1]
39
+ end
40
+ end
31
41
  end
32
42
  end
33
43
 
34
44
  let :readable_subject do
35
- server = TCPServer.new(addr, port)
36
- client = TCPSocket.open(addr, port)
45
+ server = TCPServer.new(addr, 0)
46
+ client = TCPSocket.open(addr, server.local_address.ip_port)
37
47
  peer = server.accept
38
48
 
39
49
  ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
@@ -47,16 +57,17 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
47
57
 
48
58
  ssl_peer.accept
49
59
  ssl_peer << "data"
60
+ ssl_peer.flush
50
61
 
51
62
  thread.join
52
- pending "Failed to produce a readable SSL socket" unless select([ssl_client], [], [], 0)
53
63
 
64
+ pending "Failed to produce a readable socket" unless select([ssl_client], [], [], 10)
54
65
  ssl_client
55
66
  end
56
67
 
57
68
  let :unreadable_subject do
58
- server = TCPServer.new(addr, port)
59
- client = TCPSocket.new(addr, port)
69
+ server = TCPServer.new(addr, 0)
70
+ client = TCPSocket.new(addr, server.local_address.ip_port)
60
71
  peer = server.accept
61
72
 
62
73
  ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
@@ -70,13 +81,17 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
70
81
  ssl_peer.accept
71
82
  thread.join
72
83
 
84
+ if ssl_client.ssl_version == "TLSv1.3"
85
+ expect(ssl_client.read_nonblock(1, exception: false)).to eq(:wait_readable)
86
+ end
87
+
73
88
  pending "Failed to produce an unreadable socket" if select([ssl_client], [], [], 0)
74
89
  ssl_client
75
90
  end
76
91
 
77
92
  let :writable_subject do
78
- server = TCPServer.new(addr, port)
79
- client = TCPSocket.new(addr, port)
93
+ server = TCPServer.new(addr, 0)
94
+ client = TCPSocket.new(addr, server.local_address.ip_port)
80
95
  peer = server.accept
81
96
 
82
97
  ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
@@ -95,8 +110,8 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
95
110
  end
96
111
 
97
112
  let :unwritable_subject do
98
- server = TCPServer.new(addr, port)
99
- client = TCPSocket.new(addr, port)
113
+ server = TCPServer.new(addr, 0)
114
+ client = TCPSocket.new(addr, server.local_address.ip_port)
100
115
  peer = server.accept
101
116
 
102
117
  ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
@@ -128,22 +143,22 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
128
143
 
129
144
  # Once more for good measure!
130
145
  begin
131
- # ssl_client.write_nonblock "X" * 1024
146
+ # ssl_client.write_nonblock "X" * 1024
132
147
  loop { ssl_client.write_nonblock "X" * 1024 }
133
148
  rescue OpenSSL::SSL::SSLError
134
149
  end
135
150
 
136
151
  # Sanity check to make sure we actually produced an unwritable socket
137
- # if select([], [ssl_client], [], 0)
138
- # pending "Failed to produce an unwritable socket"
139
- # end
152
+ if select([], [ssl_client], [], 0)
153
+ pending "Failed to produce an unwritable socket"
154
+ end
140
155
 
141
156
  ssl_client
142
157
  end
143
158
 
144
159
  let :pair do
145
- server = TCPServer.new(addr, port)
146
- client = TCPSocket.new(addr, port)
160
+ server = TCPServer.new(addr, 0)
161
+ client = TCPSocket.new(addr, server.local_address.ip_port)
147
162
  peer = server.accept
148
163
 
149
164
  ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
@@ -159,6 +174,19 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
159
174
  [thread.value, ssl_peer]
160
175
  end
161
176
 
162
- it_behaves_like "an NIO selectable"
163
- it_behaves_like "an NIO selectable stream"
177
+ describe "using TLS 1.2" do
178
+ before(:all) do
179
+ @tls = %i[TLS1_2 TLSv1_2]
180
+ end
181
+ it_behaves_like "an NIO selectable"
182
+ it_behaves_like "an NIO selectable stream"
183
+ end
184
+
185
+ describe "using TLS 1.3", if: OpenSSL::SSL.const_defined?(:TLS1_3_VERSION) do
186
+ before(:all) do
187
+ @tls = %i[TLS1_3 TLSv1_3]
188
+ end
189
+ it_behaves_like "an NIO selectable"
190
+ it_behaves_like "an NIO selectable stream", true
191
+ end
164
192
  end
@@ -4,19 +4,22 @@ require "spec_helper"
4
4
 
5
5
  RSpec.describe TCPSocket do
6
6
  let(:addr) { "127.0.0.1" }
7
- let(:port) { next_available_tcp_port }
8
7
 
9
8
  let :readable_subject do
10
- server = TCPServer.new(addr, port)
11
- sock = TCPSocket.new(addr, port)
9
+ server = TCPServer.new(addr, 0)
10
+ sock = TCPSocket.new(addr, server.local_address.ip_port)
12
11
  peer = server.accept
13
- peer << "data"
12
+
13
+ peer << "Xdata"
14
+ peer.flush
15
+ sock.read(1)
16
+
14
17
  sock
15
18
  end
16
19
 
17
20
  let :unreadable_subject do
18
- TCPServer.new(addr, port)
19
- sock = TCPSocket.new(addr, port)
21
+ server = TCPServer.new(addr, 0)
22
+ sock = TCPSocket.new(addr, server.local_address.ip_port)
20
23
 
21
24
  # Sanity check to make sure we actually produced an unreadable socket
22
25
  pending "Failed to produce an unreadable socket" if select([sock], [], [], 0)
@@ -25,13 +28,13 @@ RSpec.describe TCPSocket do
25
28
  end
26
29
 
27
30
  let :writable_subject do
28
- TCPServer.new(addr, port)
29
- TCPSocket.new(addr, port)
31
+ server = TCPServer.new(addr, 0)
32
+ TCPSocket.new(addr, server.local_address.ip_port)
30
33
  end
31
34
 
32
35
  let :unwritable_subject do
33
- server = TCPServer.new(addr, port)
34
- sock = TCPSocket.new(addr, port)
36
+ server = TCPServer.new(addr, 0)
37
+ sock = TCPSocket.new(addr, server.local_address.ip_port)
35
38
 
36
39
  # TODO: close this socket
37
40
  _peer = server.accept
@@ -61,8 +64,8 @@ RSpec.describe TCPSocket do
61
64
  end
62
65
 
63
66
  let :pair do
64
- server = TCPServer.new(addr, port)
65
- client = TCPSocket.new(addr, port)
67
+ server = TCPServer.new(addr, 0)
68
+ client = TCPSocket.new(addr, server.local_address.ip_port)
66
69
  [client, server.accept]
67
70
  end
68
71
 
@@ -71,19 +74,22 @@ RSpec.describe TCPSocket do
71
74
  it_behaves_like "an NIO bidirectional stream"
72
75
 
73
76
  context :connect do
74
- it "selects writable when connected", retry: 5 do # retry: Flaky on OS X
77
+ include_context NIO::Selector
78
+
79
+ it "selects writable when connected" do
75
80
  begin
76
- server = TCPServer.new(addr, port)
77
- selector = NIO::Selector.new
81
+ server = TCPServer.new(addr, 0)
78
82
 
79
83
  client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
80
84
  monitor = selector.register(client, :w)
81
85
 
82
86
  expect do
83
- client.connect_nonblock Socket.sockaddr_in(port, addr)
87
+ client.connect_nonblock server.local_address
84
88
  end.to raise_exception Errno::EINPROGRESS
85
89
 
86
- expect(selector.select(0.001)).to include monitor
90
+ ready = selector.select(1)
91
+
92
+ expect(ready).to include monitor
87
93
  result = client.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR)
88
94
  expect(result.unpack("i").first).to be_zero
89
95
  ensure
@@ -6,32 +6,37 @@ RSpec.describe UDPSocket, if: !defined?(JRUBY_VERSION) do
6
6
  let(:udp_port) { 23_456 }
7
7
 
8
8
  let :readable_subject do
9
- sock = UDPSocket.new
10
- sock.bind("127.0.0.1", udp_port)
9
+ server = UDPSocket.new
10
+ server.bind("127.0.0.1", 0)
11
11
 
12
12
  peer = UDPSocket.new
13
- peer.send("hi there", 0, "127.0.0.1", udp_port)
13
+ peer.send("hi there", 0, "127.0.0.1", server.local_address.ip_port)
14
14
 
15
- sock
15
+ server
16
16
  end
17
17
 
18
18
  let :unreadable_subject do
19
19
  sock = UDPSocket.new
20
- sock.bind("127.0.0.1", udp_port + 1)
20
+ sock.bind("127.0.0.1", 0)
21
21
  sock
22
22
  end
23
23
 
24
24
  let :writable_subject do
25
+ server = UDPSocket.new
26
+ server.bind("127.0.0.1", 0)
27
+
25
28
  peer = UDPSocket.new
26
- peer.connect "127.0.0.1", udp_port
29
+ peer.connect("127.0.0.1", server.local_address.ip_port)
30
+
27
31
  cntr = 0
28
32
  begin
29
33
  peer.send("X" * 1024, 0)
30
34
  cntr += 1
31
35
  t = select [], [peer], [], 0
32
36
  rescue Errno::ECONNREFUSED => ex
33
- skip "Couln't make writable UDPSocket subject: #{ex.class}: #{ex}"
37
+ skip "Couldn't make writable UDPSocket subject: #{ex.class}: #{ex}"
34
38
  end while t && t[1].include?(peer) && cntr < 5
39
+
35
40
  peer
36
41
  end
37
42
 
@@ -3,12 +3,6 @@
3
3
  require "spec_helper"
4
4
  require "timeout"
5
5
 
6
- # Timeouts should be at least this precise (in seconds) to pass the tests
7
- # Typical precision should be better than this, but if it's worse it will fail
8
- # the tests
9
- TIMEOUT_PRECISION = 0.1
10
-
11
- # rubocop:disable Metrics/BlockLength
12
6
  RSpec.describe NIO::Selector do
13
7
  let(:pair) { IO.pipe }
14
8
  let(:reader) { pair.first }
@@ -96,31 +90,37 @@ RSpec.describe NIO::Selector do
96
90
  end
97
91
 
98
92
  context "timeouts" do
93
+ let(:select_precision) {0.2}
94
+ let(:payload) {"hi there"}
95
+
99
96
  it "waits for a timeout when selecting" do
100
97
  monitor = subject.register(reader, :r)
101
98
 
102
- payload = "hi there"
103
99
  writer << payload
104
100
 
105
101
  timeout = 0.5
106
102
  started_at = Time.now
107
103
  expect(subject.select(timeout)).to include monitor
108
- expect(Time.now - started_at).to be_within(TIMEOUT_PRECISION).of(0)
104
+ expect(Time.now - started_at).to be_within(select_precision).of(0)
109
105
  reader.read_nonblock(payload.size)
110
106
 
111
107
  started_at = Time.now
112
108
  expect(subject.select(timeout)).to be_nil
113
- expect(Time.now - started_at).to be_within(TIMEOUT_PRECISION).of(timeout)
109
+ expect(Time.now - started_at).to be_within(select_precision).of(timeout)
114
110
  end
115
111
 
116
112
  it "raises ArgumentError if given a negative timeout" do
117
113
  subject.register(reader, :r)
118
114
 
119
- expect { subject.select(-1) }.to raise_exception(ArgumentError)
115
+ expect do
116
+ subject.select(-1)
117
+ end.to raise_exception(ArgumentError)
120
118
  end
121
119
  end
122
120
 
123
121
  context "wakeup" do
122
+ let(:select_precision) {0.2}
123
+
124
124
  it "wakes up if signaled to from another thread" do
125
125
  subject.register(reader, :r)
126
126
 
@@ -134,7 +134,7 @@ RSpec.describe NIO::Selector do
134
134
  sleep timeout
135
135
  subject.wakeup
136
136
 
137
- expect(thread.value).to be_within(TIMEOUT_PRECISION).of(timeout)
137
+ expect(thread.value).to be_within(select_precision).of(timeout)
138
138
  end
139
139
 
140
140
  it "raises IOError if asked to wake up a closed selector" do
@@ -5,26 +5,14 @@ Coveralls.wear!
5
5
 
6
6
  require "nio"
7
7
  require "support/selectable_examples"
8
- require "rspec/retry"
9
8
 
10
9
  RSpec.configure do |config|
11
10
  config.disable_monkey_patching!
12
- config.verbose_retry = true
13
- config.display_try_failure_messages = true
14
- end
15
-
16
- $current_tcp_port = 10_000
17
-
18
- def next_available_tcp_port
19
- loop do
20
- $current_tcp_port += 1
21
11
 
22
- begin
23
- sock = Timeout.timeout(0.5) { TCPSocket.new("127.0.0.1", $current_tcp_port) }
24
- rescue Errno::ECONNREFUSED, Timeout::Error
25
- break $current_tcp_port
26
- end
12
+ # Enable flags like --only-failures and --next-failure
13
+ config.example_status_persistence_file_path = ".rspec_status"
27
14
 
28
- sock.close
15
+ config.expect_with :rspec do |c|
16
+ c.syntax = :expect
29
17
  end
30
18
  end