async-io 1.7.0 → 1.8.0

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: 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