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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +8 -1
- data/.travis.yml +22 -29
- data/Gemfile +4 -5
- data/README.md +2 -1
- data/Rakefile +0 -2
- data/appveyor.yml +28 -15
- data/ext/libev/Changes +16 -0
- data/ext/libev/ev.c +105 -91
- data/ext/libev/ev.h +86 -80
- data/ext/libev/ev_epoll.c +1 -1
- data/ext/libev/ev_vars.h +2 -2
- data/ext/nio4r/nio4r.h +1 -1
- data/ext/nio4r/selector.c +5 -5
- data/lib/nio.rb +3 -3
- data/lib/nio/monitor.rb +10 -8
- data/lib/nio/selector.rb +3 -1
- data/lib/nio/version.rb +1 -1
- data/nio4r.gemspec +1 -1
- data/{tasks → rakelib}/extension.rake +2 -0
- data/{tasks → rakelib}/rspec.rake +0 -0
- data/{tasks → rakelib}/rubocop.rake +0 -0
- data/spec/nio/acceptables_spec.rb +3 -5
- data/spec/nio/bytebuffer_spec.rb +2 -3
- data/spec/nio/monitor_spec.rb +2 -2
- data/spec/nio/selectables/ssl_socket_spec.rb +47 -19
- data/spec/nio/selectables/tcp_socket_spec.rb +23 -17
- data/spec/nio/selectables/udp_socket_spec.rb +12 -7
- data/spec/nio/selector_spec.rb +11 -11
- data/spec/spec_helper.rb +4 -16
- data/spec/support/selectable_examples.rb +37 -17
- metadata +7 -10
- data/ext/libev/README.embed +0 -3
- data/ext/libev/test_libev_win32.c +0 -123
data/lib/nio/selector.rb
CHANGED
@@ -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
|
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?
|
data/lib/nio/version.rb
CHANGED
data/nio4r.gemspec
CHANGED
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",
|
24
|
-
TCPSocket.open("127.0.0.1",
|
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",
|
27
|
+
TCPServer.new("127.0.0.1", 0)
|
30
28
|
end
|
31
29
|
|
32
30
|
it_behaves_like "an NIO acceptable"
|
data/spec/nio/bytebuffer_spec.rb
CHANGED
@@ -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(:
|
292
|
-
let(:
|
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
|
|
data/spec/nio/monitor_spec.rb
CHANGED
@@ -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,
|
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(
|
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,
|
36
|
-
client = TCPSocket.open(addr,
|
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,
|
59
|
-
client = TCPSocket.new(addr,
|
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,
|
79
|
-
client = TCPSocket.new(addr,
|
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,
|
99
|
-
client = TCPSocket.new(addr,
|
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
|
-
#
|
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
|
-
|
138
|
-
|
139
|
-
|
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,
|
146
|
-
client = TCPSocket.new(addr,
|
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
|
-
|
163
|
-
|
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,
|
11
|
-
sock = TCPSocket.new(addr,
|
9
|
+
server = TCPServer.new(addr, 0)
|
10
|
+
sock = TCPSocket.new(addr, server.local_address.ip_port)
|
12
11
|
peer = server.accept
|
13
|
-
|
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,
|
19
|
-
sock = TCPSocket.new(addr,
|
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,
|
29
|
-
TCPSocket.new(addr,
|
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,
|
34
|
-
sock = TCPSocket.new(addr,
|
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,
|
65
|
-
client = TCPSocket.new(addr,
|
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
|
-
|
77
|
+
include_context NIO::Selector
|
78
|
+
|
79
|
+
it "selects writable when connected" do
|
75
80
|
begin
|
76
|
-
server = TCPServer.new(addr,
|
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
|
87
|
+
client.connect_nonblock server.local_address
|
84
88
|
end.to raise_exception Errno::EINPROGRESS
|
85
89
|
|
86
|
-
|
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
|
-
|
10
|
-
|
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",
|
13
|
+
peer.send("hi there", 0, "127.0.0.1", server.local_address.ip_port)
|
14
14
|
|
15
|
-
|
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",
|
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
|
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 "
|
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
|
|
data/spec/nio/selector_spec.rb
CHANGED
@@ -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(
|
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(
|
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
|
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(
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -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
|
-
|
23
|
-
|
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
|
-
|
15
|
+
config.expect_with :rspec do |c|
|
16
|
+
c.syntax = :expect
|
29
17
|
end
|
30
18
|
end
|