async-io 1.27.1 → 1.27.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7128e6848ad5bdee711dc7a8d1b2b13be4ced2b32cc18c3f067375a968ce59a
4
- data.tar.gz: 3b2537d7467f536c65de6740042e59ae257dac49ebc07136167e239a47c2c356
3
+ metadata.gz: 484fda3385353976eaa6b5b52e3379264cb5a532369f9f036871c1525d6fad0d
4
+ data.tar.gz: 73a793c925d309ffdae29a364c656128e57192895fcad6d3f2c4673ffa7127ea
5
5
  SHA512:
6
- metadata.gz: 80a3e758f2288cf0b01d9daad23914d2bf4b72a933d06483759bfdaf9fb38add65539dc4b4854a0bc0e1237baf72ab716f46c5e3401153e8bd6367f8c93b6379
7
- data.tar.gz: 66221768e8c2b71f448868d769bdffe2faea1af137ebe7cbe1e223763bb92424633f02f6ada580eb0b6a4f3f5f777202b3342d0dc7509e585c41a3fcf7154e99
6
+ metadata.gz: 86979dff7abe788e7aa8f867bbeab23a1dab2afab8bf5bed71700be231b9d91799187cc7758d58f0127efacdc39727d125ff49c83e92f42e1c97284ddad8a86b
7
+ data.tar.gz: 85c753ce6ffbc31dfdde378794c6969a80539c5c64a60ff3d3345620b0d27c793c8f2b44ef6fab9f084ad2c0d617da673e300264a2ab3cf530c9be7f1de0abcd
@@ -8,6 +8,7 @@ matrix:
8
8
  - rvm: 2.4
9
9
  - rvm: 2.5
10
10
  - rvm: 2.6
11
+ - rvm: 2.7
11
12
  - rvm: 2.6
12
13
  env: COVERAGE=PartialSummary,Coveralls
13
14
  - rvm: truffleruby
data/README.md CHANGED
@@ -4,7 +4,7 @@ Async::IO provides builds on [async] and provides asynchronous wrappers for `IO`
4
4
 
5
5
  [async]: https://github.com/socketry/async
6
6
 
7
- [![Build Status](https://secure.travis-ci.org/socketry/async-io.svg)](http://travis-ci.org/socketry/async-io)
7
+ [![Build Status](https://travis-ci.com/socketry/async-io.svg?branch=master)](https://travis-ci.com/socketry/async-io)
8
8
  [![Code Climate](https://codeclimate.com/github/socketry/async-io.svg)](https://codeclimate.com/github/socketry/async-io)
9
9
  [![Coverage Status](https://coveralls.io/repos/socketry/async-io/badge.svg)](https://coveralls.io/r/socketry/async-io)
10
10
 
@@ -20,10 +20,10 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.required_ruby_version = '~> 2.3'
22
22
 
23
- spec.add_development_dependency "async-container", "~> 0.10.0"
23
+ spec.add_development_dependency "async-container", "~> 0.15"
24
24
 
25
- spec.add_development_dependency "bundler"
26
25
  spec.add_development_dependency "covered"
26
+ spec.add_development_dependency "bundler"
27
27
  spec.add_development_dependency "rake", "~> 10.0"
28
28
  spec.add_development_dependency "rspec", "~> 3.0"
29
29
  end
@@ -0,0 +1,79 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'socket'
22
+
23
+ module Async
24
+ module IO
25
+ module Peer
26
+ include ::Socket::Constants
27
+
28
+ # Is it likely that the socket is still connected?
29
+ # May return false positive, but won't return false negative.
30
+ def connected?
31
+ return false if @io.closed?
32
+
33
+ # If we can wait for the socket to become readable, we know that the socket may still be open.
34
+ result = to_io.recv_nonblock(1, MSG_PEEK, exception: false)
35
+
36
+ # Either there was some data available, or we can wait to see if there is data avaialble.
37
+ return !result.empty? || result == :wait_readable
38
+
39
+ rescue Errno::ECONNRESET
40
+ # This might be thrown by recv_nonblock.
41
+ return false
42
+ end
43
+
44
+ # Best effort to set *_NODELAY if it makes sense. Swallows errors where possible.
45
+ def sync=(value)
46
+ super
47
+
48
+ case self.protocol
49
+ when 0, IPPROTO_TCP
50
+ self.setsockopt(IPPROTO_TCP, TCP_NODELAY, value ? 1 : 0)
51
+ else
52
+ Async.logger.warn(self) {"Unsure how to sync=#{value} for #{self.protocol}!"}
53
+ end
54
+ rescue Errno::EINVAL
55
+ # On Darwin, sometimes occurs when the connection is not yet fully formed. Empirically, TCP_NODELAY is enabled despite this result.
56
+ rescue Errno::EOPNOTSUPP
57
+ # Some platforms may simply not support the operation.
58
+ # Async.logger.warn(self) {"Unable to set sync=#{value}!"}
59
+ end
60
+
61
+ def sync
62
+ case self.protocol
63
+ when IPPROTO_TCP
64
+ self.getsockopt(IPPROTO_TCP, TCP_NODELAY).bool
65
+ else
66
+ true
67
+ end && super
68
+ end
69
+
70
+ def type
71
+ self.local_address.socktype
72
+ end
73
+
74
+ def protocol
75
+ self.local_address.protocol
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,39 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'async/task'
22
+
23
+ module Async
24
+ module IO
25
+ module Server
26
+ def accept_each(timeout: nil, task: Task.current)
27
+ task.annotate "accepting connections #{self.local_address.inspect} [fd=#{self.fileno}]"
28
+
29
+ callback = lambda do |io, address|
30
+ yield io, address, task: task
31
+ end
32
+
33
+ while true
34
+ self.accept(timeout: timeout, task: task, &callback)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -21,64 +21,12 @@
21
21
  require 'socket'
22
22
  require 'async/task'
23
23
 
24
+ require_relative 'peer'
25
+ require_relative 'server'
24
26
  require_relative 'generic'
25
27
 
26
28
  module Async
27
29
  module IO
28
- module Peer
29
- include ::Socket::Constants
30
-
31
- # Is it likely that the socket is still connected?
32
- # May return false positive, but won't return false negative.
33
- def connected?
34
- return false if @io.closed?
35
-
36
- # If we can wait for the socket to become readable, we know that the socket may still be open.
37
- result = to_io.recv_nonblock(1, MSG_PEEK, exception: false)
38
-
39
- # Either there was some data available, or we can wait to see if there is data avaialble.
40
- return !result.empty? || result == :wait_readable
41
-
42
- rescue Errno::ECONNRESET
43
- # This might be thrown by recv_nonblock.
44
- return false
45
- end
46
-
47
- # Best effort to set *_NODELAY if it makes sense. Swallows errors where possible.
48
- def sync=(value)
49
- super
50
-
51
- case self.protocol
52
- when 0, IPPROTO_TCP
53
- self.setsockopt(IPPROTO_TCP, TCP_NODELAY, value ? 1 : 0)
54
- else
55
- Async.logger.warn(self) {"Unsure how to sync=#{value} for #{self.protocol}!"}
56
- end
57
- rescue Errno::EINVAL
58
- # On Darwin, sometimes occurs when the connection is not yet fully formed. Empirically, TCP_NODELAY is enabled despite this result.
59
- rescue Errno::EOPNOTSUPP
60
- # Some platforms may simply not support the operation.
61
- # Async.logger.warn(self) {"Unable to set sync=#{value}!"}
62
- end
63
-
64
- def sync
65
- case self.protocol
66
- when IPPROTO_TCP
67
- self.getsockopt(IPPROTO_TCP, TCP_NODELAY).bool
68
- else
69
- true
70
- end && super
71
- end
72
-
73
- def type
74
- self.local_address.socktype
75
- end
76
-
77
- def protocol
78
- self.local_address.protocol
79
- end
80
- end
81
-
82
30
  class BasicSocket < Generic
83
31
  wraps ::BasicSocket, :setsockopt, :connect_address, :close_read, :close_write, :local_address, :remote_address, :do_not_reverse_lookup, :do_not_reverse_lookup=, :shutdown, :getsockopt, :getsockname, :getpeername, :getpeereid
84
32
 
@@ -91,20 +39,6 @@ module Async
91
39
  include Peer
92
40
  end
93
41
 
94
- module Server
95
- def accept_each(timeout: nil, task: Task.current)
96
- task.annotate "accepting connections #{self.local_address.inspect} [fd=#{self.fileno}]"
97
-
98
- callback = lambda do |io, address|
99
- yield io, address, task: task
100
- end
101
-
102
- while true
103
- self.accept(timeout: timeout, task: task, &callback)
104
- end
105
- end
106
- end
107
-
108
42
  class Socket < BasicSocket
109
43
  wraps ::Socket, :bind, :ipv6only!, :listen
110
44
 
@@ -21,6 +21,8 @@
21
21
  require_relative 'buffer'
22
22
  require_relative 'generic'
23
23
 
24
+ require 'async/semaphore'
25
+
24
26
  module Async
25
27
  module IO
26
28
  class Stream
@@ -44,6 +46,7 @@ module Async
44
46
 
45
47
  @deferred = deferred
46
48
  @pending = 0
49
+ @writing = Async::Semaphore.new(1)
47
50
 
48
51
  # We don't want Ruby to do any IO buffering.
49
52
  @io.sync = sync
@@ -53,6 +56,7 @@ module Async
53
56
 
54
57
  @read_buffer = Buffer.new
55
58
  @write_buffer = Buffer.new
59
+ @drain_buffer = Buffer.new
56
60
 
57
61
  # Used as destination buffer for underlying reads.
58
62
  @input_buffer = Buffer.new
@@ -136,29 +140,8 @@ module Async
136
140
  end
137
141
  end
138
142
 
139
- # Writes `string` to the buffer. When the buffer is full or #sync is true the
140
- # buffer is flushed to the underlying `io`.
141
- # @param string the string to write to the buffer.
142
- # @return the number of bytes appended to the buffer.
143
- def write(string)
144
- if @write_buffer.empty? and string.bytesize >= @block_size
145
- @io.write(string)
146
- else
147
- @write_buffer << string
148
-
149
- if @write_buffer.bytesize >= @block_size
150
- drain_write_buffer
151
- end
152
- end
153
-
154
- return string.bytesize
155
- end
156
-
157
- # Writes `string` to the stream and returns self.
158
- def <<(string)
159
- write(string)
160
-
161
- return self
143
+ def gets(separator = $/, **options)
144
+ read_until(separator, **options)
162
145
  end
163
146
 
164
147
  # Flushes buffered data to the stream.
@@ -176,8 +159,25 @@ module Async
176
159
  end
177
160
  end
178
161
 
179
- def gets(separator = $/, **options)
180
- read_until(separator, **options)
162
+ # Writes `string` to the buffer. When the buffer is full or #sync is true the
163
+ # buffer is flushed to the underlying `io`.
164
+ # @param string the string to write to the buffer.
165
+ # @return the number of bytes appended to the buffer.
166
+ def write(string)
167
+ @write_buffer << string
168
+
169
+ if @write_buffer.bytesize >= @block_size
170
+ flush
171
+ end
172
+
173
+ return string.bytesize
174
+ end
175
+
176
+ # Writes `string` to the stream and returns self.
177
+ def <<(string)
178
+ write(string)
179
+
180
+ return self
181
181
  end
182
182
 
183
183
  def puts(*arguments, separator: $/)
@@ -201,7 +201,7 @@ module Async
201
201
  end
202
202
 
203
203
  def close_write
204
- drain_write_buffer
204
+ drain_write_buffer unless @write_buffer.empty?
205
205
  ensure
206
206
  @io.close_write
207
207
  end
@@ -242,17 +242,20 @@ module Async
242
242
  private
243
243
 
244
244
  def drain_write_buffer
245
- Async.logger.debug(self) do
246
- if @pending > 0
247
- "Draining #{@pending} writes (#{@write_buffer.bytesize} bytes)..."
248
- else
249
- "Draining immediate write (#{@write_buffer.bytesize} bytes)..."
245
+ @writing.acquire do
246
+ Async.logger.debug(self) do
247
+ if @pending > 0
248
+ "Draining #{@pending} writes (#{@write_buffer.bytesize} bytes)..."
249
+ else
250
+ "Draining immediate write (#{@write_buffer.bytesize} bytes)..."
251
+ end
250
252
  end
253
+
254
+ @write_buffer, @drain_buffer = @drain_buffer, @write_buffer
255
+
256
+ @io.write(@drain_buffer)
257
+ @drain_buffer.clear
251
258
  end
252
-
253
- # Async.logger.debug(self, name: "write") {@write_buffer.inspect}
254
- @io.write(@write_buffer)
255
- @write_buffer.clear
256
259
  end
257
260
 
258
261
  # Fills the buffer from the underlying stream.
@@ -64,7 +64,7 @@ module Async
64
64
  # @param options keyword arguments passed through to {UNIXEndpoint#initialize}
65
65
  #
66
66
  # @return [UNIXEndpoint]
67
- def self.unix(path, type = ::Socket::SOCK_STREAM, **options)
67
+ def self.unix(path = "", type = ::Socket::SOCK_STREAM, **options)
68
68
  UNIXEndpoint.new(path, type, **options)
69
69
  end
70
70
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module IO
23
- VERSION = "1.27.1"
23
+ VERSION = "1.27.2"
24
24
  end
25
25
  end
@@ -19,7 +19,10 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require 'async/io/endpoint'
22
+
22
23
  require 'async/io/tcp_socket'
24
+ require 'async/io/socket_endpoint'
25
+ require 'async/io/ssl_endpoint'
23
26
 
24
27
  RSpec.describe Async::IO::Endpoint do
25
28
  include_context Async::RSpec::Reactor
@@ -24,8 +24,7 @@ require 'async/rspec/ssl'
24
24
  require 'async/io/host_endpoint'
25
25
  require 'async/io/shared_endpoint'
26
26
 
27
- require 'async/container/forked'
28
- require 'async/container/threaded'
27
+ require 'async/container'
29
28
 
30
29
  RSpec.shared_examples_for Async::IO::SharedEndpoint do |container_class|
31
30
  include_context Async::RSpec::SSL::VerifiedContexts
@@ -44,23 +43,20 @@ RSpec.shared_examples_for Async::IO::SharedEndpoint do |container_class|
44
43
  let(:container) {container_class.new}
45
44
 
46
45
  it "can use bound endpoint in container" do
47
- container.run(count: 1) do
46
+ container.async do
48
47
  bound_endpoint.accept do |peer|
49
48
  peer.write "Hello World"
50
49
  peer.close
51
50
  end
52
51
  end
53
52
 
54
- container.wait do
55
- Async do
56
- client_endpoint.connect do |peer|
57
- expect(peer.read(11)).to eq "Hello World"
58
- end
53
+ Async do
54
+ client_endpoint.connect do |peer|
55
+ expect(peer.read(11)).to eq "Hello World"
59
56
  end
60
-
61
- container.stop(false)
62
57
  end
63
58
 
59
+ container.stop
64
60
  bound_endpoint.close
65
61
  end
66
62
  end
@@ -125,7 +125,7 @@ RSpec.describe Async::IO::SSLServer do
125
125
  end.to raise_exception(OpenSSL::SSL::SSLError, /handshake failure/)
126
126
 
127
127
  server_task.stop
128
- end
128
+ end.wait
129
129
  end
130
130
  end
131
131
  end
@@ -87,7 +87,7 @@ RSpec.describe Async::IO::SSLSocket do
87
87
  expect do
88
88
  client_endpoint.connect
89
89
  end.to raise_exception(OpenSSL::SSL::SSLError)
90
- end
90
+ end.wait
91
91
  end
92
92
  end
93
93
  end
@@ -46,6 +46,37 @@ RSpec.describe Async::IO::Stream do
46
46
 
47
47
  it_should_behave_like Async::IO
48
48
 
49
+ describe '#drain_write_buffer' do
50
+ include_context Async::RSpec::Reactor
51
+ let(:output) {described_class.new(sockets.last)}
52
+ subject {described_class.new(sockets.first)}
53
+
54
+ let(:buffer_size) {1024*6}
55
+
56
+ it "can interleave calls to flush" do
57
+ tasks = 2.times.map do |i|
58
+ reactor.async do
59
+ buffer = i.to_s * buffer_size
60
+ 128.times do
61
+ output.write(buffer)
62
+ output.flush
63
+ end
64
+ end
65
+ end
66
+
67
+ reactor.async do
68
+ tasks.each(&:wait)
69
+ output.close
70
+ end
71
+
72
+ Async::Task.current.sleep(1)
73
+
74
+ while buffer = subject.read(buffer_size)
75
+ expect(buffer).to be == (buffer[0] * buffer_size)
76
+ end
77
+ end
78
+ end
79
+
49
80
  describe '#close_read' do
50
81
  subject {described_class.new(sockets.last)}
51
82
 
@@ -49,6 +49,6 @@ RSpec.describe Async::IO::UDPSocket do
49
49
  client.close
50
50
 
51
51
  expect(response).to be == data
52
- end
52
+ end.wait
53
53
  end
54
54
  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.27.1
4
+ version: 1.27.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-06 00:00:00.000000000 Z
11
+ date: 2020-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -44,16 +44,16 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.10.0
47
+ version: '0.15'
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: 0.10.0
54
+ version: '0.15'
55
55
  - !ruby/object:Gem::Dependency
56
- name: bundler
56
+ name: covered
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: covered
70
+ name: bundler
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -148,8 +148,10 @@ files:
148
148
  - lib/async/io/generic.rb
149
149
  - lib/async/io/host_endpoint.rb
150
150
  - lib/async/io/notification.rb
151
+ - lib/async/io/peer.rb
151
152
  - lib/async/io/protocol/generic.rb
152
153
  - lib/async/io/protocol/line.rb
154
+ - lib/async/io/server.rb
153
155
  - lib/async/io/shared_endpoint.rb
154
156
  - lib/async/io/socket.rb
155
157
  - lib/async/io/socket_endpoint.rb
@@ -208,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
208
210
  - !ruby/object:Gem::Version
209
211
  version: '0'
210
212
  requirements: []
211
- rubygems_version: 3.0.6
213
+ rubygems_version: 3.1.2
212
214
  signing_key:
213
215
  specification_version: 4
214
216
  summary: Provides support for asynchonous TCP, UDP, UNIX and SSL sockets.