async-io 1.17.2 → 1.18.1
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/.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.
|