async-io 1.17.2 → 1.18.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -8
- data/README.md +34 -0
- data/async-io.gemspec +3 -3
- data/lib/async/io/endpoint.rb +8 -0
- data/lib/async/io/generic.rb +18 -5
- data/lib/async/io/shared_endpoint.rb +3 -1
- data/lib/async/io/socket.rb +12 -11
- data/lib/async/io/ssl_socket.rb +6 -5
- data/lib/async/io/trap.rb +22 -2
- data/lib/async/io/version.rb +1 -1
- data/spec/async/io/c10k_spec.rb +2 -14
- data/spec/async/io/endpoint_spec.rb +28 -1
- data/spec/async/io/generic_spec.rb +53 -4
- data/spec/async/io/shared_endpoint/server_spec.rb +1 -1
- data/spec/async/io/shared_endpoint_spec.rb +19 -6
- data/spec/async/io/socket_spec.rb +18 -3
- data/spec/async/io/ssl_server_spec.rb +1 -1
- data/spec/async/io/ssl_socket_spec.rb +7 -4
- data/spec/async/io/stream_spec.rb +2 -2
- data/spec/async/io/trap_spec.rb +6 -0
- data/spec/async/io/wrap/http_rb_spec.rb +1 -1
- data/spec/async/io/wrap/tcp_spec.rb +2 -2
- metadata +9 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 123cd8404fb47fe4beed0cac26b5b1f6825eb34b6a7e9952ef571c6103f72fab
|
4
|
+
data.tar.gz: c2313c72cd403abddfc5a7235f82c7983639501db2ce664792e46d09cfca7ebe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7fa15dba98d1c12d00ddfdfd2410cefa1767ef946d58751c2a1498b4226ee74a10255f38749c618275116f2b49f081a9d07518cfe4eb1f82416618db27d04426
|
7
|
+
data.tar.gz: df87e800a13784df609488498dba29c72220aac213745e470628f650d0f031dc321b2463e079502a553e5a32aea7d95b6c690d229aa9d5f6201c3e8e45063575
|
data/.travis.yml
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
language: ruby
|
2
|
-
|
3
|
-
dist: trusty
|
2
|
+
dist: xenial
|
4
3
|
cache: bundler
|
5
4
|
|
6
|
-
before_script:
|
7
|
-
- gem update --system
|
8
|
-
- gem install bundler
|
9
|
-
|
10
5
|
matrix:
|
11
6
|
include:
|
12
7
|
- rvm: 2.3
|
@@ -17,9 +12,7 @@ matrix:
|
|
17
12
|
- rvm: jruby-head
|
18
13
|
env: JRUBY_OPTS="--debug -X+O"
|
19
14
|
- rvm: ruby-head
|
20
|
-
- rvm: rbx-3
|
21
15
|
allow_failures:
|
22
16
|
- rvm: ruby-head
|
23
17
|
- rvm: truffleruby
|
24
18
|
- rvm: jruby-head
|
25
|
-
- rvm: rbx-3
|
data/README.md
CHANGED
@@ -87,6 +87,40 @@ end
|
|
87
87
|
|
88
88
|
Any `yield` operation can cause a timeout to trigger. Non-`async` functions might not timeout because they are outside the scope of `async`.
|
89
89
|
|
90
|
+
#### Wrapper Timeouts
|
91
|
+
|
92
|
+
Asynchronous operations may block forever. You can assign a per-wrapper operation timeout duration. All asynchronous operations will be bounded by this timeout.
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
peer.timeout_duration = 1
|
96
|
+
peer.read(512) # If this takes more than 1 second, Async::TimeoutError will be raised.
|
97
|
+
```
|
98
|
+
|
99
|
+
The benefit of this approach is that it applies to all operations. Typically, this would be configured by the user, and set to something pretty high, e.g. 120 seconds.
|
100
|
+
|
101
|
+
### Reading Characters
|
102
|
+
|
103
|
+
This example shows how to read one character at a time as the user presses it on the keyboard, and echos it back out as uppercase:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
require 'async'
|
107
|
+
require 'async/io/stream'
|
108
|
+
require 'io/console'
|
109
|
+
|
110
|
+
$stdin.raw!
|
111
|
+
$stdin.echo = false
|
112
|
+
|
113
|
+
Async do |task|
|
114
|
+
stdin = Async::IO::Stream.new(
|
115
|
+
Async::IO::Generic.new($stdin)
|
116
|
+
)
|
117
|
+
|
118
|
+
while character = stdin.read(1)
|
119
|
+
$stdout.write character.upcase
|
120
|
+
end
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
90
124
|
## Contributing
|
91
125
|
|
92
126
|
1. Fork it
|
data/async-io.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
require_relative 'lib/async/io/version'
|
3
3
|
|
4
4
|
Gem::Specification.new do |spec|
|
@@ -15,12 +15,12 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
16
|
spec.require_paths = ["lib"]
|
17
17
|
|
18
|
-
spec.add_dependency "async", "~> 1.
|
18
|
+
spec.add_dependency "async", "~> 1.14"
|
19
19
|
spec.add_development_dependency "async-rspec", "~> 1.10"
|
20
20
|
|
21
21
|
spec.required_ruby_version = '~> 2.3'
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler"
|
23
|
+
spec.add_development_dependency "bundler"
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.0"
|
26
26
|
end
|
data/lib/async/io/endpoint.rb
CHANGED
data/lib/async/io/generic.rb
CHANGED
@@ -97,7 +97,13 @@ module Async
|
|
97
97
|
alias syswrite write
|
98
98
|
alias << write
|
99
99
|
|
100
|
-
def
|
100
|
+
def dup
|
101
|
+
super.tap do |copy|
|
102
|
+
copy.timeout = self.timeout
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def wait(timeout = self.timeout, mode = :read)
|
101
107
|
case mode
|
102
108
|
when :read
|
103
109
|
wait_readable(timeout)
|
@@ -106,12 +112,17 @@ module Async
|
|
106
112
|
else
|
107
113
|
wait_any(:rw, timeout)
|
108
114
|
end
|
115
|
+
rescue TimeoutError
|
116
|
+
return nil
|
109
117
|
end
|
110
118
|
|
111
119
|
def nonblock
|
112
120
|
true
|
113
121
|
end
|
114
|
-
|
122
|
+
|
123
|
+
def nonblock= value
|
124
|
+
true
|
125
|
+
end
|
115
126
|
|
116
127
|
def nonblock?
|
117
128
|
true
|
@@ -121,17 +132,19 @@ module Async
|
|
121
132
|
!@io.closed?
|
122
133
|
end
|
123
134
|
|
135
|
+
attr_accessor :timeout
|
136
|
+
|
124
137
|
protected
|
125
138
|
|
126
|
-
def async_send(*args)
|
139
|
+
def async_send(*args, timeout: self.timeout)
|
127
140
|
while true
|
128
141
|
result = @io.__send__(*args, exception: false)
|
129
142
|
|
130
143
|
case result
|
131
144
|
when :wait_readable
|
132
|
-
wait_readable
|
145
|
+
wait_readable(timeout)
|
133
146
|
when :wait_writable
|
134
|
-
wait_writable
|
147
|
+
wait_writable(timeout)
|
135
148
|
else
|
136
149
|
return result
|
137
150
|
end
|
data/lib/async/io/socket.rb
CHANGED
@@ -87,11 +87,11 @@ module Async
|
|
87
87
|
end
|
88
88
|
|
89
89
|
module Server
|
90
|
-
def accept_each(task: Task.current)
|
90
|
+
def accept_each(timeout: nil, task: Task.current)
|
91
91
|
task.annotate "accepting connections #{self.local_address.inspect}"
|
92
92
|
|
93
93
|
while true
|
94
|
-
self.accept(task: task) do |io, address|
|
94
|
+
self.accept(timeout: timeout, task: task) do |io, address|
|
95
95
|
yield io, address, task: task
|
96
96
|
end
|
97
97
|
end
|
@@ -115,10 +115,13 @@ module Async
|
|
115
115
|
|
116
116
|
alias connect_nonblock connect
|
117
117
|
|
118
|
-
|
119
|
-
|
118
|
+
# @param duration [Numeric] the maximum time to wait for accepting a connection, if specified.
|
119
|
+
def accept(timeout: nil, task: Task.current)
|
120
|
+
peer, address = async_send(:accept_nonblock, timeout: timeout)
|
120
121
|
wrapper = Socket.new(peer, task.reactor)
|
121
122
|
|
123
|
+
wrapper.timeout = self.timeout
|
124
|
+
|
122
125
|
return wrapper, address unless block_given?
|
123
126
|
|
124
127
|
task.async do |task|
|
@@ -126,8 +129,6 @@ module Async
|
|
126
129
|
|
127
130
|
begin
|
128
131
|
yield wrapper, address
|
129
|
-
rescue
|
130
|
-
Async.logger.error(self) {$!}
|
131
132
|
ensure
|
132
133
|
wrapper.close
|
133
134
|
end
|
@@ -137,12 +138,15 @@ module Async
|
|
137
138
|
alias accept_nonblock accept
|
138
139
|
alias sysaccept accept
|
139
140
|
|
140
|
-
def self.build(*args, task: Task.current)
|
141
|
+
def self.build(*args, timeout: nil, task: Task.current)
|
141
142
|
socket = wrapped_klass.new(*args)
|
142
143
|
|
143
144
|
yield socket
|
144
145
|
|
145
|
-
|
146
|
+
wrapper = self.new(socket, task.reactor)
|
147
|
+
wrapper.timeout = timeout
|
148
|
+
|
149
|
+
return wrapper
|
146
150
|
rescue Exception
|
147
151
|
socket.close if socket
|
148
152
|
|
@@ -168,8 +172,6 @@ module Async
|
|
168
172
|
if local_address
|
169
173
|
socket.bind(local_address.to_sockaddr)
|
170
174
|
end
|
171
|
-
|
172
|
-
self.new(socket, task.reactor)
|
173
175
|
end
|
174
176
|
|
175
177
|
begin
|
@@ -199,7 +201,6 @@ module Async
|
|
199
201
|
Async.logger.debug(self) {"Binding to #{local_address.inspect}"}
|
200
202
|
|
201
203
|
wrapper = build(local_address.afamily, local_address.socktype, protocol, **options) do |socket|
|
202
|
-
|
203
204
|
if reuse_address
|
204
205
|
socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
|
205
206
|
end
|
data/lib/async/io/ssl_socket.rb
CHANGED
@@ -84,6 +84,9 @@ module Async
|
|
84
84
|
# This ensures that when the internal IO is closed, it also closes the internal socket:
|
85
85
|
io.sync_close = true
|
86
86
|
|
87
|
+
# Copy the timeout:
|
88
|
+
@timeout = socket.timeout
|
89
|
+
|
87
90
|
super(io, socket.reactor)
|
88
91
|
end
|
89
92
|
end
|
@@ -102,7 +105,7 @@ module Async
|
|
102
105
|
self.class.new(@server.dup, @context)
|
103
106
|
end
|
104
107
|
|
105
|
-
def_delegators :@server, :local_address, :setsockopt, :getsockopt, :close, :close_on_exec=, :reactor=
|
108
|
+
def_delegators :@server, :local_address, :setsockopt, :getsockopt, :close, :close_on_exec=, :reactor=, :timeout, :timeout=
|
106
109
|
|
107
110
|
attr :server
|
108
111
|
attr :context
|
@@ -111,8 +114,8 @@ module Async
|
|
111
114
|
@server.listen(*args)
|
112
115
|
end
|
113
116
|
|
114
|
-
def accept(task: Task.current)
|
115
|
-
peer, address = @server.accept
|
117
|
+
def accept(task: Task.current, **options)
|
118
|
+
peer, address = @server.accept(**options)
|
116
119
|
|
117
120
|
wrapper = SSLSocket.new(peer, @context)
|
118
121
|
|
@@ -126,8 +129,6 @@ module Async
|
|
126
129
|
wrapper.accept
|
127
130
|
|
128
131
|
yield wrapper, address
|
129
|
-
rescue
|
130
|
-
Async.logger.error(self) {$!}
|
131
132
|
ensure
|
132
133
|
wrapper.close
|
133
134
|
end
|
data/lib/async/io/trap.rb
CHANGED
@@ -20,6 +20,8 @@
|
|
20
20
|
|
21
21
|
require_relative 'notification'
|
22
22
|
|
23
|
+
require 'thread'
|
24
|
+
|
23
25
|
module Async
|
24
26
|
module IO
|
25
27
|
# A cross-reactor/process notification pipe.
|
@@ -27,12 +29,30 @@ module Async
|
|
27
29
|
def initialize(name)
|
28
30
|
@name = name
|
29
31
|
@notifications = []
|
32
|
+
|
33
|
+
@installed = false
|
34
|
+
@mutex = Mutex.new
|
35
|
+
end
|
36
|
+
|
37
|
+
# Ignore the trap within the current process. Can be invoked before forking and/or invoking `install!` to assert default behaviour.
|
38
|
+
def ignore!
|
39
|
+
Signal.trap(@name, "IGNORE")
|
30
40
|
end
|
31
41
|
|
42
|
+
# Install the trap into the current process. Thread safe.
|
43
|
+
# @return [Boolean] whether the trap was installed or not. If the trap was already installed, returns nil.
|
32
44
|
def install!
|
33
|
-
|
45
|
+
return if @installed
|
46
|
+
|
47
|
+
@mutex.synchronize do
|
48
|
+
return if @installed
|
49
|
+
|
50
|
+
Signal.trap(@name, &self.method(:trigger))
|
51
|
+
|
52
|
+
@installed = true
|
53
|
+
end
|
34
54
|
|
35
|
-
return
|
55
|
+
return true
|
36
56
|
end
|
37
57
|
|
38
58
|
# Block the calling task until the signal occurs.
|
data/lib/async/io/version.rb
CHANGED
data/spec/async/io/c10k_spec.rb
CHANGED
@@ -27,7 +27,7 @@ RSpec.describe "echo client/server" do
|
|
27
27
|
# sudo sysctl -w net.inet.ip.portrange.hifirst=10000
|
28
28
|
# Probably due to the use of select.
|
29
29
|
|
30
|
-
let(:repeats) {RUBY_PLATFORM =~ /darwin/ ?
|
30
|
+
let(:repeats) {RUBY_PLATFORM =~ /darwin/ ? 200 : 10000}
|
31
31
|
let(:server_address) {Async::IO::Address.tcp('0.0.0.0', 10102)}
|
32
32
|
|
33
33
|
def echo_server(server_address)
|
@@ -99,19 +99,7 @@ RSpec.describe "echo client/server" do
|
|
99
99
|
example.run
|
100
100
|
end
|
101
101
|
|
102
|
-
example.reporter.message "Handled #{repeats} connections in #{duration}: #{repeats/duration}req/s"
|
103
|
-
end
|
104
|
-
|
105
|
-
around(:each) do |example|
|
106
|
-
previous_level = Async.logger.level
|
107
|
-
# Supress logging:
|
108
|
-
Async.logger.level = Logger::WARN
|
109
|
-
|
110
|
-
begin
|
111
|
-
example.run
|
112
|
-
ensure
|
113
|
-
Async.logger.level = previous_level
|
114
|
-
end
|
102
|
+
example.reporter.message "Handled #{repeats} connections in #{duration.round(2)}s: #{(repeats/duration).round(2)}req/s"
|
115
103
|
end
|
116
104
|
|
117
105
|
it "should send/receive 10,000 messages" do
|
@@ -28,15 +28,23 @@ RSpec.describe Async::IO::Endpoint do
|
|
28
28
|
it "should have hostname" do
|
29
29
|
expect(subject.hostname).to be == "lolcathost"
|
30
30
|
end
|
31
|
+
|
32
|
+
it "shouldn't have a timeout duration" do
|
33
|
+
expect(subject.timeout).to be_nil
|
34
|
+
end
|
31
35
|
end
|
32
36
|
|
33
|
-
describe Async::IO::Endpoint.tcp('0.0.0.0', 5234, reuse_port: true) do
|
37
|
+
describe Async::IO::Endpoint.tcp('0.0.0.0', 5234, reuse_port: true, timeout: 10) do
|
34
38
|
it "should be a tcp binding" do
|
35
39
|
subject.bind do |server|
|
36
40
|
expect(server.local_address.socktype).to be == ::Socket::SOCK_STREAM
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
44
|
+
it "should have a timeout duration" do
|
45
|
+
expect(subject.timeout).to be 10
|
46
|
+
end
|
47
|
+
|
40
48
|
it "should print nicely" do
|
41
49
|
expect(subject.to_s).to include('0.0.0.0', '5234')
|
42
50
|
end
|
@@ -48,6 +56,25 @@ RSpec.describe Async::IO::Endpoint do
|
|
48
56
|
it "has hostname" do
|
49
57
|
expect(subject.hostname).to be == '0.0.0.0'
|
50
58
|
end
|
59
|
+
|
60
|
+
let(:message) {"Hello World!"}
|
61
|
+
|
62
|
+
it "can connect to bound server" do
|
63
|
+
server_task = reactor.async do
|
64
|
+
subject.accept do |io|
|
65
|
+
expect(io.timeout).to be == 10
|
66
|
+
io.write message
|
67
|
+
io.close
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
io = subject.connect
|
72
|
+
expect(io.timeout).to be == 10
|
73
|
+
expect(io.read(message.bytesize)).to be == message
|
74
|
+
io.close
|
75
|
+
|
76
|
+
server_task.stop
|
77
|
+
end
|
51
78
|
end
|
52
79
|
|
53
80
|
describe Async::IO::Endpoint.tcp('0.0.0.0', 0) do
|
@@ -19,6 +19,7 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
require 'async/io'
|
22
|
+
require 'async/clock'
|
22
23
|
|
23
24
|
require_relative 'generic_examples'
|
24
25
|
|
@@ -31,25 +32,73 @@ RSpec.describe Async::IO::Generic do
|
|
31
32
|
:bytes, :chars, :codepoints, :each, :each_byte, :each_char, :each_codepoint, :each_line, :getbyte, :getc, :gets, :lineno, :lineno=, :lines, :print, :printf, :putc, :puts, :readbyte, :readchar, :readline, :readlines, :ungetbyte, :ungetc
|
32
33
|
] + CONSOLE_METHODS
|
33
34
|
|
35
|
+
let(:message) {"Hello World!"}
|
36
|
+
|
34
37
|
let(:pipe) {IO.pipe}
|
35
38
|
let(:input) {Async::IO::Generic.new(pipe.first)}
|
36
39
|
let(:output) {Async::IO::Generic.new(pipe.last)}
|
37
40
|
|
38
41
|
it "should send and receive data within the same reactor" do
|
39
|
-
|
42
|
+
received = nil
|
40
43
|
|
41
44
|
output_task = reactor.async do
|
42
|
-
|
45
|
+
received = input.read(1024)
|
43
46
|
end
|
44
47
|
|
45
48
|
reactor.async do
|
46
|
-
output.write(
|
49
|
+
output.write(message)
|
47
50
|
end
|
48
51
|
|
49
52
|
output_task.wait
|
50
|
-
expect(
|
53
|
+
expect(received).to be == message
|
51
54
|
|
52
55
|
input.close
|
53
56
|
output.close
|
54
57
|
end
|
58
|
+
|
59
|
+
describe '#wait' do
|
60
|
+
let(:wait_duration) {0.1}
|
61
|
+
|
62
|
+
it "can wait for :read and :write" do
|
63
|
+
reader = reactor.async do |task|
|
64
|
+
duration = Async::Clock.measure do
|
65
|
+
input.wait(1, :read)
|
66
|
+
end
|
67
|
+
|
68
|
+
expect(duration).to be_within(10).percent_of(wait_duration)
|
69
|
+
expect(input.read(1024)).to be == message
|
70
|
+
|
71
|
+
input.close
|
72
|
+
end
|
73
|
+
|
74
|
+
writer = reactor.async do |task|
|
75
|
+
duration = Async::Clock.measure do
|
76
|
+
output.wait(1, :write)
|
77
|
+
end
|
78
|
+
|
79
|
+
task.sleep(wait_duration)
|
80
|
+
|
81
|
+
output.write(message)
|
82
|
+
output.close
|
83
|
+
end
|
84
|
+
|
85
|
+
[reader, writer].each(&:wait)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "can return nil when timeout is exceeded" do
|
89
|
+
reader = reactor.async do |task|
|
90
|
+
duration = Async::Clock.measure do
|
91
|
+
expect(input.wait(wait_duration, :read)).to be_nil
|
92
|
+
end
|
93
|
+
|
94
|
+
expect(duration).to be_within(10).percent_of(wait_duration)
|
95
|
+
|
96
|
+
input.close
|
97
|
+
end
|
98
|
+
|
99
|
+
[reader].each(&:wait)
|
100
|
+
|
101
|
+
output.close
|
102
|
+
end
|
103
|
+
end
|
55
104
|
end
|
@@ -38,7 +38,7 @@ RSpec.shared_examples_for Async::IO::SharedEndpoint do |container_class|
|
|
38
38
|
let!(:bound_endpoint) do
|
39
39
|
Async::Reactor.run do
|
40
40
|
Async::IO::SharedEndpoint.bound(server_endpoint)
|
41
|
-
end.
|
41
|
+
end.wait
|
42
42
|
end
|
43
43
|
|
44
44
|
it "can use bound endpoint in container" do
|
@@ -21,32 +21,45 @@
|
|
21
21
|
require 'async/io/host_endpoint'
|
22
22
|
require 'async/io/shared_endpoint'
|
23
23
|
|
24
|
+
require 'pry'
|
25
|
+
|
24
26
|
RSpec.describe Async::IO::SharedEndpoint do
|
25
27
|
include_context Async::RSpec::Reactor
|
26
28
|
|
27
29
|
describe '#bound' do
|
28
|
-
let(:endpoint) {Async::IO::Endpoint.tcp("localhost", 5123)}
|
30
|
+
let(:endpoint) {Async::IO::Endpoint.tcp("localhost", 5123, timeout: 10)}
|
29
31
|
|
30
32
|
it "can bind to shared endpoint" do
|
31
33
|
bound_endpoint = described_class.bound(endpoint)
|
32
|
-
|
33
34
|
expect(bound_endpoint.wrappers).to_not be_empty
|
34
|
-
|
35
|
+
|
36
|
+
wrapper = bound_endpoint.wrappers.first
|
37
|
+
expect(wrapper).to be_a Async::IO::Socket
|
38
|
+
expect(wrapper.timeout).to be == endpoint.timeout
|
35
39
|
|
36
40
|
bound_endpoint.close
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
44
|
describe '#connected' do
|
41
|
-
let(:endpoint) {Async::IO::Endpoint.tcp("
|
45
|
+
let(:endpoint) {Async::IO::Endpoint.tcp("localhost", 5124, timeout: 10)}
|
42
46
|
|
43
47
|
it "can connect to shared endpoint" do
|
44
|
-
|
48
|
+
server_task = reactor.async do
|
49
|
+
endpoint.accept do |io|
|
50
|
+
io.close
|
51
|
+
end
|
52
|
+
end
|
45
53
|
|
54
|
+
connected_endpoint = described_class.connected(endpoint)
|
46
55
|
expect(connected_endpoint.wrappers).to_not be_empty
|
47
|
-
|
56
|
+
|
57
|
+
wrapper = connected_endpoint.wrappers.first
|
58
|
+
expect(wrapper).to be_a Async::IO::Socket
|
59
|
+
expect(wrapper.timeout).to be == endpoint.timeout
|
48
60
|
|
49
61
|
connected_endpoint.close
|
62
|
+
server_task.stop
|
50
63
|
end
|
51
64
|
end
|
52
65
|
end
|
@@ -37,7 +37,7 @@ RSpec.describe Async::IO::Socket do
|
|
37
37
|
it "should fail to connect if no listening server" do
|
38
38
|
expect do
|
39
39
|
Async::IO::Socket.connect(address)
|
40
|
-
end.to
|
40
|
+
end.to raise_exception(Errno::ECONNREFUSED)
|
41
41
|
end
|
42
42
|
|
43
43
|
it "should close the socket when interrupted by a timeout" do
|
@@ -47,7 +47,7 @@ RSpec.describe Async::IO::Socket do
|
|
47
47
|
expect(wrapper).to receive(:close)
|
48
48
|
expect do
|
49
49
|
Async::IO::Socket.connect(address)
|
50
|
-
end.to
|
50
|
+
end.to raise_exception(Async::TimeoutError)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -57,7 +57,7 @@ RSpec.describe Async::IO::Socket do
|
|
57
57
|
|
58
58
|
expect do
|
59
59
|
Async::IO::Socket.bind(address)
|
60
|
-
end.to
|
60
|
+
end.to raise_exception(Errno::EACCES)
|
61
61
|
end
|
62
62
|
|
63
63
|
it "can bind to port 0" do
|
@@ -84,6 +84,21 @@ RSpec.describe Async::IO::Socket do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
+
describe '#timeout' do
|
88
|
+
subject{described_class.pair(:UNIX, :STREAM, 0)}
|
89
|
+
|
90
|
+
it "should timeout while waiting to receive data" do
|
91
|
+
s1, s2 = *subject
|
92
|
+
|
93
|
+
s2.timeout = 1
|
94
|
+
|
95
|
+
expect{s2.recv(32)}.to raise_exception(Async::TimeoutError, "execution expired")
|
96
|
+
|
97
|
+
s1.close
|
98
|
+
s2.close
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
87
102
|
describe '.pair' do
|
88
103
|
subject{described_class.pair(:UNIX, :STREAM, 0)}
|
89
104
|
|
@@ -115,7 +115,7 @@ RSpec.describe Async::IO::SSLServer do
|
|
115
115
|
expect do
|
116
116
|
invalid_client_endpoint.connect do |client|
|
117
117
|
end
|
118
|
-
end.to
|
118
|
+
end.to raise_exception(OpenSSL::SSL::SSLError, /handshake failure/)
|
119
119
|
|
120
120
|
server_task.stop
|
121
121
|
end
|
@@ -30,9 +30,9 @@ RSpec.describe Async::IO::SSLSocket do
|
|
30
30
|
it_should_behave_like Async::IO::Generic
|
31
31
|
|
32
32
|
# Shared port for localhost network tests.
|
33
|
-
let(:endpoint) {Async::IO::Endpoint.tcp("127.0.0.1", 6779, reuse_port: true)}
|
34
|
-
let(:server_endpoint) {Async::IO::SSLEndpoint.new(endpoint, ssl_context: server_context)}
|
35
|
-
let(:client_endpoint) {Async::IO::SSLEndpoint.new(endpoint, ssl_context: client_context)}
|
33
|
+
let(:endpoint) {Async::IO::Endpoint.tcp("127.0.0.1", 6779, reuse_port: true, timeout: 10)}
|
34
|
+
let(:server_endpoint) {Async::IO::SSLEndpoint.new(endpoint, ssl_context: server_context, timeout: 20)}
|
35
|
+
let(:client_endpoint) {Async::IO::SSLEndpoint.new(endpoint, ssl_context: client_context, timeout: 20)}
|
36
36
|
|
37
37
|
let(:data) {"The quick brown fox jumped over the lazy dog."}
|
38
38
|
|
@@ -44,6 +44,8 @@ RSpec.describe Async::IO::SSLSocket do
|
|
44
44
|
|
45
45
|
begin
|
46
46
|
server.accept do |peer, address|
|
47
|
+
expect(peer.timeout).to be == 10
|
48
|
+
|
47
49
|
data = peer.read(512)
|
48
50
|
peer.write(data)
|
49
51
|
end
|
@@ -64,6 +66,7 @@ RSpec.describe Async::IO::SSLSocket do
|
|
64
66
|
reactor.async do
|
65
67
|
client_endpoint.connect do |client|
|
66
68
|
expect(client).to be_connected
|
69
|
+
expect(client.timeout).to be == 10
|
67
70
|
|
68
71
|
client.write(data)
|
69
72
|
|
@@ -82,7 +85,7 @@ RSpec.describe Async::IO::SSLSocket do
|
|
82
85
|
reactor.async do
|
83
86
|
expect do
|
84
87
|
client_endpoint.connect
|
85
|
-
end.to
|
88
|
+
end.to raise_exception(OpenSSL::SSL::SSLError)
|
86
89
|
end
|
87
90
|
end
|
88
91
|
end
|
@@ -161,7 +161,7 @@ RSpec.describe Async::IO::Stream do
|
|
161
161
|
it "should terminate stream" do
|
162
162
|
expect do
|
163
163
|
stream.eof!
|
164
|
-
end.to
|
164
|
+
end.to raise_exception(EOFError)
|
165
165
|
|
166
166
|
expect(stream).to be_eof
|
167
167
|
end
|
@@ -178,7 +178,7 @@ RSpec.describe Async::IO::Stream do
|
|
178
178
|
|
179
179
|
expect do
|
180
180
|
stream.close
|
181
|
-
end.to_not
|
181
|
+
end.to_not raise_exception
|
182
182
|
end
|
183
183
|
end
|
184
184
|
end
|
data/spec/async/io/trap_spec.rb
CHANGED
@@ -62,7 +62,7 @@ RSpec.describe Async::IO::TCPSocket do
|
|
62
62
|
|
63
63
|
expect do
|
64
64
|
Net::HTTP.get_response('www.google.com', '/')
|
65
|
-
end.to_not
|
65
|
+
end.to_not raise_exception
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
@@ -70,7 +70,7 @@ RSpec.describe Async::IO::TCPSocket do
|
|
70
70
|
it "should fetch page" do
|
71
71
|
expect do
|
72
72
|
Net::HTTP.get_response('www.google.com', '/')
|
73
|
-
end.to_not
|
73
|
+
end.to_not raise_exception
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.18.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.14'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.14'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: async-rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,8 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
171
|
- !ruby/object:Gem::Version
|
172
172
|
version: '0'
|
173
173
|
requirements: []
|
174
|
-
|
175
|
-
rubygems_version: 2.7.8
|
174
|
+
rubygems_version: 3.0.1
|
176
175
|
signing_key:
|
177
176
|
specification_version: 4
|
178
177
|
summary: Provides support for asynchonous TCP, UDP, UNIX and SSL sockets.
|