backport 0.3.0 → 1.0.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: 9693d9fb93dc1efb03c85d3abe258cd54e2fa4bca03e8bc2f64a45a56783269a
4
- data.tar.gz: a7abdaa602ccbfda748cdace2af071af73651ecff42663ac237def501260f457
3
+ metadata.gz: ef66ad1688e603cf9a8c9844c5551bbc3ca9187abf747bca2064e46fb7c2600f
4
+ data.tar.gz: 3952f19adbe1639f82c98568d4adc97aa0c690d5fa568fef768efbcf6134ee7c
5
5
  SHA512:
6
- metadata.gz: bbc92f9838a571f7cb9ea71334961cf34013a41e1010147ec4f54d4c5efde68c37b4f7cb9f0d9d52ad8ebe23ac9fa83e24568d690044ed200d838a1cb2c50358
7
- data.tar.gz: a5cdf05eb09d40bc352fd07ed2d23b85107875551c9718e0145e7abdf92f670ff784e99b50b8daf5879c7a0cbe92dca1d8ca4b3a42ed73a482610085fd56e986
6
+ metadata.gz: 334cb82482d72e1b617fa9fa2a1a1b87764b800e4a8abf6ecc2c63fc012406fb05bf744b362aa6f3211aae0f868ab188902132825602ee3909b54358e2cd1813
7
+ data.tar.gz: 2ff55f4495545b50e4e226f961d38b5471cc8d1890a4339091f126139f61422e1ae3af1e1a8c3fc80aafb915b187da099ef312a5e7da1ad4a500f737e9a6909c
@@ -2,3 +2,5 @@ Style/MethodDefParentheses:
2
2
  Enabled: false
3
3
  Layout/EmptyLineAfterGuardClause:
4
4
  Enabled: false
5
+ Style/StringLiterals:
6
+ Enabled: false
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  sudo: false
3
3
  language: ruby
4
- cache: bundler
5
4
  rvm:
6
5
  - 2.1
7
6
  - 2.2
@@ -9,6 +8,7 @@ rvm:
9
8
  - 2.4
10
9
  - 2.5.1
11
10
  - 2.5.3
11
+ - 2.6
12
12
  - jruby-9.0.5.0
13
13
  - jruby-9.1.16.0
14
14
  matrix:
@@ -1,3 +1,7 @@
1
+ ## 1.0.0 - February 19, 2019
2
+ - Renamed Adapter#sending to Adapter#receiving
3
+ - Travis tests up to Ruby 2.6
4
+
1
5
  ## 0.3.0 - January 10, 2019
2
6
  - Basic logging
3
7
  - Differentiate between "expected" and "unexpected" exceptions in Tcpip
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 sending 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
- * `sending(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
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
@@ -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.add_development_dependency "bundler", "~> 1.17"
24
- spec.add_development_dependency "rake", "~> 10.0"
25
- spec.add_development_dependency "rspec", "~> 3.0"
26
- spec.add_development_dependency "simplecov", "~> 0.14"
27
- end
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
@@ -1,4 +1,4 @@
1
- require "backport/version"
1
+ require 'backport/version'
2
2
  require 'logger'
3
3
 
4
4
  # An event-driven IO library.
@@ -40,13 +40,13 @@ module Backport
40
40
  # @return [void]
41
41
  def closing; end
42
42
 
43
- # A callback triggered when the client sends data to the server. Subclasses
44
- # and/or modules should override this method to provide their own
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 sending(data); end
49
+ def receiving(data); end
50
50
 
51
51
  # Send data to the client.
52
52
  #
@@ -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
- # Notify the adapter that the client is sending data.
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
- # @param data [String]
47
- def sending data
48
- @adapter.sending data
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
- private
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
- @in.flush
84
- begin
85
- chars = @in.sysread(255)
86
- rescue EOFError, Errno::ECONNRESET
87
- chars = nil
88
- end
89
- if chars.nil?
90
- stop
91
- break
92
- end
93
- mutex.synchronize { @buffer.concat chars }
94
- end
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
@@ -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
- clients.each do |client|
10
- input = client.read
11
- client.sending input unless input.nil?
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 => e
41
- Backport.logger.info "Minor exception while stopping server [#{e.class}] #{e.message}"
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 => e
52
+ rescue IO::WaitReadable, Errno::EAGAIN
67
53
  # ignore
68
- rescue Errno::ENOTSOCK, IOError => e
69
- Backport.logger.info "Server stopped with minor exception [#{e.class}] #{e.message}"
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 => e
72
- Backport.logger.warn "Server stopped with major exception [#{e.class}] #{e.message}"
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?
@@ -1,3 +1,3 @@
1
1
  module Backport
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
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.3.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-01-10 00:00:00.000000000 Z
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: '0'
97
+ version: '2.1'
112
98
  required_rubygems_version: !ruby/object:Gem::Requirement
113
99
  requirements:
114
100
  - - ">="