nio4r 2.5.7 → 2.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -0
- data/{CHANGES.md → changes.md} +48 -0
- data/ext/libev/ev_iouring.c +2 -2
- 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/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 +0 -0
- metadata +95 -74
- 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 -19
- 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
|