tcp-server 1.2.0-java → 1.4.1-java
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 +28 -28
- data/Rakefile +12 -2
- data/exe/tcp_server +3 -3
- data/lib/{server/argument_parser.rb → tcp_server/arguments_parser.rb} +9 -9
- data/lib/{server/channel_initializer.rb → tcp_server/channelizer.rb} +29 -25
- data/lib/{client.rb → tcp_server/client.rb} +56 -48
- data/lib/{server → tcp_server}/config.rb +8 -4
- data/lib/{server → tcp_server}/default_handler.rb +7 -6
- data/lib/{demo_listener.rb → tcp_server/demo_listener.rb} +5 -5
- data/lib/{server → tcp_server}/instance_methods.rb +70 -31
- data/lib/{server → tcp_server}/listenable.rb +4 -4
- data/lib/tcp_server/logging.rb +310 -0
- data/lib/{server → tcp_server}/message_handler.rb +8 -8
- data/lib/{server → tcp_server}/modular_handler.rb +33 -28
- data/lib/tcp_server/server.rb +58 -0
- data/lib/tcp_server/version.rb +16 -0
- data/lib/tcp_server.rb +3 -2
- metadata +38 -33
- data/lib/logging.rb +0 -253
- data/lib/server/server.rb +0 -39
- data/lib/server/shutdown_hook.rb +0 -35
- data/lib/server/version.rb +0 -16
- data/lib/server.rb +0 -36
- data/lib/tcp-server.rb +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 18fc53639397d26d2a0b3755610853697649175288c8288ae0ee7c0b8205eac6
|
|
4
|
+
data.tar.gz: af583ff89a4eaf27a809a1469df1c5950a470584f588a9e7c83dd51a38ba4147
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c4fb9fbb2b7edf7fecb14dca462e9f832e6dedd998e0789a4a28f71bf7845caf2ea2721c6d5cddec8d6e2fe0ab636602311b3ce7ff134a6c3b886eace2818760
|
|
7
|
+
data.tar.gz: f466bc6d5c711d5853adb46805ea9c895a85e2ccbd7b5f5f8006de4a3938e111a5756d690c50db01b2029a710f204f48eb70f595777283746262d0b24d7e7f5d
|
data/README.md
CHANGED
|
@@ -18,9 +18,10 @@ You may run the websocket server in a container. Using `[colima]` for a containe
|
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
20
|
colima start
|
|
21
|
-
docker
|
|
21
|
+
docker build --tag tcp-server-jruby .
|
|
22
|
+
docker compose up --detach
|
|
22
23
|
nc localhost 4000
|
|
23
|
-
docker
|
|
24
|
+
docker compose down
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
Building the image or running the container:
|
|
@@ -45,7 +46,6 @@ See: https://mise.jdx.dev/getting-started.html
|
|
|
45
46
|
```sh
|
|
46
47
|
curl https://mise.jdx.dev/install.sh | sh
|
|
47
48
|
~/.local/bin/mise --version
|
|
48
|
-
mise 2024.x.x
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Enable mise activation in future zsh sessions.
|
|
@@ -75,10 +75,10 @@ bundle install
|
|
|
75
75
|
|
|
76
76
|
## Run
|
|
77
77
|
|
|
78
|
-
The entrypoint for the
|
|
78
|
+
The entrypoint for the TCP server may now be invoked from a command line interface terminal shell.
|
|
79
79
|
|
|
80
80
|
```sh
|
|
81
|
-
bundle exec
|
|
81
|
+
bundle exec exe/tcp_server
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
|
|
@@ -96,7 +96,7 @@ bundle exec rake
|
|
|
96
96
|
To publish the gem after first verifying that the built gem works, execute:
|
|
97
97
|
|
|
98
98
|
```sh
|
|
99
|
-
version=$(ruby -r lib/
|
|
99
|
+
version=$(ruby -r lib/tcp_server/version -I . -e 'puts TcpServer::VERSION')
|
|
100
100
|
git tag --annotate --message "Release ${version}" "${version}-release"
|
|
101
101
|
git push origin --tags
|
|
102
102
|
```
|
|
@@ -105,7 +105,7 @@ git push origin --tags
|
|
|
105
105
|
Or manually, if necessary:
|
|
106
106
|
|
|
107
107
|
```sh
|
|
108
|
-
bundle exec rake verify
|
|
108
|
+
bundle exec rake clean clobber test package verify
|
|
109
109
|
bundle exec rake publish
|
|
110
110
|
```
|
|
111
111
|
|
|
@@ -124,47 +124,47 @@ bundle exec rake clean clobber
|
|
|
124
124
|
Here is a bird's-eye view of the project layout.
|
|
125
125
|
|
|
126
126
|
```sh
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
$ date; tree --gitignore
|
|
128
|
+
Sun Feb 8 09:29:34 CST 2026
|
|
129
129
|
.
|
|
130
|
-
├── Dockerfile
|
|
131
|
-
├── Gemfile
|
|
132
|
-
├── Gemfile.lock
|
|
133
|
-
├── LICENSE
|
|
134
|
-
├── README.md
|
|
135
|
-
├── Rakefile
|
|
136
130
|
├── docker-compose.yaml
|
|
131
|
+
├── Dockerfile
|
|
137
132
|
├── exe
|
|
138
133
|
│ └── tcp_server
|
|
134
|
+
├── Gemfile
|
|
139
135
|
├── lib
|
|
140
|
-
│ ├──
|
|
141
|
-
│ ├──
|
|
142
|
-
│ ├──
|
|
143
|
-
│ ├──
|
|
144
|
-
│ │ ├── argument_parser.rb
|
|
145
|
-
│ │ ├── channel_initializer.rb
|
|
136
|
+
│ ├── tcp_server
|
|
137
|
+
│ │ ├── arguments_parser.rb
|
|
138
|
+
│ │ ├── channelizer.rb
|
|
139
|
+
│ │ ├── client.rb
|
|
146
140
|
│ │ ├── config.rb
|
|
141
|
+
│ │ ├── default_handler.rb
|
|
142
|
+
│ │ ├── demo_listener.rb
|
|
147
143
|
│ │ ├── instance_methods.rb
|
|
148
144
|
│ │ ├── listenable.rb
|
|
145
|
+
│ │ ├── logging.rb
|
|
149
146
|
│ │ ├── message_handler.rb
|
|
150
147
|
│ │ ├── modular_handler.rb
|
|
151
148
|
│ │ ├── server.rb
|
|
152
|
-
│ │ ├── shutdown_hook.rb
|
|
153
149
|
│ │ └── version.rb
|
|
154
|
-
│ ├── server.rb
|
|
155
|
-
│ ├── tcp-server.rb
|
|
156
150
|
│ └── tcp_server.rb
|
|
151
|
+
├── LICENSE
|
|
152
|
+
├── Rakefile
|
|
153
|
+
├── README.md
|
|
157
154
|
├── spec
|
|
155
|
+
│ ├── server_arguments_parser_spec.rb
|
|
156
|
+
│ ├── server_default_handler_spec.rb
|
|
157
|
+
│ ├── server_listenable_spec.rb
|
|
158
|
+
│ ├── server_message_handler_spec.rb
|
|
158
159
|
│ ├── spec_helper.rb
|
|
159
160
|
│ ├── test_spec.rb
|
|
160
161
|
│ └── verify
|
|
161
162
|
│ └── verify_spec.rb
|
|
162
|
-
├── tcp-server-1.0.8-java.gem
|
|
163
|
-
├── tcp-server-jruby.gemspec
|
|
164
163
|
├── tcp_server.png
|
|
165
|
-
|
|
164
|
+
├── tcp_server.rb
|
|
165
|
+
└── tcp-server-jruby.gemspec
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
6 directories, 31 files
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
|
data/Rakefile
CHANGED
|
@@ -23,7 +23,8 @@ end
|
|
|
23
23
|
|
|
24
24
|
desc 'Run the spec tests'
|
|
25
25
|
task :test do
|
|
26
|
-
system('bundle', 'exec', 'rspec', '--exclude-pattern', 'spec/verify/**/*_spec.rb') or abort
|
|
26
|
+
# system('bundle', 'exec', 'rspec', '--exclude-pattern', 'spec/verify/**/*_spec.rb') or abort
|
|
27
|
+
system('bundle', 'exec', 'rspec') or abort
|
|
27
28
|
end
|
|
28
29
|
task test: :lint
|
|
29
30
|
|
|
@@ -41,7 +42,16 @@ task package: %i[clean clobber test]
|
|
|
41
42
|
|
|
42
43
|
desc 'Verify the gem'
|
|
43
44
|
task :verify do
|
|
44
|
-
|
|
45
|
+
env = {
|
|
46
|
+
'GEM_HOME' => 'tmp',
|
|
47
|
+
'GEM_PATH' => 'tmp',
|
|
48
|
+
'BUNDLE_GEMFILE' => nil,
|
|
49
|
+
'RUBYOPT' => nil
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# cmd = ['jruby', '-S', 'rspec', 'spec/verify']
|
|
53
|
+
cmd = ['bundle', 'exec', 'rspec', 'spec/verify']
|
|
54
|
+
system(env, *cmd) or abort
|
|
45
55
|
end
|
|
46
56
|
task verify: :explode
|
|
47
57
|
|
data/exe/tcp_server
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
# =begin
|
|
10
10
|
|
|
11
|
-
# Copyright Nels Nelson 2016-
|
|
11
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
12
12
|
|
|
13
13
|
# =end
|
|
14
14
|
|
|
15
|
-
require_relative '../lib/
|
|
15
|
+
require_relative '../lib/tcp_server'
|
|
16
16
|
|
|
17
|
-
Object.new.extend(
|
|
17
|
+
Object.new.extend(TcpServer).main
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
# =begin
|
|
8
8
|
#
|
|
9
|
-
# Copyright Nels Nelson 2016-
|
|
9
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
10
10
|
#
|
|
11
11
|
# =end
|
|
12
12
|
|
|
@@ -14,15 +14,15 @@ require 'optparse'
|
|
|
14
14
|
|
|
15
15
|
require_relative 'config'
|
|
16
16
|
|
|
17
|
-
# The
|
|
18
|
-
module
|
|
17
|
+
# The TcpServer module
|
|
18
|
+
module TcpServer
|
|
19
19
|
# The ArgumentsParser class
|
|
20
20
|
class ArgumentsParser
|
|
21
21
|
attr_reader :parser, :options
|
|
22
22
|
|
|
23
23
|
def initialize(option_parser = OptionParser.new)
|
|
24
24
|
@parser = option_parser
|
|
25
|
-
@options =
|
|
25
|
+
@options = TcpServer.server_config.dup
|
|
26
26
|
@flags = %i[banner port ssl idle_reading idle_writing log_requests log_level help version]
|
|
27
27
|
@flags.each { |method_name| method(method_name)&.call if respond_to?(method_name) }
|
|
28
28
|
end
|
|
@@ -34,7 +34,7 @@ module Server
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def validated_port(value, integer_pattern = /^\d+$/)
|
|
37
|
-
raise OptionParser::InvalidArgument, "Invalid port: #{value}" unless
|
|
37
|
+
raise OptionParser::InvalidArgument, "Invalid port: #{value}" unless
|
|
38
38
|
integer_pattern.match?(value.to_s) && value.positive? && value < 65_536
|
|
39
39
|
|
|
40
40
|
value
|
|
@@ -72,8 +72,8 @@ module Server
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def log_level
|
|
75
|
+
current_level = @options.fetch(:log_level, 0)
|
|
75
76
|
@parser.on_tail('-v', '--verbose', 'Increase verbosity') do
|
|
76
|
-
current_level = @options.fetch(:log_level, 0)
|
|
77
77
|
@options[:log_level] = current_level - 1
|
|
78
78
|
end
|
|
79
79
|
end
|
|
@@ -87,14 +87,14 @@ module Server
|
|
|
87
87
|
|
|
88
88
|
def version
|
|
89
89
|
@parser.on_tail('--version', 'Show version') do
|
|
90
|
-
puts "#{File.basename($PROGRAM_NAME)} version #{::
|
|
90
|
+
puts "#{File.basename($PROGRAM_NAME)} version #{TcpServer::VERSION}"
|
|
91
91
|
exit
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
# class ArgumentsParser
|
|
96
96
|
|
|
97
|
-
def parse_arguments(arguments_parser = ::
|
|
97
|
+
def parse_arguments(arguments_parser = TcpServer::ArgumentsParser.new)
|
|
98
98
|
arguments_parser.parser.parse!(ARGV)
|
|
99
99
|
arguments_parser.options
|
|
100
100
|
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption,
|
|
@@ -102,4 +102,4 @@ module Server
|
|
|
102
102
|
abort e.message
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
|
-
# module
|
|
105
|
+
# module TcpServer
|
|
@@ -6,30 +6,30 @@
|
|
|
6
6
|
|
|
7
7
|
# =begin
|
|
8
8
|
#
|
|
9
|
-
# Copyright Nels Nelson 2016-
|
|
9
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
10
10
|
#
|
|
11
11
|
# =end
|
|
12
12
|
|
|
13
13
|
require 'java'
|
|
14
14
|
require 'netty'
|
|
15
15
|
|
|
16
|
+
java_import 'io.netty.channel.ChannelInitializer'
|
|
17
|
+
java_import 'io.netty.handler.codec.DelimiterBasedFrameDecoder'
|
|
18
|
+
java_import 'io.netty.handler.codec.string.StringDecoder'
|
|
19
|
+
java_import 'io.netty.handler.codec.string.StringEncoder'
|
|
20
|
+
java_import 'io.netty.handler.ssl.SslContextBuilder'
|
|
21
|
+
java_import 'io.netty.handler.ssl.SslHandler'
|
|
22
|
+
java_import 'io.netty.handler.ssl.util.InsecureTrustManagerFactory'
|
|
23
|
+
java_import 'io.netty.handler.ssl.util.SelfSignedCertificate'
|
|
24
|
+
java_import 'io.netty.util.concurrent.FutureListener'
|
|
25
|
+
|
|
16
26
|
require_relative 'message_handler'
|
|
17
27
|
require_relative 'modular_handler'
|
|
18
28
|
|
|
19
|
-
# The
|
|
20
|
-
module
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
java_import Java::io.netty.handler.codec.string.StringDecoder
|
|
24
|
-
java_import Java::io.netty.handler.codec.string.StringEncoder
|
|
25
|
-
java_import Java::io.netty.handler.ssl.SslContextBuilder
|
|
26
|
-
java_import Java::io.netty.handler.ssl.SslHandler
|
|
27
|
-
java_import Java::io.netty.handler.ssl.util.InsecureTrustManagerFactory
|
|
28
|
-
java_import Java::io.netty.handler.ssl.util.SelfSignedCertificate
|
|
29
|
-
java_import Java::io.netty.util.concurrent.FutureListener
|
|
30
|
-
|
|
31
|
-
# The ChannelInitializer class
|
|
32
|
-
class ChannelInitializer < Java::io.netty.channel.ChannelInitializer
|
|
29
|
+
# The TcpServer module
|
|
30
|
+
module TcpServer
|
|
31
|
+
# The Channelizer class
|
|
32
|
+
class Channelizer < ChannelInitializer
|
|
33
33
|
attr_accessor :decoder, :encoder, :user_handlers
|
|
34
34
|
attr_reader :options
|
|
35
35
|
|
|
@@ -55,11 +55,12 @@ module Server
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def frame_decoder
|
|
58
|
-
DelimiterBasedFrameDecoder.new(
|
|
58
|
+
DelimiterBasedFrameDecoder.new(
|
|
59
|
+
@options[:max_frame_length], @options[:delimiter])
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def default_handler
|
|
62
|
-
@default_handler ||= ::
|
|
63
|
+
@default_handler ||= TcpServer::ModularHandler.new(@channel_group)
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
def add_listener(*listener)
|
|
@@ -76,7 +77,7 @@ module Server
|
|
|
76
77
|
@user_handlers.each do |handler|
|
|
77
78
|
case handler
|
|
78
79
|
when Class then pipeline.addLast(handler.new)
|
|
79
|
-
when Proc then pipeline.addLast(::
|
|
80
|
+
when Proc then pipeline.addLast(TcpServer::MessageHandler.new(&handler))
|
|
80
81
|
else pipeline.addLast(handler)
|
|
81
82
|
end
|
|
82
83
|
end
|
|
@@ -89,8 +90,10 @@ module Server
|
|
|
89
90
|
log.debug 'Initializing SSL context'
|
|
90
91
|
require 'bouncycastle'
|
|
91
92
|
ssc = SelfSignedCertificate.new
|
|
92
|
-
builder = SslContextBuilder.forServer(
|
|
93
|
-
|
|
93
|
+
builder = SslContextBuilder.forServer(
|
|
94
|
+
ssc.certificate(), ssc.privateKey())
|
|
95
|
+
@ssl_ctx = builder.trustManager(
|
|
96
|
+
InsecureTrustManagerFactory::INSTANCE).build()
|
|
94
97
|
end
|
|
95
98
|
|
|
96
99
|
def ssl_handler_instance(channel, ssl_ctx = ssl_context)
|
|
@@ -106,13 +109,14 @@ module Server
|
|
|
106
109
|
handler
|
|
107
110
|
end
|
|
108
111
|
|
|
109
|
-
def ssl_handshake_future_listener(
|
|
112
|
+
def ssl_handshake_future_listener(
|
|
113
|
+
listener = Object.new.extend(FutureListener))
|
|
110
114
|
listener.define_singleton_method(:operationComplete) do |future|
|
|
111
115
|
raise future.cause unless future.success?
|
|
112
116
|
session = future.now.pipeline.get('SslHandler#0')&.engine&.session
|
|
113
|
-
|
|
117
|
+
TcpServer.log.info "Channel protocol: #{session.protocol}, cipher suite: #{session.cipher_suite}"
|
|
114
118
|
rescue StandardError => e
|
|
115
|
-
|
|
119
|
+
TcpServer.log.warn "Error handling operation complete event: #{e.message}"
|
|
116
120
|
end
|
|
117
121
|
listener
|
|
118
122
|
end
|
|
@@ -124,6 +128,6 @@ module Server
|
|
|
124
128
|
ssl_engine
|
|
125
129
|
end
|
|
126
130
|
end
|
|
127
|
-
# class
|
|
131
|
+
# class Channelizer
|
|
128
132
|
end
|
|
129
|
-
# module
|
|
133
|
+
# module TcpServer
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
# =begin
|
|
10
10
|
#
|
|
11
|
-
# Copyright Nels Nelson 2016-
|
|
11
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
12
12
|
#
|
|
13
13
|
# =end
|
|
14
14
|
|
|
@@ -19,6 +19,23 @@ require 'netty'
|
|
|
19
19
|
|
|
20
20
|
require_relative 'logging'
|
|
21
21
|
|
|
22
|
+
java_import 'io.netty.bootstrap.Bootstrap'
|
|
23
|
+
java_import 'io.netty.channel.AbstractChannel'
|
|
24
|
+
java_import 'io.netty.channel.ChannelInitializer'
|
|
25
|
+
java_import 'io.netty.channel.ChannelOption'
|
|
26
|
+
java_import 'io.netty.channel.SimpleChannelInboundHandler'
|
|
27
|
+
java_import 'io.netty.channel.nio.NioEventLoopGroup'
|
|
28
|
+
java_import 'io.netty.channel.socket.nio.NioSocketChannel'
|
|
29
|
+
java_import 'io.netty.handler.codec.Delimiters'
|
|
30
|
+
java_import 'io.netty.handler.codec.DelimiterBasedFrameDecoder'
|
|
31
|
+
java_import 'io.netty.handler.codec.string.StringDecoder'
|
|
32
|
+
java_import 'io.netty.handler.codec.string.StringEncoder'
|
|
33
|
+
java_import 'io.netty.handler.ssl.SslContextBuilder'
|
|
34
|
+
java_import 'io.netty.handler.ssl.SslHandler'
|
|
35
|
+
java_import 'io.netty.handler.ssl.util.InsecureTrustManagerFactory'
|
|
36
|
+
java_import 'java.util.concurrent.CopyOnWriteArrayList'
|
|
37
|
+
java_import 'java.util.concurrent.LinkedBlockingQueue'
|
|
38
|
+
|
|
22
39
|
# The Client module
|
|
23
40
|
module Client
|
|
24
41
|
def client_config
|
|
@@ -29,7 +46,7 @@ module Client
|
|
|
29
46
|
log_level: Logger::INFO,
|
|
30
47
|
quit_commands: %i[bye cease desist exit leave quit stop terminate],
|
|
31
48
|
max_frame_length: 8192,
|
|
32
|
-
delimiter:
|
|
49
|
+
delimiter: Delimiters.lineDelimiter
|
|
33
50
|
}.freeze
|
|
34
51
|
end
|
|
35
52
|
module_function :client_config
|
|
@@ -37,19 +54,13 @@ end
|
|
|
37
54
|
|
|
38
55
|
# The Client module
|
|
39
56
|
module Client
|
|
40
|
-
java_import Java::io.netty.bootstrap.Bootstrap
|
|
41
|
-
java_import Java::io.netty.channel.ChannelOption
|
|
42
|
-
java_import Java::io.netty.channel.nio.NioEventLoopGroup
|
|
43
|
-
java_import Java::io.netty.handler.ssl.SslContextBuilder
|
|
44
|
-
java_import Java::io.netty.handler.ssl.util.InsecureTrustManagerFactory
|
|
45
|
-
|
|
46
57
|
# The InitializationMethods module
|
|
47
58
|
module InitializationMethods
|
|
48
59
|
def init(options)
|
|
49
60
|
@options = options
|
|
50
61
|
@host = @options[:host]
|
|
51
62
|
@port = @options[:port]
|
|
52
|
-
@queue =
|
|
63
|
+
@queue = LinkedBlockingQueue.new
|
|
53
64
|
end
|
|
54
65
|
|
|
55
66
|
def bootstrap
|
|
@@ -58,15 +69,15 @@ module Client
|
|
|
58
69
|
@bootstrap.channel(::TCP::CHANNEL_TYPE)
|
|
59
70
|
@bootstrap.option(ChannelOption::TCP_NODELAY, true)
|
|
60
71
|
@bootstrap.handler(logging_handler) if @options[:log_requests]
|
|
61
|
-
@bootstrap.handler(
|
|
72
|
+
@bootstrap.handler(channelizer)
|
|
62
73
|
end
|
|
63
74
|
|
|
64
75
|
def client_group
|
|
65
76
|
@client_group ||= NioEventLoopGroup.new
|
|
66
77
|
end
|
|
67
78
|
|
|
68
|
-
def
|
|
69
|
-
@
|
|
79
|
+
def channelizer
|
|
80
|
+
@channelizer ||= ::Client::Channelizer.new(@options)
|
|
70
81
|
end
|
|
71
82
|
|
|
72
83
|
def logging_handler
|
|
@@ -74,8 +85,8 @@ module Client
|
|
|
74
85
|
end
|
|
75
86
|
|
|
76
87
|
def configure_handlers(*handlers, &block)
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
channelizer.default_handler.add_listener(self)
|
|
89
|
+
channelizer.default_handler.listeners.addAll(handlers)
|
|
79
90
|
@user_app = block
|
|
80
91
|
@application_handler = lambda do |ctx, msg|
|
|
81
92
|
if @user_app.nil? || @user_app.arity == 1
|
|
@@ -92,13 +103,11 @@ end
|
|
|
92
103
|
|
|
93
104
|
# The Client module
|
|
94
105
|
module Client
|
|
95
|
-
java_import Java::io.netty.channel.AbstractChannel
|
|
96
|
-
|
|
97
106
|
# The InstanceMethods module
|
|
98
107
|
module InstanceMethods
|
|
99
108
|
def puts(msg)
|
|
100
109
|
wait_until_channel_is_active
|
|
101
|
-
msg.chomp
|
|
110
|
+
msg = msg.chomp
|
|
102
111
|
log.trace "#puts msg: #{msg.inspect}"
|
|
103
112
|
raise 'Message is empty!' if msg.nil? || msg.empty?
|
|
104
113
|
@last_write_future = @channel.writeAndFlush("#{msg}\n")
|
|
@@ -221,13 +230,11 @@ module Client
|
|
|
221
230
|
# The Listenable module
|
|
222
231
|
module Listenable
|
|
223
232
|
def listeners
|
|
224
|
-
@listeners ||=
|
|
233
|
+
@listeners ||= CopyOnWriteArrayList.new
|
|
225
234
|
end
|
|
226
235
|
|
|
227
236
|
def add_listener(*listener)
|
|
228
237
|
listeners.addAll(listener)
|
|
229
|
-
ensure
|
|
230
|
-
log.trace "Listeners: #{listeners}"
|
|
231
238
|
end
|
|
232
239
|
|
|
233
240
|
def remove_listener(*listener)
|
|
@@ -237,6 +244,8 @@ module Client
|
|
|
237
244
|
def replace_listeners(*listener)
|
|
238
245
|
listeners.clear
|
|
239
246
|
add_listener(*listener)
|
|
247
|
+
ensure
|
|
248
|
+
log.trace "##{__method__} listeners: #{listeners}"
|
|
240
249
|
end
|
|
241
250
|
|
|
242
251
|
def notify(event, *args)
|
|
@@ -253,40 +262,40 @@ end
|
|
|
253
262
|
|
|
254
263
|
# The Client module
|
|
255
264
|
module Client
|
|
256
|
-
# java_import Java::io.netty.channel.ChannelInboundHandlerAdapter
|
|
257
|
-
java_import Java::io.netty.channel.SimpleChannelInboundHandler
|
|
258
|
-
|
|
259
265
|
# The ModularHandler class
|
|
260
|
-
# class ModularHandler < ChannelInboundHandlerAdapter
|
|
261
266
|
class ModularHandler < SimpleChannelInboundHandler
|
|
262
267
|
include ::Client::Listenable
|
|
263
268
|
|
|
269
|
+
# Overrides the Java::IoNettyChannel::SimpleChannelInboundHandler#isSharable
|
|
270
|
+
# method
|
|
271
|
+
# rubocop: disable Naming/PredicateMethod
|
|
264
272
|
def isSharable
|
|
265
273
|
true
|
|
266
274
|
end
|
|
275
|
+
# rubocop: enable Naming/PredicateMethod
|
|
267
276
|
|
|
268
277
|
def channelRegistered(ctx)
|
|
269
278
|
log.trace "##{__method__} channel: #{ctx.channel}"
|
|
270
279
|
notify :channel_registered, ctx
|
|
271
|
-
super
|
|
280
|
+
super
|
|
272
281
|
end
|
|
273
282
|
|
|
274
283
|
def channelUnregistered(ctx)
|
|
275
284
|
log.trace "##{__method__} channel: #{ctx.channel}"
|
|
276
285
|
notify :channel_unregistered, ctx
|
|
277
|
-
super
|
|
286
|
+
super
|
|
278
287
|
end
|
|
279
288
|
|
|
280
289
|
def channelActive(ctx)
|
|
281
290
|
::Client.log.debug "Channel active #{ctx.channel}"
|
|
282
291
|
notify :channel_active, ctx
|
|
283
|
-
super
|
|
292
|
+
super
|
|
284
293
|
end
|
|
285
294
|
|
|
286
295
|
def channelInactive(ctx)
|
|
287
296
|
log.trace "##{__method__} channel: #{ctx.channel}"
|
|
288
297
|
notify :channel_inactive, ctx
|
|
289
|
-
super
|
|
298
|
+
super
|
|
290
299
|
end
|
|
291
300
|
|
|
292
301
|
def messageReceived(ctx, msg)
|
|
@@ -297,7 +306,7 @@ module Client
|
|
|
297
306
|
def channelRead(ctx, msg)
|
|
298
307
|
log.trace "##{__method__} channel: #{ctx.channel}, message: #{msg.inspect}"
|
|
299
308
|
notify :channel_read, ctx, msg
|
|
300
|
-
super
|
|
309
|
+
super
|
|
301
310
|
end
|
|
302
311
|
|
|
303
312
|
# Please keep in mind that this method will be renamed to
|
|
@@ -312,25 +321,25 @@ module Client
|
|
|
312
321
|
def channelReadComplete(ctx)
|
|
313
322
|
log.trace "##{__method__} channel: #{ctx.channel}"
|
|
314
323
|
notify :channel_read_complete, ctx
|
|
315
|
-
super
|
|
324
|
+
super
|
|
316
325
|
end
|
|
317
326
|
|
|
318
327
|
def channelWritabilityChanged(ctx)
|
|
319
328
|
log.trace "##{__method__} channel: #{ctx.channel}"
|
|
320
329
|
notify :channel_writability_changed, ctx
|
|
321
|
-
super
|
|
330
|
+
super
|
|
322
331
|
end
|
|
323
332
|
|
|
324
333
|
def userEventTriggered(ctx, evt)
|
|
325
334
|
log.trace "##{__method__} channel: #{ctx.channel}, event: #{evt}"
|
|
326
335
|
notify :user_event_triggered, ctx, evt
|
|
327
|
-
super
|
|
336
|
+
super
|
|
328
337
|
end
|
|
329
338
|
|
|
330
339
|
def exceptionCaught(ctx, cause)
|
|
331
340
|
::Client.log.warn "##{__method__} channel: #{ctx.channel}, cause: #{cause.message}"
|
|
332
341
|
listeners = notify :exception_caught, ctx, cause
|
|
333
|
-
super
|
|
342
|
+
super if listeners.empty?
|
|
334
343
|
end
|
|
335
344
|
|
|
336
345
|
IdentiferTemplate = '#<%<class>s:0x%<id>s>'.freeze
|
|
@@ -346,13 +355,8 @@ end
|
|
|
346
355
|
|
|
347
356
|
# The Client module
|
|
348
357
|
module Client
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
java_import Java::io.netty.handler.codec.string.StringDecoder
|
|
352
|
-
java_import Java::io.netty.handler.codec.string.StringEncoder
|
|
353
|
-
|
|
354
|
-
# The ChannelInitializer class
|
|
355
|
-
class ChannelInitializer < Java::io.netty.channel.ChannelInitializer
|
|
358
|
+
# The Channelizer class
|
|
359
|
+
class Channelizer < ChannelInitializer
|
|
356
360
|
attr_accessor :decoder, :encoder, :handlers
|
|
357
361
|
|
|
358
362
|
def initialize(options = {})
|
|
@@ -380,7 +384,8 @@ module Client
|
|
|
380
384
|
end
|
|
381
385
|
|
|
382
386
|
def frame_decoder
|
|
383
|
-
DelimiterBasedFrameDecoder.new(
|
|
387
|
+
DelimiterBasedFrameDecoder.new(
|
|
388
|
+
@options[:max_frame_length], @options[:delimiter])
|
|
384
389
|
end
|
|
385
390
|
|
|
386
391
|
def default_handler
|
|
@@ -405,7 +410,8 @@ module Client
|
|
|
405
410
|
return @ssl_ctx unless @ssl_ctx.nil?
|
|
406
411
|
log.debug 'Initializing SSL context'
|
|
407
412
|
builder = SslContextBuilder.forClient()
|
|
408
|
-
@ssl_ctx = builder.trustManager(
|
|
413
|
+
@ssl_ctx = builder.trustManager(
|
|
414
|
+
InsecureTrustManagerFactory::INSTANCE).build()
|
|
409
415
|
end
|
|
410
416
|
|
|
411
417
|
def ssl_handler(channel, ssl_ctx = ssl_context)
|
|
@@ -422,13 +428,13 @@ module Client
|
|
|
422
428
|
ssl_engine
|
|
423
429
|
end
|
|
424
430
|
end
|
|
425
|
-
# class
|
|
431
|
+
# class Channelizer
|
|
426
432
|
end
|
|
427
433
|
# module Client
|
|
428
434
|
|
|
429
435
|
# The TCP module
|
|
430
436
|
module TCP
|
|
431
|
-
CHANNEL_TYPE =
|
|
437
|
+
CHANNEL_TYPE = NioSocketChannel.java_class
|
|
432
438
|
|
|
433
439
|
# The Client class
|
|
434
440
|
class Client
|
|
@@ -439,6 +445,8 @@ module TCP
|
|
|
439
445
|
def initialize(options = {}, *handlers, &block)
|
|
440
446
|
init(::Client.client_config.merge(options))
|
|
441
447
|
configure_handlers(*handlers, &block)
|
|
448
|
+
listeners = channelizer.default_handler.listeners
|
|
449
|
+
log.trace "##{__method__} listeners: #{listeners}"
|
|
442
450
|
connect
|
|
443
451
|
session
|
|
444
452
|
end
|
|
@@ -480,7 +488,7 @@ module Client
|
|
|
480
488
|
end
|
|
481
489
|
|
|
482
490
|
def validated_port(val, integer_pattern = /^\d+$/)
|
|
483
|
-
raise OptionParser::InvalidArgument, "Invalid port: #{val}" unless
|
|
491
|
+
raise OptionParser::InvalidArgument, "Invalid port: #{val}" unless
|
|
484
492
|
integer_pattern.match?(val.to_s) && val.positive? && val < 65_536
|
|
485
493
|
|
|
486
494
|
val
|
|
@@ -500,9 +508,9 @@ module Client
|
|
|
500
508
|
end
|
|
501
509
|
|
|
502
510
|
def log_level
|
|
511
|
+
current_level = @options.fetch(:log_level, 0)
|
|
503
512
|
@parser.on_tail('-v', '--verbose', 'Increase verbosity') do
|
|
504
|
-
@options[:log_level]
|
|
505
|
-
@options[:log_level] -= 1
|
|
513
|
+
@options[:log_level] = current_level - 1
|
|
506
514
|
end
|
|
507
515
|
end
|
|
508
516
|
|
|
@@ -515,7 +523,7 @@ module Client
|
|
|
515
523
|
|
|
516
524
|
def version
|
|
517
525
|
@parser.on_tail('--version', 'Show version') do
|
|
518
|
-
puts "#{File.basename($PROGRAM_NAME)} version #{::
|
|
526
|
+
puts "#{File.basename($PROGRAM_NAME)} version #{::Client::VERSION}"
|
|
519
527
|
exit
|
|
520
528
|
end
|
|
521
529
|
end
|