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 +4 -4
- data/README.md +1 -1
- data/async-io.gemspec +1 -1
- data/lib/async/io/notification.rb +0 -2
- data/lib/async/io/stream.rb +47 -16
- data/lib/async/io/version.rb +1 -1
- data/spec/async/io/ssl_server_spec.rb +86 -22
- data/spec/async/io/stream_spec.rb +46 -2
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b298c4c118c511eaabda86500a8d08bf02b01cd156e0858cb962f4d3005ce665
|
4
|
+
data.tar.gz: 1a231a883488b441d599991043409204b2f282df470fdd8b15d18afa6f9b8e06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b87b6ca52a9e6164e4c6e549eaa2581d2c71908488f1f8235f0a2a8bbe25dd6e551a419b65f9ebd4be6b5c0182e99315c552cf36cc309777d311cd79397accc
|
7
|
+
data.tar.gz: 1cd79fdb63398e13ce10041b4facf4bb1dc5498065c18eac8f3265cd2f842f6360b6f31e2b41acf11df29c62096810940050f4e51a7bceb4e9e242af9cfbf81d
|
data/README.md
CHANGED
data/async-io.gemspec
CHANGED
@@ -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.
|
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"
|
data/lib/async/io/stream.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
77
|
-
|
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
|
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
|
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
|
data/lib/async/io/version.rb
CHANGED
@@ -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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
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
|
-
|
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.
|
29
|
+
io.write "Hello World"
|
30
30
|
io.seek(0)
|
31
31
|
|
32
|
-
expect(
|
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.
|
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
|
+
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.
|
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.
|
40
|
+
version: '1.4'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|