nio4r 2.3.1 → 2.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/workflow.yml +43 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +37 -11
- data/CHANGES.md +40 -0
- data/Gemfile +5 -6
- data/README.md +53 -7
- data/Rakefile +0 -2
- data/examples/echo_server.rb +2 -2
- data/ext/libev/Changes +35 -0
- data/ext/libev/README +2 -1
- data/ext/libev/ev.c +213 -151
- data/ext/libev/ev.h +95 -88
- data/ext/libev/ev_epoll.c +26 -15
- data/ext/libev/ev_kqueue.c +11 -5
- data/ext/libev/ev_linuxaio.c +642 -0
- data/ext/libev/ev_poll.c +13 -8
- data/ext/libev/ev_port.c +5 -2
- data/ext/libev/ev_vars.h +14 -3
- data/ext/libev/ev_wrap.h +16 -0
- data/ext/nio4r/extconf.rb +2 -0
- data/ext/nio4r/monitor.c +1 -0
- data/ext/nio4r/nio4r.h +1 -1
- data/ext/nio4r/org/nio4r/Selector.java +5 -1
- data/ext/nio4r/selector.c +5 -5
- data/lib/nio.rb +3 -3
- data/lib/nio/bytebuffer.rb +4 -0
- data/lib/nio/monitor.rb +10 -8
- data/lib/nio/selector.rb +3 -1
- data/lib/nio/version.rb +1 -1
- data/nio4r.gemspec +10 -2
- 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 -4
- data/spec/nio/monitor_spec.rb +2 -2
- data/spec/nio/selectables/ssl_socket_spec.rb +50 -20
- data/spec/nio/selectables/tcp_socket_spec.rb +23 -17
- data/spec/nio/selectables/udp_socket_spec.rb +13 -8
- data/spec/nio/selector_spec.rb +34 -16
- data/spec/spec_helper.rb +4 -16
- data/spec/support/selectable_examples.rb +37 -17
- metadata +18 -17
- data/.travis.yml +0 -36
- data/LICENSE.txt +0 -20
- data/appveyor.yml +0 -27
- data/ext/libev/README.embed +0 -3
- data/ext/libev/test_libev_win32.c +0 -123
data/ext/libev/ev_poll.c
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* libev poll fd activity backend
|
3
3
|
*
|
4
|
-
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
|
4
|
+
* Copyright (c) 2007,2008,2009,2010,2011,2016,2019 Marc Alexander Lehmann <libev@schmorp.de>
|
5
5
|
* All rights reserved.
|
6
6
|
*
|
7
7
|
* Redistribution and use in source and binary forms, with or without modifica-
|
@@ -41,10 +41,12 @@
|
|
41
41
|
|
42
42
|
inline_size
|
43
43
|
void
|
44
|
-
|
44
|
+
array_needsize_pollidx (int *base, int offset, int count)
|
45
45
|
{
|
46
|
-
/*
|
47
|
-
* to
|
46
|
+
/* using memset (.., -1, ...) is tempting, we we try
|
47
|
+
* to be ultraportable
|
48
|
+
*/
|
49
|
+
base += offset;
|
48
50
|
while (count--)
|
49
51
|
*base++ = -1;
|
50
52
|
}
|
@@ -57,14 +59,14 @@ poll_modify (EV_P_ int fd, int oev, int nev)
|
|
57
59
|
if (oev == nev)
|
58
60
|
return;
|
59
61
|
|
60
|
-
array_needsize (int, pollidxs, pollidxmax, fd + 1,
|
62
|
+
array_needsize (int, pollidxs, pollidxmax, fd + 1, array_needsize_pollidx);
|
61
63
|
|
62
64
|
idx = pollidxs [fd];
|
63
65
|
|
64
66
|
if (idx < 0) /* need to allocate a new pollfd */
|
65
67
|
{
|
66
68
|
pollidxs [fd] = idx = pollcnt++;
|
67
|
-
array_needsize (struct pollfd, polls, pollmax, pollcnt,
|
69
|
+
array_needsize (struct pollfd, polls, pollmax, pollcnt, array_needsize_noinit);
|
68
70
|
polls [idx].fd = fd;
|
69
71
|
}
|
70
72
|
|
@@ -108,14 +110,17 @@ poll_poll (EV_P_ ev_tstamp timeout)
|
|
108
110
|
else
|
109
111
|
for (p = polls; res; ++p)
|
110
112
|
{
|
111
|
-
assert (("libev: poll
|
113
|
+
assert (("libev: poll returned illegal result, broken BSD kernel?", p < polls + pollcnt));
|
112
114
|
|
113
115
|
if (expect_false (p->revents)) /* this expect is debatable */
|
114
116
|
{
|
115
117
|
--res;
|
116
118
|
|
117
119
|
if (expect_false (p->revents & POLLNVAL))
|
118
|
-
|
120
|
+
{
|
121
|
+
assert (("libev: poll found invalid fd in poll set", 0));
|
122
|
+
fd_kill (EV_A_ p->fd);
|
123
|
+
}
|
119
124
|
else
|
120
125
|
fd_event (
|
121
126
|
EV_A_
|
data/ext/libev/ev_port.c
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* libev solaris event port backend
|
3
3
|
*
|
4
|
-
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
|
4
|
+
* Copyright (c) 2007,2008,2009,2010,2011,2019 Marc Alexander Lehmann <libev@schmorp.de>
|
5
5
|
* All rights reserved.
|
6
6
|
*
|
7
7
|
* Redistribution and use in source and binary forms, with or without modifica-
|
@@ -69,7 +69,10 @@ port_associate_and_check (EV_P_ int fd, int ev)
|
|
69
69
|
)
|
70
70
|
{
|
71
71
|
if (errno == EBADFD)
|
72
|
-
|
72
|
+
{
|
73
|
+
assert (("libev: port_associate found invalid fd", errno != EBADFD));
|
74
|
+
fd_kill (EV_A_ fd);
|
75
|
+
}
|
73
76
|
else
|
74
77
|
ev_syserr ("(libev) port_associate");
|
75
78
|
}
|
data/ext/libev/ev_vars.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* loop member variable declarations
|
3
3
|
*
|
4
|
-
* Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann <libev@schmorp.de>
|
4
|
+
* Copyright (c) 2007,2008,2009,2010,2011,2012,2013,2019 Marc Alexander Lehmann <libev@schmorp.de>
|
5
5
|
* All rights reserved.
|
6
6
|
*
|
7
7
|
* Redistribution and use in source and binary forms, with or without modifica-
|
@@ -107,6 +107,17 @@ VARx(int, epoll_epermcnt)
|
|
107
107
|
VARx(int, epoll_epermmax)
|
108
108
|
#endif
|
109
109
|
|
110
|
+
#if EV_USE_LINUXAIO || EV_GENWRAP
|
111
|
+
VARx(aio_context_t, linuxaio_ctx)
|
112
|
+
VARx(int, linuxaio_iteration)
|
113
|
+
VARx(struct aniocb **, linuxaio_iocbps)
|
114
|
+
VARx(int, linuxaio_iocbpmax)
|
115
|
+
VARx(struct iocb **, linuxaio_submits)
|
116
|
+
VARx(int, linuxaio_submitcnt)
|
117
|
+
VARx(int, linuxaio_submitmax)
|
118
|
+
VARx(ev_io, linuxaio_epoll_w)
|
119
|
+
#endif
|
120
|
+
|
110
121
|
#if EV_USE_KQUEUE || EV_GENWRAP
|
111
122
|
VARx(pid_t, kqueue_fd_pid)
|
112
123
|
VARx(struct kevent *, kqueue_changes)
|
@@ -195,8 +206,8 @@ VARx(unsigned int, loop_depth) /* #ev_run enters - #ev_run leaves */
|
|
195
206
|
|
196
207
|
VARx(void *, userdata)
|
197
208
|
/* C++ doesn't support the ev_loop_callback typedef here. stinks. */
|
198
|
-
VAR (release_cb, void (*release_cb)(EV_P)
|
199
|
-
VAR (acquire_cb, void (*acquire_cb)(EV_P)
|
209
|
+
VAR (release_cb, void (*release_cb)(EV_P) EV_NOEXCEPT)
|
210
|
+
VAR (acquire_cb, void (*acquire_cb)(EV_P) EV_NOEXCEPT)
|
200
211
|
VAR (invoke_cb , ev_loop_callback invoke_cb)
|
201
212
|
#endif
|
202
213
|
|
data/ext/libev/ev_wrap.h
CHANGED
@@ -50,6 +50,14 @@
|
|
50
50
|
#define kqueue_eventmax ((loop)->kqueue_eventmax)
|
51
51
|
#define kqueue_events ((loop)->kqueue_events)
|
52
52
|
#define kqueue_fd_pid ((loop)->kqueue_fd_pid)
|
53
|
+
#define linuxaio_ctx ((loop)->linuxaio_ctx)
|
54
|
+
#define linuxaio_epoll_w ((loop)->linuxaio_epoll_w)
|
55
|
+
#define linuxaio_iocbpmax ((loop)->linuxaio_iocbpmax)
|
56
|
+
#define linuxaio_iocbps ((loop)->linuxaio_iocbps)
|
57
|
+
#define linuxaio_iteration ((loop)->linuxaio_iteration)
|
58
|
+
#define linuxaio_submitcnt ((loop)->linuxaio_submitcnt)
|
59
|
+
#define linuxaio_submitmax ((loop)->linuxaio_submitmax)
|
60
|
+
#define linuxaio_submits ((loop)->linuxaio_submits)
|
53
61
|
#define loop_count ((loop)->loop_count)
|
54
62
|
#define loop_depth ((loop)->loop_depth)
|
55
63
|
#define loop_done ((loop)->loop_done)
|
@@ -149,6 +157,14 @@
|
|
149
157
|
#undef kqueue_eventmax
|
150
158
|
#undef kqueue_events
|
151
159
|
#undef kqueue_fd_pid
|
160
|
+
#undef linuxaio_ctx
|
161
|
+
#undef linuxaio_epoll_w
|
162
|
+
#undef linuxaio_iocbpmax
|
163
|
+
#undef linuxaio_iocbps
|
164
|
+
#undef linuxaio_iteration
|
165
|
+
#undef linuxaio_submitcnt
|
166
|
+
#undef linuxaio_submitmax
|
167
|
+
#undef linuxaio_submits
|
152
168
|
#undef loop_count
|
153
169
|
#undef loop_depth
|
154
170
|
#undef loop_done
|
data/ext/nio4r/extconf.rb
CHANGED
@@ -4,6 +4,7 @@ require "rubygems"
|
|
4
4
|
|
5
5
|
# Write a dummy Makefile on Windows because we use the pure Ruby implementation there
|
6
6
|
if Gem.win_platform?
|
7
|
+
require "devkit" if RUBY_PLATFORM.include?("mingw")
|
7
8
|
File.write("Makefile", "all install::\n")
|
8
9
|
File.write("nio4r_ext.so", "")
|
9
10
|
exit
|
@@ -13,6 +14,7 @@ require "mkmf"
|
|
13
14
|
|
14
15
|
have_header("unistd.h")
|
15
16
|
|
17
|
+
$defs << "-DEV_USE_LINUXAIO" if have_header("linux/aio_abi.h")
|
16
18
|
$defs << "-DEV_USE_SELECT" if have_header("sys/select.h")
|
17
19
|
$defs << "-DEV_USE_POLL" if have_type("port_event_t", "poll.h")
|
18
20
|
$defs << "-DEV_USE_EPOLL" if have_header("sys/epoll.h")
|
data/ext/nio4r/monitor.c
CHANGED
data/ext/nio4r/nio4r.h
CHANGED
@@ -19,6 +19,7 @@ import org.jruby.anno.JRubyMethod;
|
|
19
19
|
import org.jruby.runtime.Block;
|
20
20
|
import org.jruby.runtime.ThreadContext;
|
21
21
|
import org.jruby.runtime.builtin.IRubyObject;
|
22
|
+
import org.jruby.util.io.OpenFile;
|
22
23
|
|
23
24
|
import org.nio4r.Monitor;
|
24
25
|
|
@@ -136,7 +137,10 @@ public class Selector extends RubyObject {
|
|
136
137
|
@JRubyMethod
|
137
138
|
public IRubyObject deregister(ThreadContext context, IRubyObject io) {
|
138
139
|
Ruby runtime = context.getRuntime();
|
139
|
-
|
140
|
+
OpenFile file = RubyIO.convertToIO(context, io).getOpenFileInitialized();
|
141
|
+
if (file.fd() == null)
|
142
|
+
return context.nil;
|
143
|
+
Channel rawChannel = file.channel();
|
140
144
|
|
141
145
|
if(!(rawChannel instanceof SelectableChannel)) {
|
142
146
|
throw runtime.newArgumentError("not a selectable IO object");
|
data/ext/nio4r/selector.c
CHANGED
@@ -52,8 +52,8 @@ static VALUE NIO_Selector_close_synchronized(VALUE *args);
|
|
52
52
|
static VALUE NIO_Selector_closed_synchronized(VALUE *args);
|
53
53
|
|
54
54
|
static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout);
|
55
|
-
static void NIO_Selector_timeout_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents);
|
56
|
-
static void NIO_Selector_wakeup_callback(ev_loop *ev_loop, struct ev_io *io, int revents);
|
55
|
+
static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
|
56
|
+
static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents);
|
57
57
|
|
58
58
|
/* Default number of slots in the buffer for selected monitors */
|
59
59
|
#define INITIAL_READY_BUFFER 32
|
@@ -530,12 +530,12 @@ static VALUE NIO_Selector_is_empty(VALUE self)
|
|
530
530
|
|
531
531
|
|
532
532
|
/* Called whenever a timeout fires on the event loop */
|
533
|
-
static void NIO_Selector_timeout_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents)
|
533
|
+
static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
|
534
534
|
{
|
535
535
|
}
|
536
536
|
|
537
537
|
/* Called whenever a wakeup request is sent to a selector */
|
538
|
-
static void NIO_Selector_wakeup_callback(ev_loop *ev_loop, struct ev_io *io, int revents)
|
538
|
+
static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents)
|
539
539
|
{
|
540
540
|
char buffer[128];
|
541
541
|
struct NIO_Selector *selector = (struct NIO_Selector *)io->data;
|
@@ -546,7 +546,7 @@ static void NIO_Selector_wakeup_callback(ev_loop *ev_loop, struct ev_io *io, int
|
|
546
546
|
}
|
547
547
|
|
548
548
|
/* libev callback fired whenever a monitor gets an event */
|
549
|
-
void NIO_Selector_monitor_callback(ev_loop *ev_loop, struct ev_io *io, int revents)
|
549
|
+
void NIO_Selector_monitor_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents)
|
550
550
|
{
|
551
551
|
struct NIO_Monitor *monitor_data = (struct NIO_Monitor *)io->data;
|
552
552
|
struct NIO_Selector *selector = monitor_data->selector;
|
data/lib/nio.rb
CHANGED
@@ -18,7 +18,7 @@ if ENV["NIO4R_PURE"] == "true" || (Gem.win_platform? && !defined?(JRUBY_VERSION)
|
|
18
18
|
require "nio/monitor"
|
19
19
|
require "nio/selector"
|
20
20
|
require "nio/bytebuffer"
|
21
|
-
NIO::ENGINE = "ruby"
|
21
|
+
NIO::ENGINE = "ruby"
|
22
22
|
else
|
23
23
|
require "nio4r_ext"
|
24
24
|
|
@@ -26,8 +26,8 @@ else
|
|
26
26
|
require "java"
|
27
27
|
require "jruby"
|
28
28
|
org.nio4r.Nio4r.new.load(JRuby.runtime, false)
|
29
|
-
NIO::ENGINE = "java"
|
29
|
+
NIO::ENGINE = "java"
|
30
30
|
else
|
31
|
-
NIO::ENGINE = "libev"
|
31
|
+
NIO::ENGINE = "libev"
|
32
32
|
end
|
33
33
|
end
|
data/lib/nio/bytebuffer.rb
CHANGED
@@ -24,6 +24,7 @@ module NIO
|
|
24
24
|
# @return [NIO::ByteBuffer]
|
25
25
|
def initialize(capacity)
|
26
26
|
raise TypeError, "no implicit conversion of #{capacity.class} to Integer" unless capacity.is_a?(Integer)
|
27
|
+
|
27
28
|
@capacity = capacity
|
28
29
|
clear
|
29
30
|
end
|
@@ -119,9 +120,11 @@ module NIO
|
|
119
120
|
# @return [self]
|
120
121
|
def put(str)
|
121
122
|
raise TypeError, "expected String, got #{str.class}" unless str.respond_to?(:to_str)
|
123
|
+
|
122
124
|
str = str.to_str
|
123
125
|
|
124
126
|
raise OverflowError, "buffer is full" if str.length > @limit - @position
|
127
|
+
|
125
128
|
@buffer[@position...str.length] = str
|
126
129
|
@position += str.length
|
127
130
|
self
|
@@ -188,6 +191,7 @@ module NIO
|
|
188
191
|
# @raise [NIO::ByteBuffer::MarkUnsetError] mark has not been set (call `#mark` first)
|
189
192
|
def reset
|
190
193
|
raise MarkUnsetError, "mark has not been set" unless @mark
|
194
|
+
|
191
195
|
@position = @mark
|
192
196
|
self
|
193
197
|
end
|
data/lib/nio/monitor.rb
CHANGED
@@ -6,16 +6,18 @@ module NIO
|
|
6
6
|
attr_reader :io, :interests, :selector
|
7
7
|
attr_accessor :value, :readiness
|
8
8
|
|
9
|
-
# :nodoc
|
9
|
+
# :nodoc:
|
10
10
|
def initialize(io, interests, selector)
|
11
|
-
unless io.is_a?(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
unless defined?(::OpenSSL) && io.is_a?(::OpenSSL::SSL::SSLSocket)
|
12
|
+
unless io.is_a?(IO)
|
13
|
+
if IO.respond_to? :try_convert
|
14
|
+
io = IO.try_convert(io)
|
15
|
+
elsif io.respond_to? :to_io
|
16
|
+
io = io.to_io
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
+
raise TypeError, "can't convert #{io.class} into IO" unless io.is_a? IO
|
20
|
+
end
|
19
21
|
end
|
20
22
|
|
21
23
|
@io = io
|
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
|
-
|
47
|
+
unless defined?(::OpenSSL) && 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
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path("
|
3
|
+
require File.expand_path("lib/nio/version", __dir__)
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.authors = ["Tony Arcieri"]
|
@@ -20,7 +20,15 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
spec.version = NIO::VERSION
|
22
22
|
|
23
|
-
spec.
|
23
|
+
spec.metadata = {
|
24
|
+
"bug_tracker_uri" => "https://github.com/socketry/nio4r/issues",
|
25
|
+
"changelog_uri" => "https://github.com/socketry/nio4r/blob/master/CHANGES.md",
|
26
|
+
"documentation_uri" => "https://www.rubydoc.info/gems/nio4r/#{spec.version}",
|
27
|
+
"source_code_uri" => "https://github.com/socketry/nio4r/tree/v#{spec.version}",
|
28
|
+
"wiki_uri" => "https://github.com/socketry/nio4r/wiki"
|
29
|
+
}
|
30
|
+
|
31
|
+
spec.required_ruby_version = ">= 2.4"
|
24
32
|
|
25
33
|
if defined? JRUBY_VERSION
|
26
34
|
spec.files << "lib/nio4r_ext.jar"
|
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
|
|
@@ -353,4 +352,3 @@ RSpec.describe NIO::ByteBuffer do
|
|
353
352
|
end
|
354
353
|
end
|
355
354
|
end
|
356
|
-
# rubocop:enable Metrics/BlockLength
|
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 }
|
@@ -1,13 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
|
-
require "openssl"
|
5
4
|
|
6
5
|
RSpec.describe OpenSSL::SSL::SSLSocket do
|
6
|
+
|
7
|
+
require "openssl"
|
8
|
+
|
9
|
+
before(:all) do
|
10
|
+
@tls = []
|
11
|
+
end
|
12
|
+
|
7
13
|
let(:addr) { "127.0.0.1" }
|
8
|
-
let(:port) { next_available_tcp_port }
|
9
14
|
|
10
|
-
let(:ssl_key) { OpenSSL::PKey::RSA.new(
|
15
|
+
let(:ssl_key) { OpenSSL::PKey::RSA.new(2048) }
|
11
16
|
|
12
17
|
let(:ssl_cert) do
|
13
18
|
name = OpenSSL::X509::Name.new([%w[CN 127.0.0.1]])
|
@@ -28,12 +33,19 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
|
|
28
33
|
OpenSSL::SSL::SSLContext.new.tap do |ctx|
|
29
34
|
ctx.cert = ssl_cert
|
30
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
|
31
43
|
end
|
32
44
|
end
|
33
45
|
|
34
46
|
let :readable_subject do
|
35
|
-
server = TCPServer.new(addr,
|
36
|
-
client = TCPSocket.open(addr,
|
47
|
+
server = TCPServer.new(addr, 0)
|
48
|
+
client = TCPSocket.open(addr, server.local_address.ip_port)
|
37
49
|
peer = server.accept
|
38
50
|
|
39
51
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -47,16 +59,17 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
|
|
47
59
|
|
48
60
|
ssl_peer.accept
|
49
61
|
ssl_peer << "data"
|
62
|
+
ssl_peer.flush
|
50
63
|
|
51
64
|
thread.join
|
52
|
-
pending "Failed to produce a readable SSL socket" unless select([ssl_client], [], [], 0)
|
53
65
|
|
66
|
+
pending "Failed to produce a readable socket" unless select([ssl_client], [], [], 10)
|
54
67
|
ssl_client
|
55
68
|
end
|
56
69
|
|
57
70
|
let :unreadable_subject do
|
58
|
-
server = TCPServer.new(addr,
|
59
|
-
client = TCPSocket.new(addr,
|
71
|
+
server = TCPServer.new(addr, 0)
|
72
|
+
client = TCPSocket.new(addr, server.local_address.ip_port)
|
60
73
|
peer = server.accept
|
61
74
|
|
62
75
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -70,13 +83,17 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
|
|
70
83
|
ssl_peer.accept
|
71
84
|
thread.join
|
72
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
|
+
|
73
90
|
pending "Failed to produce an unreadable socket" if select([ssl_client], [], [], 0)
|
74
91
|
ssl_client
|
75
92
|
end
|
76
93
|
|
77
94
|
let :writable_subject do
|
78
|
-
server = TCPServer.new(addr,
|
79
|
-
client = TCPSocket.new(addr,
|
95
|
+
server = TCPServer.new(addr, 0)
|
96
|
+
client = TCPSocket.new(addr, server.local_address.ip_port)
|
80
97
|
peer = server.accept
|
81
98
|
|
82
99
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -95,8 +112,8 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
|
|
95
112
|
end
|
96
113
|
|
97
114
|
let :unwritable_subject do
|
98
|
-
server = TCPServer.new(addr,
|
99
|
-
client = TCPSocket.new(addr,
|
115
|
+
server = TCPServer.new(addr, 0)
|
116
|
+
client = TCPSocket.new(addr, server.local_address.ip_port)
|
100
117
|
peer = server.accept
|
101
118
|
|
102
119
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -128,22 +145,22 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
|
|
128
145
|
|
129
146
|
# Once more for good measure!
|
130
147
|
begin
|
131
|
-
#
|
148
|
+
# ssl_client.write_nonblock "X" * 1024
|
132
149
|
loop { ssl_client.write_nonblock "X" * 1024 }
|
133
150
|
rescue OpenSSL::SSL::SSLError
|
134
151
|
end
|
135
152
|
|
136
153
|
# Sanity check to make sure we actually produced an unwritable socket
|
137
|
-
|
138
|
-
|
139
|
-
|
154
|
+
if select([], [ssl_client], [], 0)
|
155
|
+
pending "Failed to produce an unwritable socket"
|
156
|
+
end
|
140
157
|
|
141
158
|
ssl_client
|
142
159
|
end
|
143
160
|
|
144
161
|
let :pair do
|
145
|
-
server = TCPServer.new(addr,
|
146
|
-
client = TCPSocket.new(addr,
|
162
|
+
server = TCPServer.new(addr, 0)
|
163
|
+
client = TCPSocket.new(addr, server.local_address.ip_port)
|
147
164
|
peer = server.accept
|
148
165
|
|
149
166
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -159,6 +176,19 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
|
|
159
176
|
[thread.value, ssl_peer]
|
160
177
|
end
|
161
178
|
|
162
|
-
|
163
|
-
|
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
|
164
194
|
end
|