async-io 1.7.0 → 1.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de12378d6473944c46fa065b1b88b71069055f82fbcd565dc403e5430ed3f137
4
- data.tar.gz: 65d6c18c5adc877aa518f4de267023dd5623440d0424fb302fb54af92f4a7c03
3
+ metadata.gz: b298c4c118c511eaabda86500a8d08bf02b01cd156e0858cb962f4d3005ce665
4
+ data.tar.gz: 1a231a883488b441d599991043409204b2f282df470fdd8b15d18afa6f9b8e06
5
5
  SHA512:
6
- metadata.gz: 966cc66e1081be1c43f0a54ee5722c70689b12b63555ec3ff448714ed24e344347b0eb3e8716af027d42a293464a8f70c2f97eac71544f169eacdd68fd18b3ed
7
- data.tar.gz: dd68773cafc1b02f8512e77f696b5ec4d12f4d4287ca6ff80084c54b57ef65e545116e6388bf8301aade58e946a3d2a7d2d118ef1b2bd02db502a7c95f16c48e
6
+ metadata.gz: 8b87b6ca52a9e6164e4c6e549eaa2581d2c71908488f1f8235f0a2a8bbe25dd6e551a419b65f9ebd4be6b5c0182e99315c552cf36cc309777d311cd79397accc
7
+ data.tar.gz: 1cd79fdb63398e13ce10041b4facf4bb1dc5498065c18eac8f3265cd2f842f6360b6f31e2b41acf11df29c62096810940050f4e51a7bceb4e9e242af9cfbf81d
data/README.md CHANGED
@@ -22,7 +22,7 @@ And then execute:
22
22
 
23
23
  Or install it yourself as:
24
24
 
25
- $ gem install async-socket
25
+ $ gem install async-io
26
26
 
27
27
  ## Usage
28
28
 
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.has_rdoc = "yard"
18
18
 
19
19
  spec.add_dependency "async", "~> 1.3"
20
- spec.add_development_dependency "async-rspec", "~> 1.2"
20
+ spec.add_development_dependency "async-rspec", "~> 1.4"
21
21
 
22
22
  spec.add_development_dependency "bundler", "~> 1.3"
23
23
  spec.add_development_dependency "rake", "~> 10.0"
@@ -29,8 +29,6 @@ module Async
29
29
 
30
30
  @input = pipe.first
31
31
  @output = pipe.last
32
-
33
- @count = 0
34
32
  end
35
33
 
36
34
  def close
@@ -38,27 +38,49 @@ module Async
38
38
  end
39
39
 
40
40
  attr :io
41
+ attr :block_size
41
42
 
42
43
  # Reads `size` bytes from the stream. If size is not specified, read until end of file.
43
44
  def read(size = nil)
44
- return "" if size == 0
45
+ return '' if size == 0
46
+
47
+ if size
48
+ if size <= @block_size
49
+ fill_read_buffer until @eof or @read_buffer.size >= size
50
+ else
51
+ fill_read_buffer(size - @read_buffer.size) until @eof or @read_buffer.size >= size
52
+ end
53
+ else
54
+ fill_read_buffer until @eof
55
+ end
45
56
 
46
- until @eof || (size && size <= @read_buffer.size)
57
+ return consume_read_buffer(size)
58
+ end
59
+
60
+ # Read at most `size` bytes from the stream. Will avoid reading from the underlying stream if possible.
61
+ def read_partial(size = nil)
62
+ return '' if size == 0
63
+
64
+ if @read_buffer.empty? and !@eof
47
65
  fill_read_buffer
48
66
  end
49
-
67
+
50
68
  return consume_read_buffer(size)
51
69
  end
52
-
70
+
53
71
  # Writes `string` to the buffer. When the buffer is full or #sync is true the
54
72
  # buffer is flushed to the underlying `io`.
55
73
  # @param string the string to write to the buffer.
56
74
  # @return the number of bytes appended to the buffer.
57
75
  def write(string)
58
- @write_buffer << string
59
-
60
- if @write_buffer.size > @block_size
61
- flush
76
+ if @write_buffer.empty? and string.bytesize >= @block_size
77
+ syswrite(string)
78
+ else
79
+ @write_buffer << string
80
+
81
+ if @write_buffer.size >= @block_size
82
+ flush
83
+ end
62
84
  end
63
85
 
64
86
  return string.bytesize
@@ -73,8 +95,10 @@ module Async
73
95
 
74
96
  # Flushes buffered data to the stream.
75
97
  def flush
76
- syswrite(@write_buffer)
77
- @write_buffer.clear
98
+ unless @write_buffer.empty?
99
+ syswrite(@write_buffer)
100
+ @write_buffer.clear
101
+ end
78
102
  end
79
103
 
80
104
  def gets(separator = $/)
@@ -138,15 +162,13 @@ module Async
138
162
  private
139
163
 
140
164
  # Fills the buffer from the underlying stream.
141
- def fill_read_buffer
142
- if buffer = @io.read(@block_size)
143
- @read_buffer << buffer
144
- else
165
+ def fill_read_buffer(size = @block_size)
166
+ if !sysread(size, @read_buffer)
145
167
  @eof = true
146
168
  end
147
169
  end
148
170
 
149
- # Consumes `size` bytes from the buffer.
171
+ # Consumes at most `size` bytes from the buffer.
150
172
  # @param size [Integer|nil] The amount of data to consume. If nil, consume entire buffer.
151
173
  def consume_read_buffer(size = nil)
152
174
  # If we are at eof, and the read buffer is empty, we can't consume anything.
@@ -154,7 +176,7 @@ module Async
154
176
 
155
177
  result = nil
156
178
 
157
- if size == nil || size == @read_buffer.size
179
+ if size == nil || size >= @read_buffer.size
158
180
  # Consume the entire read buffer:
159
181
  result = @read_buffer.dup
160
182
  @read_buffer.clear
@@ -187,6 +209,15 @@ module Async
187
209
 
188
210
  return written
189
211
  end
212
+
213
+ # Read to
214
+ def sysread(size, buffer)
215
+ if buffer.empty?
216
+ @io.read(size, buffer)
217
+ elsif chunk = @io.read(size)
218
+ buffer << chunk
219
+ end
220
+ end
190
221
  end
191
222
  end
192
223
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module IO
23
- VERSION = "1.7.0"
23
+ VERSION = "1.8.0"
24
24
  end
25
25
  end
@@ -25,36 +25,100 @@ require_relative 'generic_examples'
25
25
 
26
26
  RSpec.describe Async::IO::SSLServer do
27
27
  include_context Async::RSpec::Reactor
28
- include_context Async::RSpec::SSL::VerifiedContexts
29
- include_context Async::RSpec::SSL::ValidCertificate
30
28
 
31
- let(:endpoint) {Async::IO::Endpoint.tcp("127.0.0.1", 6780, reuse_port: true)}
32
- let(:server_endpoint) {Async::IO::SecureEndpoint.new(endpoint, ssl_context: server_context)}
33
- let(:client_endpoint) {Async::IO::SecureEndpoint.new(endpoint, ssl_context: client_context)}
34
-
35
- let(:data) {"What one programmer can do in one month, two programmers can do in two months."}
36
-
37
- it 'can accept_each connections' do
38
- # Accept a single incoming connection and then finish.
39
- server_task = reactor.async do |task|
40
- server_endpoint.bind do |server|
41
- server.listen(10)
42
-
43
- server.accept_each do |peer, address|
44
- data = peer.read(512)
45
- peer.write(data)
29
+ context 'single host' do
30
+ include_context Async::RSpec::SSL::VerifiedContexts
31
+ include_context Async::RSpec::SSL::ValidCertificate
32
+
33
+ let(:endpoint) {Async::IO::Endpoint.tcp("127.0.0.1", 6780, reuse_port: true)}
34
+ let(:server_endpoint) {Async::IO::SecureEndpoint.new(endpoint, ssl_context: server_context)}
35
+ let(:client_endpoint) {Async::IO::SecureEndpoint.new(endpoint, ssl_context: client_context)}
36
+
37
+ let(:data) {"What one programmer can do in one month, two programmers can do in two months."}
38
+
39
+ it 'can accept_each connections' do
40
+ # Accept a single incoming connection and then finish.
41
+ server_task = reactor.async do |task|
42
+ server_endpoint.bind do |server|
43
+ server.listen(10)
44
+
45
+ server.accept_each do |peer, address|
46
+ data = peer.read(512)
47
+ peer.write(data)
48
+ end
46
49
  end
47
50
  end
51
+
52
+ reactor.async do
53
+ client_endpoint.connect do |client|
54
+ client.write(data)
55
+
56
+ expect(client.read(512)).to be == data
57
+ end
58
+
59
+ server_task.stop
60
+ end
48
61
  end
62
+ end
63
+
64
+ context 'multiple hosts' do
65
+ let(:hosts) {['test.com', 'example.com']}
66
+
67
+ include_context Async::RSpec::SSL::HostCertificates
49
68
 
50
- reactor.async do
51
- client_endpoint.connect do |client|
52
- client.write(data)
69
+ let(:endpoint) {Async::IO::Endpoint.tcp("127.0.0.1", 6782, reuse_port: true)}
70
+ let(:server_endpoint) {Async::IO::SecureEndpoint.new(endpoint, ssl_context: server_context)}
71
+ let(:valid_client_endpoint) {Async::IO::SecureEndpoint.new(endpoint, hostname: 'example.com', ssl_context: client_context)}
72
+ let(:invalid_client_endpoint) {Async::IO::SecureEndpoint.new(endpoint, hostname: 'fleeb.com', ssl_context: client_context)}
73
+
74
+ let(:data) {"What one programmer can do in one month, two programmers can do in two months."}
75
+
76
+ it 'can select correct host' do
77
+ # Accept a single incoming connection and then finish.
78
+ server_task = reactor.async do |task|
79
+ server_endpoint.bind do |server|
80
+ server.listen(10)
81
+
82
+ server.accept_each do |peer, address|
83
+ expect(peer.hostname).to be == 'example.com'
84
+
85
+ data = peer.read(512)
86
+ peer.write(data)
87
+ end
88
+ end
89
+ end
90
+
91
+ reactor.async do
92
+ valid_client_endpoint.connect do |client|
93
+ client.write(data)
94
+
95
+ expect(client.read(512)).to be == data
96
+ end
53
97
 
54
- expect(client.read(512)).to be == data
98
+ server_task.stop
99
+ end
100
+ end
101
+
102
+ it 'it fails with invalid host' do
103
+ # Accept a single incoming connection and then finish.
104
+ server_task = reactor.async do |task|
105
+ server_endpoint.bind do |server|
106
+ server.listen(10)
107
+
108
+ server.accept_each do |peer, address|
109
+ peer.close
110
+ end
111
+ end
55
112
  end
56
113
 
57
- server_task.stop
114
+ reactor.async do
115
+ expect do
116
+ invalid_client_endpoint.connect do |client|
117
+ end
118
+ end.to raise_error(OpenSSL::SSL::SSLError, /handshake failure/)
119
+
120
+ server_task.stop
121
+ end
58
122
  end
59
123
  end
60
124
  end
@@ -26,16 +26,60 @@ RSpec.describe Async::IO::Stream do
26
26
 
27
27
  describe '#read' do
28
28
  it "should read everything" do
29
- io.puts "Hello World"
29
+ io.write "Hello World"
30
30
  io.seek(0)
31
31
 
32
- expect(stream.read).to be == "Hello World\n"
32
+ expect(io).to receive(:read).and_call_original.twice
33
+
34
+ expect(stream.read).to be == "Hello World"
35
+ expect(stream).to be_eof
36
+ end
37
+
38
+ it "should read only the amount requested" do
39
+ io.write "Hello World"
40
+ io.seek(0)
41
+
42
+ expect(io).to receive(:read).and_call_original.twice
43
+
44
+ expect(stream.read(4)).to be == "Hell"
45
+ expect(stream).to_not be_eof
46
+
47
+ expect(stream.read(20)).to be == "o World"
33
48
  expect(stream).to be_eof
34
49
  end
35
50
  end
36
51
 
52
+ describe '#flush' do
53
+ it "should not call write if write buffer is empty" do
54
+ expect(io).to_not receive(:write)
55
+
56
+ stream.flush
57
+ end
58
+
59
+ it "should flush underlying data when it exceeds block size" do
60
+ expect(io).to receive(:write).and_call_original.once
61
+
62
+ stream.block_size.times do
63
+ stream.write("!")
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '#read_partial' do
69
+ it "should avoid calling read" do
70
+ io.write "Hello World" * 1024
71
+ io.seek(0)
72
+
73
+ expect(io).to receive(:read).and_call_original.once
74
+
75
+ expect(stream.read_partial(11)).to be == "Hello World"
76
+ end
77
+ end
78
+
37
79
  describe '#write' do
38
80
  it "should read one line" do
81
+ expect(io).to receive(:write).and_call_original.once
82
+
39
83
  stream.write "Hello World\n"
40
84
  stream.flush
41
85
 
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.7.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-11 00:00:00.000000000 Z
11
+ date: 2018-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.2'
33
+ version: '1.4'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.2'
40
+ version: '1.4'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement