backport 0.3.0 → 1.0.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/.rubocop.yml +2 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +4 -0
- data/README.md +80 -80
- data/backport.gemspec +28 -27
- data/lib/backport.rb +1 -1
- data/lib/backport/adapter.rb +4 -4
- data/lib/backport/client.rb +42 -19
- data/lib/backport/machine.rb +6 -0
- data/lib/backport/server/connectable.rb +9 -3
- data/lib/backport/server/tcpip.rb +7 -25
- data/lib/backport/version.rb +1 -1
- metadata +3 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef66ad1688e603cf9a8c9844c5551bbc3ca9187abf747bca2064e46fb7c2600f
|
4
|
+
data.tar.gz: 3952f19adbe1639f82c98568d4adc97aa0c690d5fa568fef768efbcf6134ee7c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 334cb82482d72e1b617fa9fa2a1a1b87764b800e4a8abf6ecc2c63fc012406fb05bf744b362aa6f3211aae0f868ab188902132825602ee3909b54358e2cd1813
|
7
|
+
data.tar.gz: 2ff55f4495545b50e4e226f961d38b5471cc8d1890a4339091f126139f61422e1ae3af1e1a8c3fc80aafb915b187da099ef312a5e7da1ad4a500f737e9a6909c
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,80 +1,80 @@
|
|
1
|
-
# Backport
|
2
|
-
|
3
|
-
A pure Ruby library for event-driven IO.
|
4
|
-
|
5
|
-
This library is designed with portability as the highest priority, which is why it's written in pure Ruby. Consider [EventMachine](https://github.com/eventmachine/eventmachine) if you need a solution that's faster, more mature, and scalable.
|
6
|
-
|
7
|
-
## Installation
|
8
|
-
|
9
|
-
Install the gem:
|
10
|
-
|
11
|
-
```
|
12
|
-
gem install backport
|
13
|
-
```
|
14
|
-
|
15
|
-
Or add it to your application's Gemfile:
|
16
|
-
|
17
|
-
```ruby
|
18
|
-
gem 'backport'
|
19
|
-
```
|
20
|
-
|
21
|
-
## Usage
|
22
|
-
|
23
|
-
### Examples
|
24
|
-
|
25
|
-
A simple echo server:
|
26
|
-
|
27
|
-
```ruby
|
28
|
-
require 'backport'
|
29
|
-
|
30
|
-
module MyAdapter
|
31
|
-
def opening
|
32
|
-
puts "Opening a connection"
|
33
|
-
end
|
34
|
-
|
35
|
-
def closing
|
36
|
-
puts "Closing a connection"
|
37
|
-
end
|
38
|
-
|
39
|
-
def
|
40
|
-
write "Client sent: #{data}"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
Backport.run do
|
45
|
-
Backport.prepare_tcp_server(host: 'localhost', port: 8000, adapter: MyAdapter)
|
46
|
-
end
|
47
|
-
```
|
48
|
-
|
49
|
-
An interval server that runs once per second:
|
50
|
-
|
51
|
-
```ruby
|
52
|
-
require 'backport'
|
53
|
-
|
54
|
-
Backport.run do
|
55
|
-
Backport.prepare_interval 1 do
|
56
|
-
puts "tick"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
```
|
60
|
-
|
61
|
-
### Using Adapters
|
62
|
-
|
63
|
-
Backport servers that handle client connections, such as TCP servers, use an
|
64
|
-
adapter to provide an application interface to the client. Developers can
|
65
|
-
provide their own adapter implementations in two ways: a Ruby module that will
|
66
|
-
be used to extend a Backport::Adapter object, or a class that extends
|
67
|
-
Backport::Adapter. In either case, the adapter should provide the following
|
68
|
-
methods:
|
69
|
-
|
70
|
-
* `opening`: A callback triggered when the client connection is accepted
|
71
|
-
* `closing`: A callback triggered when the client connection is closed
|
72
|
-
* `
|
73
|
-
|
74
|
-
Backport::Adapter also provides the following methods:
|
75
|
-
|
76
|
-
* `write(data)`: Send raw data to the client
|
77
|
-
* `write_line(data)`: Send a line of data to the client
|
78
|
-
* `close`: Disconnect the client from the server
|
79
|
-
* `closed?`: True if the connection is closed
|
80
|
-
* `remote`: A hash of data about the client, e.g., the remote IP address
|
1
|
+
# Backport
|
2
|
+
|
3
|
+
A pure Ruby library for event-driven IO.
|
4
|
+
|
5
|
+
This library is designed with portability as the highest priority, which is why it's written in pure Ruby. Consider [EventMachine](https://github.com/eventmachine/eventmachine) if you need a solution that's faster, more mature, and scalable.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Install the gem:
|
10
|
+
|
11
|
+
```
|
12
|
+
gem install backport
|
13
|
+
```
|
14
|
+
|
15
|
+
Or add it to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'backport'
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Examples
|
24
|
+
|
25
|
+
A simple echo server:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
require 'backport'
|
29
|
+
|
30
|
+
module MyAdapter
|
31
|
+
def opening
|
32
|
+
puts "Opening a connection"
|
33
|
+
end
|
34
|
+
|
35
|
+
def closing
|
36
|
+
puts "Closing a connection"
|
37
|
+
end
|
38
|
+
|
39
|
+
def receiving data
|
40
|
+
write "Client sent: #{data}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Backport.run do
|
45
|
+
Backport.prepare_tcp_server(host: 'localhost', port: 8000, adapter: MyAdapter)
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
An interval server that runs once per second:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'backport'
|
53
|
+
|
54
|
+
Backport.run do
|
55
|
+
Backport.prepare_interval 1 do
|
56
|
+
puts "tick"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
### Using Adapters
|
62
|
+
|
63
|
+
Backport servers that handle client connections, such as TCP servers, use an
|
64
|
+
adapter to provide an application interface to the client. Developers can
|
65
|
+
provide their own adapter implementations in two ways: a Ruby module that will
|
66
|
+
be used to extend a Backport::Adapter object, or a class that extends
|
67
|
+
Backport::Adapter. In either case, the adapter should provide the following
|
68
|
+
methods:
|
69
|
+
|
70
|
+
* `opening`: A callback triggered when the client connection is accepted
|
71
|
+
* `closing`: A callback triggered when the client connection is closed
|
72
|
+
* `receiving(data)`: A callback triggered when the server receives data from the client
|
73
|
+
|
74
|
+
Backport::Adapter also provides the following methods:
|
75
|
+
|
76
|
+
* `write(data)`: Send raw data to the client
|
77
|
+
* `write_line(data)`: Send a line of data to the client
|
78
|
+
* `close`: Disconnect the client from the server
|
79
|
+
* `closed?`: True if the connection is closed
|
80
|
+
* `remote`: A hash of data about the client, e.g., the remote IP address
|
data/backport.gemspec
CHANGED
@@ -1,27 +1,28 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "backport/version"
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "backport"
|
8
|
-
spec.version = Backport::VERSION
|
9
|
-
spec.authors = ["Fred Snyder"]
|
10
|
-
spec.email = ["fsnyder@castwide.com"]
|
11
|
-
|
12
|
-
spec.summary = %q{A pure Ruby library for event-driven IO}
|
13
|
-
spec.homepage = "http://github.com/castwide/backport"
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
# Specify which files should be added to the gem when it is released.
|
17
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
-
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
-
end
|
21
|
-
spec.require_paths = ["lib"]
|
22
|
-
|
23
|
-
spec.
|
24
|
-
|
25
|
-
spec.add_development_dependency "
|
26
|
-
spec.add_development_dependency "
|
27
|
-
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "backport/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "backport"
|
8
|
+
spec.version = Backport::VERSION
|
9
|
+
spec.authors = ["Fred Snyder"]
|
10
|
+
spec.email = ["fsnyder@castwide.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{A pure Ruby library for event-driven IO}
|
13
|
+
spec.homepage = "http://github.com/castwide/backport"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
# Specify which files should be added to the gem when it is released.
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
+
end
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.required_ruby_version = '>= 2.1'
|
24
|
+
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
spec.add_development_dependency "simplecov", "~> 0.14"
|
28
|
+
end
|
data/lib/backport.rb
CHANGED
data/lib/backport/adapter.rb
CHANGED
@@ -40,13 +40,13 @@ module Backport
|
|
40
40
|
# @return [void]
|
41
41
|
def closing; end
|
42
42
|
|
43
|
-
# A callback triggered when the
|
44
|
-
# and/or modules should override this method to provide their
|
45
|
-
# functionality.
|
43
|
+
# A callback triggered when the server receives data from the client.
|
44
|
+
# Subclasses and/or modules should override this method to provide their
|
45
|
+
# own functionality.
|
46
46
|
#
|
47
47
|
# @param data [String]
|
48
48
|
# @return [void]
|
49
|
-
def
|
49
|
+
def receiving(data); end
|
50
50
|
|
51
51
|
# Send data to the client.
|
52
52
|
#
|
data/lib/backport/client.rb
CHANGED
@@ -5,6 +5,10 @@ module Backport
|
|
5
5
|
# @return [Adapter]
|
6
6
|
attr_reader :adapter
|
7
7
|
|
8
|
+
# @param input [IO]
|
9
|
+
# @param output [IO]
|
10
|
+
# @param adapter [Class, Module]
|
11
|
+
# @param remote [Hash]
|
8
12
|
def initialize input, output, adapter, remote = {}
|
9
13
|
@in = input
|
10
14
|
@out = output
|
@@ -13,6 +17,8 @@ module Backport
|
|
13
17
|
@buffer = ''
|
14
18
|
end
|
15
19
|
|
20
|
+
# True if the client is stopped.
|
21
|
+
#
|
16
22
|
def stopped?
|
17
23
|
@stopped ||= false
|
18
24
|
end
|
@@ -41,13 +47,22 @@ module Backport
|
|
41
47
|
# @deprecated Prefer #start to #run for non-blocking client/server methods
|
42
48
|
alias run start
|
43
49
|
|
44
|
-
#
|
50
|
+
# Handle a tick from the server. This method will check for client input
|
51
|
+
# and update the adapter accordingly, or stop the client if the adapter is
|
52
|
+
# closed.
|
45
53
|
#
|
46
|
-
# @
|
47
|
-
def
|
48
|
-
|
54
|
+
# @return [void]
|
55
|
+
def tick
|
56
|
+
if adapter.closed?
|
57
|
+
stop
|
58
|
+
else
|
59
|
+
input = read
|
60
|
+
@adapter.receiving input unless input.nil?
|
61
|
+
end
|
49
62
|
end
|
50
63
|
|
64
|
+
private
|
65
|
+
|
51
66
|
# Read the client input. Return nil if the input buffer is empty.
|
52
67
|
#
|
53
68
|
# @return [String, nil]
|
@@ -60,8 +75,7 @@ module Backport
|
|
60
75
|
return tmp unless tmp.empty?
|
61
76
|
end
|
62
77
|
|
63
|
-
|
64
|
-
|
78
|
+
# @return [Adapter]
|
65
79
|
def make_adapter cls_mod, remote
|
66
80
|
if cls_mod.is_a?(Class) && cls_mod <= Backport::Adapter
|
67
81
|
@adapter = cls_mod.new(@out, remote)
|
@@ -73,25 +87,34 @@ module Backport
|
|
73
87
|
end
|
74
88
|
end
|
75
89
|
|
90
|
+
# @return [Mutex]
|
76
91
|
def mutex
|
77
92
|
@mutex ||= Mutex.new
|
78
93
|
end
|
79
94
|
|
95
|
+
# Start the thread that checks the input IO for client data.
|
96
|
+
#
|
97
|
+
# @return [void]
|
80
98
|
def run_input_thread
|
81
99
|
Thread.new do
|
82
|
-
until stopped?
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
100
|
+
read_input until stopped?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Read input from the client.
|
105
|
+
#
|
106
|
+
# @return [void]
|
107
|
+
def read_input
|
108
|
+
@in.flush
|
109
|
+
begin
|
110
|
+
chars = @in.sysread(255)
|
111
|
+
rescue EOFError, Errno::ECONNRESET
|
112
|
+
chars = nil
|
113
|
+
end
|
114
|
+
if chars.nil?
|
115
|
+
stop
|
116
|
+
else
|
117
|
+
mutex.synchronize { @buffer.concat chars }
|
95
118
|
end
|
96
119
|
end
|
97
120
|
end
|
data/lib/backport/machine.rb
CHANGED
@@ -50,6 +50,9 @@ module Backport
|
|
50
50
|
@servers ||= []
|
51
51
|
end
|
52
52
|
|
53
|
+
# Update the machine's servers.
|
54
|
+
#
|
55
|
+
# @return [void]
|
53
56
|
def tick
|
54
57
|
servers.delete_if(&:stopped?)
|
55
58
|
stop if servers.empty?
|
@@ -58,6 +61,9 @@ module Backport
|
|
58
61
|
|
59
62
|
private
|
60
63
|
|
64
|
+
# Start the thread that updates servers via the #tick method.
|
65
|
+
#
|
66
|
+
# @return [void]
|
61
67
|
def run_server_thread
|
62
68
|
servers.map(&:start)
|
63
69
|
until stopped?
|
@@ -6,9 +6,9 @@ module Backport
|
|
6
6
|
#
|
7
7
|
module Connectable
|
8
8
|
def tick
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
mutex.synchronize do
|
10
|
+
clients.each(&:tick)
|
11
|
+
clients.delete_if(&:stopped?)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -24,6 +24,12 @@ module Backport
|
|
24
24
|
def clients
|
25
25
|
@clients ||= []
|
26
26
|
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def mutex
|
31
|
+
@mutex ||= Mutex.new
|
32
|
+
end
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
@@ -14,20 +14,6 @@ module Backport
|
|
14
14
|
@stopped = false
|
15
15
|
end
|
16
16
|
|
17
|
-
def tick
|
18
|
-
mutex.synchronize do
|
19
|
-
clients.each do |client|
|
20
|
-
if client.adapter.closed?
|
21
|
-
client.stop
|
22
|
-
next
|
23
|
-
end
|
24
|
-
input = client.read
|
25
|
-
client.sending input unless input.nil?
|
26
|
-
end
|
27
|
-
clients.delete_if(&:stopped?)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
17
|
def starting
|
32
18
|
start_accept_thread
|
33
19
|
end
|
@@ -37,8 +23,8 @@ module Backport
|
|
37
23
|
return if socket.closed?
|
38
24
|
begin
|
39
25
|
socket.shutdown Socket::SHUT_RDWR
|
40
|
-
rescue Errno::ENOTCONN, IOError =>
|
41
|
-
Backport.logger.info "Minor exception while stopping server [#{
|
26
|
+
rescue Errno::ENOTCONN, IOError => err
|
27
|
+
Backport.logger.info "Minor exception while stopping server [#{err.class}] #{err.message}"
|
42
28
|
end
|
43
29
|
socket.close
|
44
30
|
end
|
@@ -63,13 +49,13 @@ module Backport
|
|
63
49
|
clients.push Client.new(conn, conn, @adapter, data)
|
64
50
|
clients.last.run
|
65
51
|
result = clients.last
|
66
|
-
rescue IO::WaitReadable, Errno::EAGAIN
|
52
|
+
rescue IO::WaitReadable, Errno::EAGAIN
|
67
53
|
# ignore
|
68
|
-
rescue Errno::ENOTSOCK, IOError =>
|
69
|
-
Backport.logger.info "Server stopped with minor exception [#{
|
54
|
+
rescue Errno::ENOTSOCK, IOError => err
|
55
|
+
Backport.logger.info "Server stopped with minor exception [#{err.class}] #{err.message}"
|
70
56
|
stop
|
71
|
-
rescue Exception =>
|
72
|
-
Backport.logger.warn "Server stopped with major exception [#{
|
57
|
+
rescue Exception => err
|
58
|
+
Backport.logger.warn "Server stopped with major exception [#{err.class}] #{err.message}"
|
73
59
|
stop
|
74
60
|
end
|
75
61
|
end
|
@@ -81,10 +67,6 @@ module Backport
|
|
81
67
|
# @return [TCPSocket]
|
82
68
|
attr_reader :socket
|
83
69
|
|
84
|
-
def mutex
|
85
|
-
@mutex ||= Mutex.new
|
86
|
-
end
|
87
|
-
|
88
70
|
def start_accept_thread
|
89
71
|
Thread.new do
|
90
72
|
until stopped?
|
data/lib/backport/version.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fred Snyder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.17'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.17'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rake
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,7 +94,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
94
|
requirements:
|
109
95
|
- - ">="
|
110
96
|
- !ruby/object:Gem::Version
|
111
|
-
version: '
|
97
|
+
version: '2.1'
|
112
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
99
|
requirements:
|
114
100
|
- - ">="
|