async-io 1.12.1 → 1.12.2
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/async-io.gemspec +1 -1
- data/examples/chat/client.rb +40 -0
- data/examples/chat/server.rb +81 -0
- data/examples/{broken_ssl.rb → issues/broken_ssl.rb} +0 -0
- data/lib/async/io/protocol/line.rb +3 -1
- data/lib/async/io/version.rb +1 -1
- data/spec/async/io/protocol/line_spec.rb +14 -7
- data/spec/async/io/stream_spec.rb +11 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '02688119fe7ef47b527495b74b5b60d5826de683dc4bc798b63f6c61ee962049'
|
4
|
+
data.tar.gz: c15e88f8b47470fe35b97ba674faaa20ac39f3cbab617a47523b66c516103125
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 224d6230ea743a3c4d5132222d4ef74c61362fa487c8da642ee470333be2cb8656604f5b4ccec4d28e0b799c22d13207c605ad1c4a53f2614e6cbc13c7d9ccf3
|
7
|
+
data.tar.gz: a129eceb200dc5171465d6b6f902efcf37d2c6b4d722c5ae1b82c716e66e78a77c8585742950482006a89fc86fc166ec6d83ce641eea09b7ef0e2ceefd7326f9
|
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.6"
|
21
21
|
|
22
22
|
spec.required_ruby_version = '~> 2.3'
|
23
23
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.expand_path("../../lib", __dir__)
|
4
|
+
|
5
|
+
require 'async/reactor'
|
6
|
+
require 'async/io/stream'
|
7
|
+
require 'async/io/host_endpoint'
|
8
|
+
require 'async/io/protocol/line'
|
9
|
+
|
10
|
+
class User < Async::IO::Protocol::Line
|
11
|
+
end
|
12
|
+
|
13
|
+
endpoint = Async::IO::Endpoint.parse(ARGV.pop || "tcp://localhost:7138")
|
14
|
+
|
15
|
+
input = Async::IO::Protocol::Line.new(
|
16
|
+
Async::IO::Stream.new(
|
17
|
+
Async::IO::Generic.new($stdin)
|
18
|
+
)
|
19
|
+
)
|
20
|
+
|
21
|
+
Async::Reactor.run do |task|
|
22
|
+
socket = endpoint.connect
|
23
|
+
stream = Async::IO::Stream.new(socket)
|
24
|
+
user = User.new(stream)
|
25
|
+
|
26
|
+
connection = task.async do
|
27
|
+
while line = user.read_line
|
28
|
+
puts line
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
while line = input.read_line
|
33
|
+
user.write_lines line
|
34
|
+
end
|
35
|
+
rescue EOFError
|
36
|
+
# It's okay, we are disconnecting, because stdin has closed.
|
37
|
+
ensure
|
38
|
+
connection.stop
|
39
|
+
user.close
|
40
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.expand_path("../../lib", __dir__)
|
4
|
+
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
require 'async/reactor'
|
8
|
+
require 'async/io/host_endpoint'
|
9
|
+
require 'async/io/protocol/line'
|
10
|
+
|
11
|
+
class User < Async::IO::Protocol::Line
|
12
|
+
attr_accessor :name
|
13
|
+
|
14
|
+
def login!
|
15
|
+
self.write_lines "Tell me your name, traveller:"
|
16
|
+
self.name = self.read_line
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@name || "unknown"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Server
|
25
|
+
def initialize(endpoint)
|
26
|
+
@endpoint = endpoint
|
27
|
+
@users = Set.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def broadcast(*message)
|
31
|
+
puts *message
|
32
|
+
|
33
|
+
@users.each do |user|
|
34
|
+
begin
|
35
|
+
user.write_lines(*message)
|
36
|
+
rescue EOFError
|
37
|
+
# In theory, it's possible this will fail if the remote end has disconnected. Each user has it's own task running `#connected`, and eventually `user.read_line` will fail. When it does, the disconnection logic will be invoked. A better way to do this would be to have a message queue, but for the sake of keeping this example simple, this is by far the better option.
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def connected(user)
|
43
|
+
user.login!
|
44
|
+
|
45
|
+
broadcast("#{user} has joined")
|
46
|
+
|
47
|
+
user.write_lines("currently connected: #{@users.map(&:to_s).join(', ')}")
|
48
|
+
|
49
|
+
while message = user.read_line
|
50
|
+
broadcast("#{user.name}: #{message}")
|
51
|
+
end
|
52
|
+
rescue EOFError
|
53
|
+
# It's okay, client has disconnected.
|
54
|
+
ensure
|
55
|
+
disconnected(user)
|
56
|
+
end
|
57
|
+
|
58
|
+
def disconnected(user, reason = "quit")
|
59
|
+
@users.delete(user)
|
60
|
+
|
61
|
+
broadcast("#{user} has disconnected: #{reason}")
|
62
|
+
end
|
63
|
+
|
64
|
+
def run
|
65
|
+
Async::Reactor.run do |task|
|
66
|
+
@endpoint.accept do |peer|
|
67
|
+
stream = Async::IO::Stream.new(peer)
|
68
|
+
user = User.new(stream)
|
69
|
+
|
70
|
+
@users << user
|
71
|
+
|
72
|
+
connected(user)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
endpoint = Async::IO::Endpoint.parse(ARGV.pop || "tcp://localhost:7138")
|
79
|
+
server = Server.new(endpoint)
|
80
|
+
|
81
|
+
server.run
|
File without changes
|
@@ -24,7 +24,7 @@ module Async
|
|
24
24
|
module IO
|
25
25
|
module Protocol
|
26
26
|
class Line
|
27
|
-
def initialize(stream, eol =
|
27
|
+
def initialize(stream, eol = $/)
|
28
28
|
@stream = stream
|
29
29
|
@eol = eol
|
30
30
|
end
|
@@ -45,6 +45,8 @@ module Async
|
|
45
45
|
@stream.write(@eol)
|
46
46
|
end
|
47
47
|
end
|
48
|
+
|
49
|
+
@stream.flush
|
48
50
|
end
|
49
51
|
|
50
52
|
def read_line
|
data/lib/async/io/version.rb
CHANGED
@@ -23,12 +23,19 @@ require 'async/io/protocol/line'
|
|
23
23
|
RSpec.describe Async::IO::Protocol::Line do
|
24
24
|
let(:io) {StringIO.new}
|
25
25
|
let(:stream) {Async::IO::Stream.new(io)}
|
26
|
-
|
26
|
+
subject {described_class.new(stream, "\n")}
|
27
|
+
|
28
|
+
context "default line ending" do
|
29
|
+
subject {described_class.new(stream)}
|
30
|
+
|
31
|
+
it "should have default eol terminator" do
|
32
|
+
expect(subject.eol).to_not be_nil
|
33
|
+
end
|
34
|
+
end
|
27
35
|
|
28
36
|
describe '#write_lines' do
|
29
37
|
it "should write line" do
|
30
|
-
|
31
|
-
stream.flush
|
38
|
+
subject.write_lines "Hello World"
|
32
39
|
|
33
40
|
expect(io.string).to be == "Hello World\n"
|
34
41
|
end
|
@@ -41,11 +48,11 @@ RSpec.describe Async::IO::Protocol::Line do
|
|
41
48
|
end
|
42
49
|
|
43
50
|
it "should read one line" do
|
44
|
-
expect(
|
51
|
+
expect(subject.read_line).to be == "Hello World"
|
45
52
|
end
|
46
53
|
|
47
54
|
it "should be binary encoding" do
|
48
|
-
expect(
|
55
|
+
expect(subject.read_line.encoding).to be == Encoding::BINARY
|
49
56
|
end
|
50
57
|
end
|
51
58
|
|
@@ -56,11 +63,11 @@ RSpec.describe Async::IO::Protocol::Line do
|
|
56
63
|
end
|
57
64
|
|
58
65
|
it "should read multiple lines" do
|
59
|
-
expect(
|
66
|
+
expect(subject.read_lines).to be == ["Hello", "World"]
|
60
67
|
end
|
61
68
|
|
62
69
|
it "should be binary encoding" do
|
63
|
-
expect(
|
70
|
+
expect(subject.read_lines.first.encoding).to be == Encoding::BINARY
|
64
71
|
end
|
65
72
|
end
|
66
73
|
end
|
@@ -50,6 +50,8 @@ RSpec.describe Async::IO::Stream do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
describe '#read_until' do
|
53
|
+
include_context Async::RSpec::Memory
|
54
|
+
|
53
55
|
it "can read a line" do
|
54
56
|
io.write("hello\nworld\n")
|
55
57
|
io.seek(0)
|
@@ -58,6 +60,15 @@ RSpec.describe Async::IO::Stream do
|
|
58
60
|
expect(stream.read_until("\n")).to be == 'world'
|
59
61
|
expect(stream.read_until("\n")).to be_nil
|
60
62
|
end
|
63
|
+
|
64
|
+
it "minimises allocations" do
|
65
|
+
io.write("hello\nworld\n")
|
66
|
+
io.seek(0)
|
67
|
+
|
68
|
+
expect do
|
69
|
+
stream.read_until("\n")
|
70
|
+
end.to limit_allocations(String => 3)
|
71
|
+
end
|
61
72
|
end
|
62
73
|
|
63
74
|
describe '#flush' do
|
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.12.
|
4
|
+
version: 1.12.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: 2018-
|
11
|
+
date: 2018-06-10 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.6'
|
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.6'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,7 +94,9 @@ files:
|
|
94
94
|
- README.md
|
95
95
|
- Rakefile
|
96
96
|
- async-io.gemspec
|
97
|
-
- examples/
|
97
|
+
- examples/chat/client.rb
|
98
|
+
- examples/chat/server.rb
|
99
|
+
- examples/issues/broken_ssl.rb
|
98
100
|
- lib/async/io.rb
|
99
101
|
- lib/async/io/address.rb
|
100
102
|
- lib/async/io/address_endpoint.rb
|