nio4r 2.3.1 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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