nio4r 2.5.8 → 2.7.4
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/{CHANGES.md → changes.md} +48 -0
- data/ext/nio4r/bytebuffer.c +75 -38
- data/ext/nio4r/extconf.rb +19 -1
- data/ext/nio4r/monitor.c +47 -22
- data/ext/nio4r/nio4r.h +1 -5
- data/ext/nio4r/nio4r_ext.c +4 -0
- data/ext/nio4r/org/nio4r/ByteBuffer.java +1 -1
- data/ext/nio4r/org/nio4r/Monitor.java +2 -2
- data/ext/nio4r/org/nio4r/Selector.java +2 -2
- data/ext/nio4r/selector.c +72 -48
- data/lib/nio/bytebuffer.rb +6 -0
- data/lib/nio/monitor.rb +7 -0
- data/lib/nio/selector.rb +15 -0
- data/lib/nio/version.rb +6 -1
- data/lib/nio.rb +9 -0
- data/lib/nio4r.rb +7 -0
- data/license.md +80 -0
- data/readme.md +91 -0
- data.tar.gz.sig +3 -0
- metadata +96 -75
- metadata.gz.sig +0 -0
- data/.github/workflows/workflow.yml +0 -47
- data/.gitignore +0 -21
- data/.rspec +0 -4
- data/.rubocop.yml +0 -100
- data/Gemfile +0 -18
- data/README.md +0 -133
- data/Rakefile +0 -8
- data/examples/echo_server.rb +0 -47
- data/logo.png +0 -0
- data/nio4r.gemspec +0 -42
- data/rakelib/extension.rake +0 -15
- data/rakelib/rspec.rake +0 -9
- data/rakelib/rubocop.rake +0 -5
- data/spec/nio/acceptables_spec.rb +0 -32
- data/spec/nio/bytebuffer_spec.rb +0 -354
- data/spec/nio/monitor_spec.rb +0 -162
- data/spec/nio/selectables/pipe_spec.rb +0 -47
- data/spec/nio/selectables/ssl_socket_spec.rb +0 -194
- data/spec/nio/selectables/tcp_socket_spec.rb +0 -101
- data/spec/nio/selectables/udp_socket_spec.rb +0 -48
- data/spec/nio/selector_spec.rb +0 -240
- data/spec/spec_helper.rb +0 -20
- data/spec/support/selectable_examples.rb +0 -85
@@ -1,194 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
RSpec.describe OpenSSL::SSL::SSLSocket do
|
6
|
-
|
7
|
-
require "openssl"
|
8
|
-
|
9
|
-
before(:all) do
|
10
|
-
@tls = []
|
11
|
-
end
|
12
|
-
|
13
|
-
let(:addr) { "127.0.0.1" }
|
14
|
-
|
15
|
-
let(:ssl_key) { OpenSSL::PKey::RSA.new(2048) }
|
16
|
-
|
17
|
-
let(:ssl_cert) do
|
18
|
-
name = OpenSSL::X509::Name.new([%w[CN 127.0.0.1]])
|
19
|
-
OpenSSL::X509::Certificate.new.tap do |cert|
|
20
|
-
cert.version = 2
|
21
|
-
cert.serial = 1
|
22
|
-
cert.issuer = name
|
23
|
-
cert.subject = name
|
24
|
-
cert.not_before = Time.now
|
25
|
-
cert.not_after = Time.now + (7 * 24 * 60 * 60)
|
26
|
-
cert.public_key = ssl_key.public_key
|
27
|
-
|
28
|
-
cert.sign(ssl_key, OpenSSL::Digest::SHA256.new)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
let(:ssl_server_context) do
|
33
|
-
OpenSSL::SSL::SSLContext.new.tap do |ctx|
|
34
|
-
ctx.cert = ssl_cert
|
35
|
-
ctx.key = ssl_key
|
36
|
-
unless @tls.empty?
|
37
|
-
if ctx.respond_to? :set_minmax_proto_version, true
|
38
|
-
ctx.max_version = @tls[0]
|
39
|
-
else
|
40
|
-
ctx.ssl_version = @tls[1]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
let :readable_subject do
|
47
|
-
server = TCPServer.new(addr, 0)
|
48
|
-
client = TCPSocket.open(addr, server.local_address.ip_port)
|
49
|
-
peer = server.accept
|
50
|
-
|
51
|
-
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
52
|
-
ssl_peer.sync_close = true
|
53
|
-
|
54
|
-
ssl_client = OpenSSL::SSL::SSLSocket.new(client)
|
55
|
-
ssl_client.sync_close = true
|
56
|
-
|
57
|
-
# SSLSocket#connect and #accept are blocking calls.
|
58
|
-
thread = Thread.new { ssl_client.connect }
|
59
|
-
|
60
|
-
ssl_peer.accept
|
61
|
-
ssl_peer << "data"
|
62
|
-
ssl_peer.flush
|
63
|
-
|
64
|
-
thread.join
|
65
|
-
|
66
|
-
pending "Failed to produce a readable socket" unless select([ssl_client], [], [], 10)
|
67
|
-
ssl_client
|
68
|
-
end
|
69
|
-
|
70
|
-
let :unreadable_subject do
|
71
|
-
server = TCPServer.new(addr, 0)
|
72
|
-
client = TCPSocket.new(addr, server.local_address.ip_port)
|
73
|
-
peer = server.accept
|
74
|
-
|
75
|
-
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
76
|
-
ssl_peer.sync_close = true
|
77
|
-
|
78
|
-
ssl_client = OpenSSL::SSL::SSLSocket.new(client)
|
79
|
-
ssl_client.sync_close = true
|
80
|
-
|
81
|
-
# SSLSocket#connect and #accept are blocking calls.
|
82
|
-
thread = Thread.new { ssl_client.connect }
|
83
|
-
ssl_peer.accept
|
84
|
-
thread.join
|
85
|
-
|
86
|
-
if ssl_client.ssl_version == "TLSv1.3"
|
87
|
-
expect(ssl_client.read_nonblock(1, exception: false)).to eq(:wait_readable)
|
88
|
-
end
|
89
|
-
|
90
|
-
pending "Failed to produce an unreadable socket" if select([ssl_client], [], [], 0)
|
91
|
-
ssl_client
|
92
|
-
end
|
93
|
-
|
94
|
-
let :writable_subject do
|
95
|
-
server = TCPServer.new(addr, 0)
|
96
|
-
client = TCPSocket.new(addr, server.local_address.ip_port)
|
97
|
-
peer = server.accept
|
98
|
-
|
99
|
-
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
100
|
-
ssl_peer.sync_close = true
|
101
|
-
|
102
|
-
ssl_client = OpenSSL::SSL::SSLSocket.new(client)
|
103
|
-
ssl_client.sync_close = true
|
104
|
-
|
105
|
-
# SSLSocket#connect and #accept are blocking calls.
|
106
|
-
thread = Thread.new { ssl_client.connect }
|
107
|
-
|
108
|
-
ssl_peer.accept
|
109
|
-
thread.join
|
110
|
-
|
111
|
-
ssl_client
|
112
|
-
end
|
113
|
-
|
114
|
-
let :unwritable_subject do
|
115
|
-
server = TCPServer.new(addr, 0)
|
116
|
-
client = TCPSocket.new(addr, server.local_address.ip_port)
|
117
|
-
peer = server.accept
|
118
|
-
|
119
|
-
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
120
|
-
ssl_peer.sync_close = true
|
121
|
-
|
122
|
-
ssl_client = OpenSSL::SSL::SSLSocket.new(client)
|
123
|
-
ssl_client.sync_close = true
|
124
|
-
|
125
|
-
# SSLSocket#connect and #accept are blocking calls.
|
126
|
-
thread = Thread.new { ssl_client.connect }
|
127
|
-
|
128
|
-
ssl_peer.accept
|
129
|
-
thread.join
|
130
|
-
|
131
|
-
cntr = 0
|
132
|
-
begin
|
133
|
-
count = ssl_client.write_nonblock "X" * 1024
|
134
|
-
expect(count).not_to eq(0)
|
135
|
-
cntr += 1
|
136
|
-
t = select [], [ssl_client], [], 0
|
137
|
-
rescue IO::WaitReadable, IO::WaitWritable
|
138
|
-
pending "SSL will report writable but not accept writes"
|
139
|
-
end while t && t[1].include?(ssl_client) && cntr < 30
|
140
|
-
|
141
|
-
# I think the kernel might manage to drain its buffer a bit even after
|
142
|
-
# the socket first goes unwritable. Attempt to sleep past this and then
|
143
|
-
# attempt to write again
|
144
|
-
sleep 0.1
|
145
|
-
|
146
|
-
# Once more for good measure!
|
147
|
-
begin
|
148
|
-
# ssl_client.write_nonblock "X" * 1024
|
149
|
-
loop { ssl_client.write_nonblock "X" * 1024 }
|
150
|
-
rescue OpenSSL::SSL::SSLError
|
151
|
-
end
|
152
|
-
|
153
|
-
# Sanity check to make sure we actually produced an unwritable socket
|
154
|
-
if select([], [ssl_client], [], 0)
|
155
|
-
pending "Failed to produce an unwritable socket"
|
156
|
-
end
|
157
|
-
|
158
|
-
ssl_client
|
159
|
-
end
|
160
|
-
|
161
|
-
let :pair do
|
162
|
-
server = TCPServer.new(addr, 0)
|
163
|
-
client = TCPSocket.new(addr, server.local_address.ip_port)
|
164
|
-
peer = server.accept
|
165
|
-
|
166
|
-
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
167
|
-
ssl_peer.sync_close = true
|
168
|
-
|
169
|
-
ssl_client = OpenSSL::SSL::SSLSocket.new(client)
|
170
|
-
ssl_client.sync_close = true
|
171
|
-
|
172
|
-
# SSLSocket#connect and #accept are blocking calls.
|
173
|
-
thread = Thread.new { ssl_client.connect }
|
174
|
-
ssl_peer.accept
|
175
|
-
|
176
|
-
[thread.value, ssl_peer]
|
177
|
-
end
|
178
|
-
|
179
|
-
describe "using TLS 1.2" do
|
180
|
-
before(:all) do
|
181
|
-
@tls = %i[TLS1_2 TLSv1_2]
|
182
|
-
end
|
183
|
-
it_behaves_like "an NIO selectable"
|
184
|
-
it_behaves_like "an NIO selectable stream"
|
185
|
-
end
|
186
|
-
|
187
|
-
describe "using TLS 1.3", if: OpenSSL::SSL.const_defined?(:TLS1_3_VERSION) do
|
188
|
-
before(:all) do
|
189
|
-
@tls = %i[TLS1_3 TLSv1_3]
|
190
|
-
end
|
191
|
-
it_behaves_like "an NIO selectable"
|
192
|
-
it_behaves_like "an NIO selectable stream", true
|
193
|
-
end
|
194
|
-
end
|
@@ -1,101 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
RSpec.describe TCPSocket do
|
6
|
-
let(:addr) { "127.0.0.1" }
|
7
|
-
|
8
|
-
let :readable_subject do
|
9
|
-
server = TCPServer.new(addr, 0)
|
10
|
-
sock = TCPSocket.new(addr, server.local_address.ip_port)
|
11
|
-
peer = server.accept
|
12
|
-
|
13
|
-
peer << "Xdata"
|
14
|
-
peer.flush
|
15
|
-
sock.read(1)
|
16
|
-
|
17
|
-
sock
|
18
|
-
end
|
19
|
-
|
20
|
-
let :unreadable_subject do
|
21
|
-
server = TCPServer.new(addr, 0)
|
22
|
-
sock = TCPSocket.new(addr, server.local_address.ip_port)
|
23
|
-
|
24
|
-
# Sanity check to make sure we actually produced an unreadable socket
|
25
|
-
pending "Failed to produce an unreadable socket" if select([sock], [], [], 0)
|
26
|
-
|
27
|
-
sock
|
28
|
-
end
|
29
|
-
|
30
|
-
let :writable_subject do
|
31
|
-
server = TCPServer.new(addr, 0)
|
32
|
-
TCPSocket.new(addr, server.local_address.ip_port)
|
33
|
-
end
|
34
|
-
|
35
|
-
let :unwritable_subject do
|
36
|
-
server = TCPServer.new(addr, 0)
|
37
|
-
sock = TCPSocket.new(addr, server.local_address.ip_port)
|
38
|
-
|
39
|
-
# TODO: close this socket
|
40
|
-
_peer = server.accept
|
41
|
-
|
42
|
-
loop do
|
43
|
-
sock.write_nonblock "X" * 1024
|
44
|
-
_, writers = Kernel.select([], [sock], [], 0)
|
45
|
-
|
46
|
-
break unless writers && writers.include?(sock)
|
47
|
-
end
|
48
|
-
|
49
|
-
# HAX: I think the kernel might manage to drain its buffer a bit even after
|
50
|
-
# the socket first goes unwritable. Attempt to sleep past this and then
|
51
|
-
# attempt to write again
|
52
|
-
sleep 0.1
|
53
|
-
|
54
|
-
# Once more for good measure!
|
55
|
-
begin
|
56
|
-
sock.write_nonblock "X" * 1024
|
57
|
-
rescue Errno::EWOULDBLOCK
|
58
|
-
end
|
59
|
-
|
60
|
-
# Sanity check to make sure we actually produced an unwritable socket
|
61
|
-
pending "Failed to produce an unwritable socket" if select([], [sock], [], 0)
|
62
|
-
|
63
|
-
sock
|
64
|
-
end
|
65
|
-
|
66
|
-
let :pair do
|
67
|
-
server = TCPServer.new(addr, 0)
|
68
|
-
client = TCPSocket.new(addr, server.local_address.ip_port)
|
69
|
-
[client, server.accept]
|
70
|
-
end
|
71
|
-
|
72
|
-
it_behaves_like "an NIO selectable"
|
73
|
-
it_behaves_like "an NIO selectable stream"
|
74
|
-
it_behaves_like "an NIO bidirectional stream"
|
75
|
-
|
76
|
-
context :connect do
|
77
|
-
include_context NIO::Selector
|
78
|
-
|
79
|
-
it "selects writable when connected" do
|
80
|
-
begin
|
81
|
-
server = TCPServer.new(addr, 0)
|
82
|
-
|
83
|
-
client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
84
|
-
monitor = selector.register(client, :w)
|
85
|
-
|
86
|
-
expect do
|
87
|
-
client.connect_nonblock server.local_address
|
88
|
-
end.to raise_exception Errno::EINPROGRESS
|
89
|
-
|
90
|
-
ready = selector.select(1)
|
91
|
-
|
92
|
-
expect(ready).to include monitor
|
93
|
-
result = client.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR)
|
94
|
-
expect(result.unpack("i").first).to be_zero
|
95
|
-
ensure
|
96
|
-
server.close rescue nil
|
97
|
-
selector.close rescue nil
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
RSpec.describe UDPSocket, if: !defined?(JRUBY_VERSION) do
|
6
|
-
let(:udp_port) { 23_456 }
|
7
|
-
|
8
|
-
let :readable_subject do
|
9
|
-
server = UDPSocket.new
|
10
|
-
server.bind("127.0.0.1", 0)
|
11
|
-
|
12
|
-
peer = UDPSocket.new
|
13
|
-
peer.send("hi there", 0, "127.0.0.1", server.local_address.ip_port)
|
14
|
-
|
15
|
-
server
|
16
|
-
end
|
17
|
-
|
18
|
-
let :unreadable_subject do
|
19
|
-
sock = UDPSocket.new
|
20
|
-
sock.bind("127.0.0.1", 0)
|
21
|
-
sock
|
22
|
-
end
|
23
|
-
|
24
|
-
let :writable_subject do
|
25
|
-
server = UDPSocket.new
|
26
|
-
server.bind("127.0.0.1", 0)
|
27
|
-
|
28
|
-
peer = UDPSocket.new
|
29
|
-
peer.connect("127.0.0.1", server.local_address.ip_port)
|
30
|
-
|
31
|
-
cntr = 0
|
32
|
-
begin
|
33
|
-
peer.send("X" * 1024, 0)
|
34
|
-
cntr += 1
|
35
|
-
t = select [], [peer], [], 0
|
36
|
-
rescue Errno::ECONNREFUSED => e
|
37
|
-
skip "Couldn't make writable UDPSocket subject: #{e.class}: #{e}"
|
38
|
-
end while t && t[1].include?(peer) && cntr < 5
|
39
|
-
|
40
|
-
peer
|
41
|
-
end
|
42
|
-
|
43
|
-
let :unwritable_subject do
|
44
|
-
pending "come up with a UDPSocket that's blocked on writing"
|
45
|
-
end
|
46
|
-
|
47
|
-
it_behaves_like "an NIO selectable"
|
48
|
-
end
|
data/spec/nio/selector_spec.rb
DELETED
@@ -1,240 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
require "timeout"
|
5
|
-
|
6
|
-
RSpec.describe NIO::Selector do
|
7
|
-
let(:pair) { IO.pipe }
|
8
|
-
let(:reader) { pair.first }
|
9
|
-
let(:writer) { pair.last }
|
10
|
-
|
11
|
-
context ".backends" do
|
12
|
-
it "knows all supported backends" do
|
13
|
-
expect(described_class.backends).to be_a Array
|
14
|
-
expect(described_class.backends.first).to be_a Symbol
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
context "#initialize" do
|
19
|
-
it "allows explicitly specifying a backend" do |example|
|
20
|
-
backend = described_class.backends.first
|
21
|
-
selector = described_class.new(backend)
|
22
|
-
expect(selector.backend).to eq backend
|
23
|
-
|
24
|
-
example.reporter.message "Supported backends: #{described_class.backends}"
|
25
|
-
end
|
26
|
-
|
27
|
-
it "automatically selects a backend if none or nil is specified" do
|
28
|
-
expect(described_class.new.backend).to eq described_class.new(nil).backend
|
29
|
-
end
|
30
|
-
|
31
|
-
it "raises ArgumentError if given an invalid backend" do
|
32
|
-
expect { described_class.new(:derp) }.to raise_error ArgumentError
|
33
|
-
end
|
34
|
-
|
35
|
-
it "raises TypeError if given a non-Symbol parameter" do
|
36
|
-
expect { described_class.new(42).to raise_error TypeError }
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
context "backend" do
|
41
|
-
it "knows its backend" do |example|
|
42
|
-
expect(subject.backend).to be_a Symbol
|
43
|
-
|
44
|
-
example.reporter.message "Current backend: #{subject.backend}"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
context "register" do
|
49
|
-
it "registers IO objects" do
|
50
|
-
monitor = subject.register(reader, :r)
|
51
|
-
expect(monitor).not_to be_closed
|
52
|
-
end
|
53
|
-
|
54
|
-
it "raises TypeError if asked to register non-IO objects" do
|
55
|
-
expect { subject.register(42, :r) }.to raise_exception TypeError
|
56
|
-
end
|
57
|
-
|
58
|
-
it "raises when asked to register after closing" do
|
59
|
-
subject.close
|
60
|
-
expect { subject.register(reader, :r) }.to raise_exception IOError
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
it "knows which IO objects are registered" do
|
65
|
-
subject.register(reader, :r)
|
66
|
-
expect(subject).to be_registered(reader)
|
67
|
-
expect(subject).not_to be_registered(writer)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "deregisters IO objects" do
|
71
|
-
subject.register(reader, :r)
|
72
|
-
|
73
|
-
monitor = subject.deregister(reader)
|
74
|
-
expect(subject).not_to be_registered(reader)
|
75
|
-
expect(monitor).to be_closed
|
76
|
-
end
|
77
|
-
|
78
|
-
it "allows deregistering closed IO objects" do
|
79
|
-
subject.register(reader, :r)
|
80
|
-
reader.close
|
81
|
-
|
82
|
-
expect do
|
83
|
-
subject.deregister(reader)
|
84
|
-
end.not_to raise_error
|
85
|
-
end
|
86
|
-
|
87
|
-
it "reports if it is empty" do
|
88
|
-
expect(subject).to be_empty
|
89
|
-
subject.register(reader, :r)
|
90
|
-
expect(subject).not_to be_empty
|
91
|
-
end
|
92
|
-
|
93
|
-
# This spec might seem a bit silly, but this actually something the
|
94
|
-
# Java NIO API specifically precludes that we need to work around
|
95
|
-
it "allows reregistration of the same IO object across select calls" do
|
96
|
-
monitor = subject.register(reader, :r)
|
97
|
-
writer << "ohai"
|
98
|
-
|
99
|
-
expect(subject.select).to include monitor
|
100
|
-
expect(reader.read(4)).to eq("ohai")
|
101
|
-
subject.deregister(reader)
|
102
|
-
|
103
|
-
new_monitor = subject.register(reader, :r)
|
104
|
-
writer << "thar"
|
105
|
-
expect(subject.select).to include new_monitor
|
106
|
-
expect(reader.read(4)).to eq("thar")
|
107
|
-
end
|
108
|
-
|
109
|
-
context "timeouts" do
|
110
|
-
let(:select_precision) {0.2}
|
111
|
-
let(:timeout) {2.0}
|
112
|
-
let(:payload) {"hi there"}
|
113
|
-
|
114
|
-
it "waits for timeout when selecting from empty selector" do
|
115
|
-
started_at = Time.now
|
116
|
-
expect(subject.select(timeout)).to be_nil
|
117
|
-
expect(Time.now - started_at).to be_within(select_precision).of(timeout)
|
118
|
-
end
|
119
|
-
|
120
|
-
it "waits for a timeout when selecting with reader" do
|
121
|
-
monitor = subject.register(reader, :r)
|
122
|
-
|
123
|
-
writer << payload
|
124
|
-
|
125
|
-
started_at = Time.now
|
126
|
-
expect(subject.select(timeout)).to include monitor
|
127
|
-
expect(Time.now - started_at).to be_within(select_precision).of(0)
|
128
|
-
reader.read_nonblock(payload.size)
|
129
|
-
|
130
|
-
started_at = Time.now
|
131
|
-
expect(subject.select(timeout)).to be_nil
|
132
|
-
expect(Time.now - started_at).to be_within(select_precision).of(timeout)
|
133
|
-
end
|
134
|
-
|
135
|
-
it "raises ArgumentError if given a negative timeout" do
|
136
|
-
subject.register(reader, :r)
|
137
|
-
|
138
|
-
expect do
|
139
|
-
subject.select(-1)
|
140
|
-
end.to raise_exception(ArgumentError)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
context "wakeup" do
|
145
|
-
let(:select_precision) {0.2}
|
146
|
-
|
147
|
-
it "wakes up if signaled to from another thread" do
|
148
|
-
subject.register(reader, :r)
|
149
|
-
|
150
|
-
thread = Thread.new do
|
151
|
-
started_at = Time.now
|
152
|
-
expect(subject.select).to eq []
|
153
|
-
Time.now - started_at
|
154
|
-
end
|
155
|
-
|
156
|
-
timeout = 0.1
|
157
|
-
sleep timeout
|
158
|
-
subject.wakeup
|
159
|
-
|
160
|
-
expect(thread.value).to be_within(select_precision).of(timeout)
|
161
|
-
end
|
162
|
-
|
163
|
-
it "raises IOError if asked to wake up a closed selector" do
|
164
|
-
subject.close
|
165
|
-
expect(subject).to be_closed
|
166
|
-
|
167
|
-
expect { subject.wakeup }.to raise_exception IOError
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
context "select" do
|
172
|
-
it "does not block on super small precision intervals" do
|
173
|
-
wait_interval = 1e-4
|
174
|
-
|
175
|
-
expect do
|
176
|
-
Timeout.timeout(2) do
|
177
|
-
subject.select(wait_interval)
|
178
|
-
end
|
179
|
-
end.not_to raise_error
|
180
|
-
end
|
181
|
-
|
182
|
-
it "selects IO objects" do
|
183
|
-
writer << "ohai"
|
184
|
-
unready = IO.pipe.first
|
185
|
-
|
186
|
-
reader_monitor = subject.register(reader, :r)
|
187
|
-
unready_monitor = subject.register(unready, :r)
|
188
|
-
|
189
|
-
selected = subject.select(0)
|
190
|
-
expect(selected.size).to eq(1)
|
191
|
-
expect(selected).to include reader_monitor
|
192
|
-
expect(selected).not_to include unready_monitor
|
193
|
-
end
|
194
|
-
|
195
|
-
it "selects closed IO objects" do
|
196
|
-
monitor = subject.register(reader, :r)
|
197
|
-
expect(subject.select(0)).to be_nil
|
198
|
-
|
199
|
-
thread = Thread.new { subject.select }
|
200
|
-
Thread.pass while thread.status && thread.status != "sleep"
|
201
|
-
|
202
|
-
writer.close
|
203
|
-
selected = thread.value
|
204
|
-
expect(selected).to include monitor
|
205
|
-
end
|
206
|
-
|
207
|
-
it "iterates across selected objects with a block" do
|
208
|
-
readable1, writer = IO.pipe
|
209
|
-
writer << "ohai"
|
210
|
-
|
211
|
-
readable2, writer = IO.pipe
|
212
|
-
writer << "ohai"
|
213
|
-
|
214
|
-
unreadable = IO.pipe.first
|
215
|
-
|
216
|
-
monitor1 = subject.register(readable1, :r)
|
217
|
-
monitor2 = subject.register(readable2, :r)
|
218
|
-
monitor3 = subject.register(unreadable, :r)
|
219
|
-
|
220
|
-
readables = []
|
221
|
-
result = subject.select { |monitor| readables << monitor }
|
222
|
-
expect(result).to eq(2)
|
223
|
-
|
224
|
-
expect(readables).to include monitor1
|
225
|
-
expect(readables).to include monitor2
|
226
|
-
expect(readables).not_to include monitor3
|
227
|
-
end
|
228
|
-
|
229
|
-
it "raises IOError if asked to select on a closed selector" do
|
230
|
-
subject.close
|
231
|
-
|
232
|
-
expect { subject.select(0) }.to raise_exception IOError
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
it "closes" do
|
237
|
-
subject.close
|
238
|
-
expect(subject).to be_closed
|
239
|
-
end
|
240
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "coveralls"
|
4
|
-
Coveralls.wear!
|
5
|
-
|
6
|
-
require "nio"
|
7
|
-
require "support/selectable_examples"
|
8
|
-
|
9
|
-
RSpec.configure do |config|
|
10
|
-
config.disable_monkey_patching!
|
11
|
-
|
12
|
-
# Enable flags like --only-failures and --next-failure
|
13
|
-
config.example_status_persistence_file_path = ".rspec_status"
|
14
|
-
|
15
|
-
config.filter_run_when_matching :focus
|
16
|
-
|
17
|
-
config.expect_with :rspec do |c|
|
18
|
-
c.syntax = :expect
|
19
|
-
end
|
20
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.shared_context NIO::Selector do
|
4
|
-
let(:selector) {@selector = NIO::Selector.new}
|
5
|
-
|
6
|
-
after(:each) do
|
7
|
-
if defined?(@selector)
|
8
|
-
@selector.close
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
RSpec.shared_context "an NIO selectable" do
|
14
|
-
include_context NIO::Selector
|
15
|
-
|
16
|
-
it "selects readable objects" do
|
17
|
-
monitor = selector.register(readable_subject, :r)
|
18
|
-
ready = selector.select(1)
|
19
|
-
expect(ready).to be_an Enumerable
|
20
|
-
expect(ready).to include monitor
|
21
|
-
end
|
22
|
-
|
23
|
-
it "does not select unreadable objects" do
|
24
|
-
selector.register(unreadable_subject, :r)
|
25
|
-
expect(selector.select(0)).to be_nil
|
26
|
-
end
|
27
|
-
|
28
|
-
it "selects writable objects" do
|
29
|
-
monitor = selector.register(writable_subject, :w)
|
30
|
-
ready = selector.select(1)
|
31
|
-
expect(ready).to be_an Enumerable
|
32
|
-
expect(ready).to include monitor
|
33
|
-
end
|
34
|
-
|
35
|
-
it "does not select unwritable objects" do
|
36
|
-
selector.register(unwritable_subject, :w)
|
37
|
-
ready = selector.select(0)
|
38
|
-
expect(ready).to be_nil
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
RSpec.shared_context "an NIO selectable stream" do |is_tls13|
|
43
|
-
include_context NIO::Selector
|
44
|
-
|
45
|
-
let(:stream) { pair.first }
|
46
|
-
let(:peer) { pair.last }
|
47
|
-
|
48
|
-
it "selects readable when the other end closes" do
|
49
|
-
monitor = selector.register(stream, :r)
|
50
|
-
unless is_tls13
|
51
|
-
expect(selector.select(1)).to be_nil
|
52
|
-
end
|
53
|
-
|
54
|
-
peer.close
|
55
|
-
# Wait and give the TCP session time to close
|
56
|
-
expect(selector.select(0.1)).to include monitor
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
RSpec.shared_context "an NIO bidirectional stream" do
|
61
|
-
include_context NIO::Selector
|
62
|
-
|
63
|
-
let(:stream) {pair.first}
|
64
|
-
let(:peer) {pair.last}
|
65
|
-
|
66
|
-
it "selects readable and writable" do
|
67
|
-
selector.register(readable_subject, :rw)
|
68
|
-
|
69
|
-
selector.select(1) do |monitor|
|
70
|
-
expect(monitor.readiness).to eq(:rw)
|
71
|
-
end
|
72
|
-
|
73
|
-
readable_subject.close
|
74
|
-
end
|
75
|
-
|
76
|
-
it "keeps readiness after the selectable has been closed" do
|
77
|
-
selector.register(readable_subject, :rw)
|
78
|
-
|
79
|
-
selector.select(1) do |monitor|
|
80
|
-
expect(monitor.readiness).to eq(:rw)
|
81
|
-
readable_subject.close
|
82
|
-
expect(monitor.readiness).to eq(:rw)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|