backport 0.1.0 → 0.2.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/.rspec +0 -1
- data/.travis.yml +11 -0
- data/CHANGELOG.md +12 -0
- data/README.md +12 -3
- data/backport.gemspec +1 -0
- data/lib/backport.rb +35 -6
- data/lib/backport/adapter.rb +51 -11
- data/lib/backport/client.rb +18 -12
- data/lib/backport/machine.rb +21 -6
- data/lib/backport/server/base.rb +24 -5
- data/lib/backport/server/connectable.rb +1 -3
- data/lib/backport/server/interval.rb +3 -6
- data/lib/backport/server/stdio.rb +1 -1
- data/lib/backport/server/tcpip.rb +39 -10
- data/lib/backport/version.rb +1 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2aedfc84d845aedeb6d7146cb21a74c4d1d74d90844407982ce454e8aa2d16b3
|
4
|
+
data.tar.gz: e222830378478e7990601880b42e60dd4e34ba1791e63345a2a5fa0a2898bbf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5cd8632b158f0e5dda4e5377b5fc74a9e7ae416a738941dcf276cd48d8040c671eb9e337a089215e092ec499fecbadfaf6c015f49f09b72bc4cee556780afa4f
|
7
|
+
data.tar.gz: e52ea4e36b3bfbd397dabc9410d07af6106d1c7bc1e9dbac98d18a2def432300e64696ee3e277ad1e19639d61027ccb46d3a3f665603031ffab26c81d4f7ba83
|
data/.rspec
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
## 0.2.0 - December 21, 2018
|
2
|
+
- Minor bug fixes in STDIO server
|
3
|
+
- More efficient client reads
|
4
|
+
- Rename server methods `prepare` for clarity
|
5
|
+
- Improved socket state handling
|
6
|
+
- More accurate interval time
|
7
|
+
- Adapter#remote attribute
|
8
|
+
- Adapter#close method
|
9
|
+
- Socket exception handling
|
10
|
+
|
11
|
+
## 0.1.0 - December 20, 2018
|
12
|
+
First release
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ gem 'backport'
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
|
23
|
+
A simple echo server:
|
24
24
|
|
25
25
|
```ruby
|
26
26
|
require 'backport'
|
@@ -40,8 +40,17 @@ module MyAdapter
|
|
40
40
|
end
|
41
41
|
|
42
42
|
Backport.run do
|
43
|
-
Backport.
|
44
|
-
|
43
|
+
Backport.prepare_tcp_server(host: 'localhost', port: 8000, adapter: MyAdapter)
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
An interval server that runs once per second:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
require 'backport'
|
51
|
+
|
52
|
+
Backport.run do
|
53
|
+
Backport.prepare_interval 1 do
|
45
54
|
puts "tick"
|
46
55
|
end
|
47
56
|
end
|
data/backport.gemspec
CHANGED
data/lib/backport.rb
CHANGED
@@ -8,28 +8,57 @@ module Backport
|
|
8
8
|
autoload :Client, 'backport/client'
|
9
9
|
|
10
10
|
class << self
|
11
|
-
|
12
|
-
|
11
|
+
# Prepare a STDIO server to run in Backport.
|
12
|
+
#
|
13
|
+
# @param adapter [Adapter]
|
14
|
+
# @return [void]
|
15
|
+
def prepare_stdio_server adapter: Adapter
|
16
|
+
machine.prepare Backport::Server::Stdio.new(adapter: adapter)
|
13
17
|
end
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
# Prepare a TCP server to run in Backport.
|
20
|
+
#
|
21
|
+
# @param host [String]
|
22
|
+
# @param port [Integer]
|
23
|
+
# @param adapter [Adapter]
|
24
|
+
# @return [void]
|
25
|
+
def prepare_tcp_server host: 'localhost', port: 1117, adapter: Adapter
|
26
|
+
machine.prepare Backport::Server::Tcpip.new(host: host, port: port, adapter: adapter)
|
17
27
|
end
|
18
28
|
|
19
|
-
|
20
|
-
|
29
|
+
# Prepare an interval server to run in Backport.
|
30
|
+
#
|
31
|
+
# @param period [Float] Seconds between intervals
|
32
|
+
# @return [void]
|
33
|
+
def prepare_interval period, &block
|
34
|
+
machine.prepare Backport::Server::Interval.new(period, &block)
|
21
35
|
end
|
22
36
|
|
37
|
+
# Run the Backport machine. The provided block will be executed before the
|
38
|
+
# machine starts. Program execution is halted until the machine stops.
|
39
|
+
#
|
40
|
+
# @example Print "tick" once per second
|
41
|
+
# Backport.run do
|
42
|
+
# Backport.prepare_interval 1 do
|
43
|
+
# puts "tick"
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# @return [void]
|
23
48
|
def run &block
|
24
49
|
machine.run &block
|
25
50
|
end
|
26
51
|
|
52
|
+
# Stop the Backport machine.
|
53
|
+
#
|
54
|
+
# @return [void]
|
27
55
|
def stop
|
28
56
|
machine.stop
|
29
57
|
end
|
30
58
|
|
31
59
|
private
|
32
60
|
|
61
|
+
# @return [Machine]
|
33
62
|
def machine
|
34
63
|
@machine ||= Machine.new
|
35
64
|
end
|
data/lib/backport/adapter.rb
CHANGED
@@ -1,27 +1,67 @@
|
|
1
1
|
module Backport
|
2
2
|
class Adapter
|
3
|
-
|
3
|
+
# A hash of information about the client connection. The data can vary
|
4
|
+
# based on the transport, e.g., :hostname and :address for TCP connections
|
5
|
+
# or :filename for file streams.
|
6
|
+
#
|
7
|
+
# @return [Hash{Symbol => String, Integer}]
|
8
|
+
attr_reader :remote
|
9
|
+
|
10
|
+
# @param output [IO]
|
11
|
+
# @param remote [Hash{Symbol => String, Integer}]
|
12
|
+
def initialize output, remote = {}
|
4
13
|
@out = output
|
14
|
+
@remote = remote
|
5
15
|
end
|
6
16
|
|
7
|
-
|
8
|
-
|
9
|
-
|
17
|
+
# A callback triggered when a client connection is opening. Subclasses
|
18
|
+
# and/or modules should override this method to provide their own
|
19
|
+
# functionality.
|
20
|
+
#
|
21
|
+
# @return [void]
|
22
|
+
def opening; end
|
10
23
|
|
11
|
-
|
12
|
-
|
13
|
-
|
24
|
+
# A callback triggered when a client connection is closing. Subclasses
|
25
|
+
# and/or modules should override this method to provide their own
|
26
|
+
# functionality.
|
27
|
+
#
|
28
|
+
# @return [void]
|
29
|
+
def closing; end
|
14
30
|
|
15
|
-
|
16
|
-
|
17
|
-
|
31
|
+
# A callback triggered when the client is sending data to the server.
|
32
|
+
# Subclasses and/or modules should override this method to provide their
|
33
|
+
# own functionality.
|
34
|
+
#
|
35
|
+
# @param data [String]
|
36
|
+
# @return [void]
|
37
|
+
def sending(data); end
|
18
38
|
|
39
|
+
# Send data to the client.
|
40
|
+
#
|
41
|
+
# @param data [String]
|
42
|
+
# @return [void]
|
19
43
|
def write data
|
20
|
-
@out.
|
44
|
+
@out.write data
|
45
|
+
@out.flush
|
21
46
|
end
|
22
47
|
|
48
|
+
# Send a line of data to the client.
|
49
|
+
#
|
50
|
+
# @param data [String]
|
51
|
+
# @return [void]
|
23
52
|
def write_line data
|
24
53
|
@out.puts data
|
54
|
+
@out.flush
|
55
|
+
end
|
56
|
+
|
57
|
+
def closed?
|
58
|
+
@closed ||= false
|
59
|
+
end
|
60
|
+
|
61
|
+
def close
|
62
|
+
return if closed?
|
63
|
+
@closed = true
|
64
|
+
closing
|
25
65
|
end
|
26
66
|
end
|
27
67
|
end
|
data/lib/backport/client.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
module Backport
|
2
2
|
class Client
|
3
|
-
|
3
|
+
# @return [Adapter]
|
4
|
+
attr_reader :adapter
|
5
|
+
|
6
|
+
def initialize input, output, adapter, remote = {}
|
4
7
|
@in = input
|
5
8
|
@out = output
|
6
|
-
|
7
|
-
@adapter = make_adapter(adapter)
|
9
|
+
@adapter = make_adapter(adapter, remote)
|
8
10
|
@stopped = true
|
9
11
|
@buffer = ''
|
10
12
|
end
|
@@ -41,11 +43,11 @@ module Backport
|
|
41
43
|
|
42
44
|
private
|
43
45
|
|
44
|
-
def make_adapter cls_mod
|
45
|
-
if cls_mod.is_a?(Class)
|
46
|
-
@adapter = cls_mod.new(@out)
|
47
|
-
elsif cls_mod.
|
48
|
-
@adapter = Adapter.new(@out)
|
46
|
+
def make_adapter cls_mod, remote
|
47
|
+
if cls_mod.is_a?(Class) && cls_mod <= Backport::Adapter
|
48
|
+
@adapter = cls_mod.new(@out, remote)
|
49
|
+
elsif cls_mod.class == Module
|
50
|
+
@adapter = Adapter.new(@out, remote)
|
49
51
|
@adapter.extend cls_mod
|
50
52
|
else
|
51
53
|
raise TypeError, "#{cls_mod} is not a valid Backport adapter"
|
@@ -59,13 +61,17 @@ module Backport
|
|
59
61
|
def run_input_thread
|
60
62
|
Thread.new do
|
61
63
|
until stopped?
|
62
|
-
|
63
|
-
|
64
|
-
|
64
|
+
@in.flush
|
65
|
+
begin
|
66
|
+
chars = @in.sysread(255)
|
67
|
+
rescue EOFError, Errno::ECONNRESET
|
68
|
+
chars = nil
|
69
|
+
end
|
70
|
+
if chars.nil?
|
65
71
|
stop
|
66
72
|
break
|
67
73
|
end
|
68
|
-
mutex.synchronize { @buffer.concat
|
74
|
+
mutex.synchronize { @buffer.concat chars }
|
69
75
|
end
|
70
76
|
end
|
71
77
|
end
|
data/lib/backport/machine.rb
CHANGED
@@ -4,30 +4,48 @@ module Backport
|
|
4
4
|
@stopped = true
|
5
5
|
end
|
6
6
|
|
7
|
+
# Run the machine. If a block is provided, it gets executed before the
|
8
|
+
# maching starts its main loop. The main loop halts program execution
|
9
|
+
# until the machine is stopped.
|
10
|
+
#
|
11
|
+
# @return [void]
|
7
12
|
def run
|
8
13
|
return unless stopped?
|
14
|
+
servers.clear
|
9
15
|
@stopped = false
|
10
16
|
yield if block_given?
|
11
17
|
run_server_thread
|
12
18
|
end
|
13
19
|
|
20
|
+
# Stop the machine.
|
21
|
+
#
|
22
|
+
# @return [void]
|
14
23
|
def stop
|
15
24
|
servers.map(&:stop)
|
16
25
|
servers.clear
|
17
26
|
@stopped = true
|
18
27
|
end
|
19
28
|
|
29
|
+
# True if the machine is stopped.
|
30
|
+
#
|
20
31
|
def stopped?
|
21
32
|
@stopped ||= false
|
22
33
|
end
|
23
34
|
|
24
|
-
|
35
|
+
# Add a server to the machine. The server will be started when the machine
|
36
|
+
# starts. If the machine is already running, the server will be started
|
37
|
+
# immediately.
|
38
|
+
#
|
39
|
+
# @param server [Server::Base]
|
40
|
+
# @return [void]
|
41
|
+
def prepare server
|
25
42
|
servers.push server
|
43
|
+
server.start unless stopped?
|
26
44
|
end
|
27
45
|
|
28
46
|
private
|
29
47
|
|
30
|
-
# @return [Array<
|
48
|
+
# @return [Array<Server::Base>]
|
31
49
|
def servers
|
32
50
|
@servers ||= []
|
33
51
|
end
|
@@ -35,10 +53,7 @@ module Backport
|
|
35
53
|
def run_server_thread
|
36
54
|
servers.map(&:start)
|
37
55
|
until stopped?
|
38
|
-
servers.each
|
39
|
-
server.tick
|
40
|
-
sleep 0.001
|
41
|
-
end
|
56
|
+
servers.each(&:tick)
|
42
57
|
sleep 0.001
|
43
58
|
end
|
44
59
|
end
|
data/lib/backport/server/base.rb
CHANGED
@@ -1,21 +1,40 @@
|
|
1
1
|
module Backport
|
2
2
|
module Server
|
3
3
|
class Base
|
4
|
-
def
|
5
|
-
@
|
6
|
-
@
|
4
|
+
def started?
|
5
|
+
@started ||= false
|
6
|
+
@started
|
7
7
|
end
|
8
8
|
|
9
9
|
def stop
|
10
10
|
stopping
|
11
|
-
@
|
11
|
+
@started = false
|
12
12
|
end
|
13
13
|
|
14
|
+
# A callback triggered when a Machine starts running or the server is
|
15
|
+
# added to a running machine. Subclasses should override this method to
|
16
|
+
# provide their own functionality.
|
17
|
+
#
|
18
|
+
# @return [void]
|
19
|
+
def starting; end
|
20
|
+
|
21
|
+
# A callback triggered when the server is stopping. Subclasses should
|
22
|
+
# override this method to provide their own functionality.
|
23
|
+
#
|
24
|
+
# @return [void]
|
14
25
|
def stopping; end
|
15
26
|
|
27
|
+
# A callback triggered from the main loop of a running Machine.
|
28
|
+
# Subclasses should override this method to provide their own
|
29
|
+
# functionality.
|
30
|
+
#
|
31
|
+
# @return [void]
|
16
32
|
def tick; end
|
17
33
|
|
18
|
-
def start
|
34
|
+
def start
|
35
|
+
starting
|
36
|
+
@started = true
|
37
|
+
end
|
19
38
|
end
|
20
39
|
end
|
21
40
|
end
|
@@ -8,13 +8,10 @@ module Backport
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def tick
|
11
|
-
|
11
|
+
now = Time.now
|
12
|
+
return unless now - @last_time > @period
|
12
13
|
@block.call
|
13
|
-
@last_time =
|
14
|
-
end
|
15
|
-
|
16
|
-
def stop
|
17
|
-
@stopped = true
|
14
|
+
@last_time = now
|
18
15
|
end
|
19
16
|
end
|
20
17
|
end
|
@@ -8,28 +8,44 @@ module Backport
|
|
8
8
|
def initialize host: 'localhost', port: 1117, adapter: Adapter
|
9
9
|
@socket = TCPServer.new(host, port)
|
10
10
|
@adapter = adapter
|
11
|
-
|
12
|
-
|
13
|
-
def stopped?
|
14
|
-
@stopped ||= false
|
11
|
+
@stopped = false
|
15
12
|
end
|
16
13
|
|
17
14
|
def tick
|
18
15
|
mutex.synchronize do
|
19
16
|
clients.each do |client|
|
17
|
+
if client.adapter.closed?
|
18
|
+
client.stop
|
19
|
+
next
|
20
|
+
end
|
20
21
|
input = client.read
|
21
22
|
client.sending input unless input.nil?
|
22
23
|
end
|
24
|
+
clients.delete_if(&:stopped?)
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
def
|
27
|
-
super
|
28
|
+
def starting
|
28
29
|
start_accept_thread
|
29
30
|
end
|
30
31
|
|
32
|
+
def stopping
|
33
|
+
super
|
34
|
+
begin
|
35
|
+
socket.shutdown
|
36
|
+
rescue Errno::ENOTCONN, IOError
|
37
|
+
# ignore
|
38
|
+
end
|
39
|
+
socket.close
|
40
|
+
end
|
41
|
+
|
42
|
+
def stopped?
|
43
|
+
@stopped
|
44
|
+
end
|
45
|
+
|
31
46
|
private
|
32
47
|
|
48
|
+
# @return [TCPSocket]
|
33
49
|
attr_reader :socket
|
34
50
|
|
35
51
|
def mutex
|
@@ -39,10 +55,23 @@ module Backport
|
|
39
55
|
def start_accept_thread
|
40
56
|
Thread.new do
|
41
57
|
until stopped?
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
58
|
+
begin
|
59
|
+
conn = socket.accept
|
60
|
+
mutex.synchronize do
|
61
|
+
addr = conn.addr(true)
|
62
|
+
data = {
|
63
|
+
family: addr[0],
|
64
|
+
port: addr[1],
|
65
|
+
hostname: addr[2],
|
66
|
+
address: addr[3]
|
67
|
+
}
|
68
|
+
clients.push Client.new(conn, conn, @adapter, data)
|
69
|
+
clients.last.run
|
70
|
+
end
|
71
|
+
rescue Exception => e
|
72
|
+
STDERR.puts "Server stopped with exception [#{e.class}] #{e.message}"
|
73
|
+
stop
|
74
|
+
break
|
46
75
|
end
|
47
76
|
end
|
48
77
|
end
|
data/lib/backport/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.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: 2018-12-
|
11
|
+
date: 2018-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.14'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.14'
|
55
69
|
description:
|
56
70
|
email:
|
57
71
|
- fsnyder@castwide.com
|
@@ -62,6 +76,7 @@ files:
|
|
62
76
|
- ".gitignore"
|
63
77
|
- ".rspec"
|
64
78
|
- ".travis.yml"
|
79
|
+
- CHANGELOG.md
|
65
80
|
- Gemfile
|
66
81
|
- LICENSE.txt
|
67
82
|
- README.md
|