tcp-server 1.0.6-java → 1.0.9-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 +14 -9
- data/lib/client.rb +83 -55
- data/lib/log.rb +86 -49
- data/lib/server/argument_parser.rb +8 -10
- data/lib/server/channel_initializer.rb +15 -14
- data/lib/server/config.rb +10 -4
- data/lib/server/instance_methods.rb +6 -2
- data/lib/server/listenable.rb +15 -8
- data/lib/server/message_handler.rb +3 -3
- data/lib/server/modular_handler.rb +1 -1
- data/lib/server/server.rb +1 -1
- data/lib/server/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c707938b3d92a5bd20d128d6f09bfcbbc50ff56f0d4c34006c40b27815b299c3
|
|
4
|
+
data.tar.gz: 01bb7897ee596c304ff32bf6dbb2ac5b380bc9f45cad86d0f39479515a4f07b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8cff7fdc5fbae7e1e7521bda5cd6b335a3c7f7822e1362b5a284d62a5a81b71f466dd52519c2c6f12389eb425fa262fc066ec0b756bf502391ae04841b581e37
|
|
7
|
+
data.tar.gz: bc42f07bfe23e91f9626de066f233ace69987e624950ed81d7724ee2ec8e5095d2475446f58a7a5b8d35c123b9201c98abf5a7a8d11fb6cb01636313c0584532
|
data/README.md
CHANGED
|
@@ -18,7 +18,9 @@ You may run the websocket server in a container. The [`colima`](https://github.c
|
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
20
|
colima start
|
|
21
|
-
docker-compose up
|
|
21
|
+
docker-compose up --detach
|
|
22
|
+
nc localhost 4000
|
|
23
|
+
docker-compose down
|
|
22
24
|
```
|
|
23
25
|
|
|
24
26
|
Building the image or running the container:
|
|
@@ -57,8 +59,7 @@ Download and install the latest version of [JRuby].
|
|
|
57
59
|
|
|
58
60
|
```sh
|
|
59
61
|
asdf plugin add ruby
|
|
60
|
-
|
|
61
|
-
# ~/.asdf/plugins/ruby/bin/list-all
|
|
62
|
+
asdf plugin update --all
|
|
62
63
|
asdf list all ruby
|
|
63
64
|
asdf install ruby jruby-9.3.4.0
|
|
64
65
|
```
|
|
@@ -123,7 +124,7 @@ Here is a bird's-eye view of the project layout.
|
|
|
123
124
|
|
|
124
125
|
```sh
|
|
125
126
|
# date && tree -I "logs|vendor|tmp"
|
|
126
|
-
|
|
127
|
+
Wed Jul 20 13:18:23 CDT 2022
|
|
127
128
|
.
|
|
128
129
|
├── Dockerfile
|
|
129
130
|
├── Gemfile
|
|
@@ -157,14 +158,14 @@ Thu Jun 16 00:03:23 CDT 2022
|
|
|
157
158
|
│ ├── test_spec.rb
|
|
158
159
|
│ └── verify
|
|
159
160
|
│ └── verify_spec.rb
|
|
160
|
-
├── tcp-server-1.0.2-java.gem
|
|
161
161
|
├── tcp-server-jruby.gemspec
|
|
162
162
|
├── tcp_server.png
|
|
163
163
|
└── tcp_server.rb
|
|
164
164
|
|
|
165
|
-
5 directories,
|
|
165
|
+
5 directories, 30 files
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
+
|
|
168
169
|
## CI linting
|
|
169
170
|
|
|
170
171
|
Use the GitLab CI Linting API to validate the syntax of a CI definition file.
|
|
@@ -173,12 +174,13 @@ Use the GitLab CI Linting API to validate the syntax of a CI definition file.
|
|
|
173
174
|
jq --null-input --arg yaml "$(<.gitlab/ci/gem.gitlab-ci.yml)" '.content=$yaml' | curl --silent --location https://gitlab.com/api/v4/ci/lint --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" --header "Content-Type: application/json" --data @- | jq --raw-output '.errors[0]'
|
|
174
175
|
```
|
|
175
176
|
|
|
177
|
+
|
|
176
178
|
## CI configuration
|
|
177
179
|
|
|
178
180
|
Generate a deploy key.
|
|
179
181
|
|
|
180
182
|
```sh
|
|
181
|
-
ssh-keygen -t ed25519 -C deploy_key
|
|
183
|
+
ssh-keygen -t ed25519 -P '' -C deploy_key -f deploy_key_ed25519
|
|
182
184
|
```
|
|
183
185
|
|
|
184
186
|
Use the GitLab Project-level Variables API to add the deploy key as a ssh private key variable.
|
|
@@ -186,18 +188,21 @@ Use the GitLab Project-level Variables API to add the deploy key as a ssh privat
|
|
|
186
188
|
```sh
|
|
187
189
|
project_path="nelsnelson/$(basename $(pwd))"
|
|
188
190
|
|
|
191
|
+
# Test auth token validity
|
|
192
|
+
curl --silent --show-error --location "https://gitlab.com/api/v4/projects" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" | jq '.[0]["id"]'
|
|
193
|
+
|
|
189
194
|
project=$(curl --silent --show-error --location "https://gitlab.com/api/v4/search?scope=projects&search=${project_path}" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" | jq --arg project_path "${project_path}" '.[] | select(.path_with_namespace == $project_path)')
|
|
190
195
|
|
|
191
196
|
project_id=$(curl --silent --show-error --location "https://gitlab.com/api/v4/search?scope=projects&search=${project_path}" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" | jq --arg project_path "${project_path}" '.[] | select(.path_with_namespace == $project_path) | .id')
|
|
192
197
|
|
|
193
198
|
# Add the deploy_token as a CI variable:
|
|
194
|
-
curl --silent --show-error --location --request POST "https://gitlab.com/api/v4/projects/${project_id}/variables" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" --form "key=SSH_PRIVATE_KEY" --form "value=$(cat ./
|
|
199
|
+
curl --silent --show-error --location --request POST "https://gitlab.com/api/v4/projects/${project_id}/variables" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" --form "key=SSH_PRIVATE_KEY" --form "value=$(cat ./deploy_key_ed25519)" --form "protected=true" | jq
|
|
195
200
|
```
|
|
196
201
|
|
|
197
202
|
Use the Deploy keys API to add a the public deploy key as a deploy key for the project.
|
|
198
203
|
|
|
199
204
|
```sh
|
|
200
|
-
curl --silent --show-error --location --request POST "https://gitlab.com/api/v4/projects/${project_id}/deploy_keys" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" --
|
|
205
|
+
curl --silent --show-error --location --request POST "https://gitlab.com/api/v4/projects/${project_id}/deploy_keys" --header "PRIVATE-TOKEN: ${GITLAB_COM_API_PRIVATE_TOKEN}" --form "title=deploy_key" --form "key=$(cat ./deploy_key_ed25519.pub)" --form "can_push=true" | jq
|
|
201
206
|
```
|
|
202
207
|
|
|
203
208
|
[license]: https://gitlab.com/nelsnelson/tcp-server-jruby/blob/master/LICENSE
|
data/lib/client.rb
CHANGED
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
#
|
|
13
13
|
# =end
|
|
14
14
|
|
|
15
|
-
require 'optparse'
|
|
16
|
-
|
|
17
15
|
require 'java'
|
|
18
16
|
require 'netty'
|
|
19
17
|
|
|
18
|
+
require 'logger'
|
|
19
|
+
require 'optparse'
|
|
20
|
+
|
|
20
21
|
require 'log'
|
|
21
22
|
|
|
22
23
|
# The Client module
|
|
@@ -26,16 +27,18 @@ end
|
|
|
26
27
|
|
|
27
28
|
# The Client module
|
|
28
29
|
module Client
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
def client_config
|
|
31
|
+
@client_config ||= {
|
|
32
|
+
host: '0.0.0.0',
|
|
32
33
|
port: 8080,
|
|
33
|
-
host: 'localhost',
|
|
34
34
|
ssl: false,
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
log_level: Logger::INFO,
|
|
36
|
+
quit_commands: %i[bye cease desist exit leave quit stop terminate],
|
|
37
|
+
max_frame_length: 8192,
|
|
38
|
+
delimiter: Java::io.netty.handler.codec.Delimiters.lineDelimiter
|
|
37
39
|
}.freeze
|
|
38
40
|
end
|
|
41
|
+
module_function :client_config
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
# The Client module
|
|
@@ -77,8 +80,8 @@ module Client
|
|
|
77
80
|
end
|
|
78
81
|
|
|
79
82
|
def configure_handlers(*handlers, &block)
|
|
80
|
-
|
|
81
|
-
listeners.addAll(handlers)
|
|
83
|
+
channel_initializer.default_handler.add_listener(self)
|
|
84
|
+
channel_initializer.default_handler.listeners.addAll(handlers)
|
|
82
85
|
@user_app = block
|
|
83
86
|
@application_handler = lambda do |ctx, msg|
|
|
84
87
|
if @user_app.nil? || @user_app.arity == 1
|
|
@@ -100,7 +103,7 @@ module Client
|
|
|
100
103
|
# The InstanceMethods module
|
|
101
104
|
module InstanceMethods
|
|
102
105
|
def puts(msg)
|
|
103
|
-
|
|
106
|
+
wait_until_channel_is_active
|
|
104
107
|
msg.chomp!
|
|
105
108
|
log.trace "#puts msg: #{msg.inspect}"
|
|
106
109
|
raise 'Message is empty!' if msg.nil? || msg.empty?
|
|
@@ -115,6 +118,10 @@ module Client
|
|
|
115
118
|
nil
|
|
116
119
|
end
|
|
117
120
|
|
|
121
|
+
def wait_until_channel_is_active(timeout = 5, give_up = Time.now + timeout)
|
|
122
|
+
sleep 0.1 until @channel.active? || Time.now > give_up
|
|
123
|
+
end
|
|
124
|
+
|
|
118
125
|
def connect(host = @options[:host], port = @options[:port])
|
|
119
126
|
return unless @channel.nil?
|
|
120
127
|
# Start the connection attempt.
|
|
@@ -143,7 +150,7 @@ module Client
|
|
|
143
150
|
|
|
144
151
|
def session
|
|
145
152
|
when_client_has_shut_down(@client_group) do |group|
|
|
146
|
-
log.debug "Channel group has shut down: #{group
|
|
153
|
+
log.debug "Channel group has shut down: #{group}"
|
|
147
154
|
end
|
|
148
155
|
@user_app.nil? ? read_user_commands : invoke_user_app
|
|
149
156
|
end
|
|
@@ -176,13 +183,13 @@ module Client
|
|
|
176
183
|
|
|
177
184
|
def channel_unregistered(ctx)
|
|
178
185
|
log.trace "##{__method__} channel: #{ctx.channel}"
|
|
179
|
-
shutdown
|
|
186
|
+
# shutdown
|
|
180
187
|
end
|
|
181
188
|
|
|
182
189
|
def message_received(ctx, message)
|
|
183
190
|
notify :message_received, ctx, message
|
|
184
191
|
if @application_handler.nil?
|
|
185
|
-
$stdout.
|
|
192
|
+
$stdout.print message.chomp unless message.nil?
|
|
186
193
|
else
|
|
187
194
|
@application_handler.call(ctx, message)
|
|
188
195
|
end
|
|
@@ -197,10 +204,11 @@ module Client
|
|
|
197
204
|
def read_user_commands
|
|
198
205
|
log.trace 'Reading user commands'
|
|
199
206
|
loop do
|
|
207
|
+
log.debug 'Waiting for user input...'
|
|
200
208
|
input = $stdin.gets
|
|
201
209
|
raise 'Poll failure from stdin' if input.nil?
|
|
202
210
|
break unless @channel.active?
|
|
203
|
-
break
|
|
211
|
+
break unless execute_command(input).nil?
|
|
204
212
|
end
|
|
205
213
|
end
|
|
206
214
|
|
|
@@ -222,23 +230,32 @@ module Client
|
|
|
222
230
|
@listeners ||= java.util.concurrent.CopyOnWriteArrayList.new
|
|
223
231
|
end
|
|
224
232
|
|
|
225
|
-
def add_listener(listener)
|
|
226
|
-
listeners
|
|
233
|
+
def add_listener(*listener)
|
|
234
|
+
listeners.addAll(listener)
|
|
235
|
+
ensure
|
|
236
|
+
log.trace "Listeners: #{listeners}"
|
|
227
237
|
end
|
|
228
238
|
|
|
229
|
-
def remove_listener(listener)
|
|
230
|
-
listeners.
|
|
239
|
+
def remove_listener(*listener)
|
|
240
|
+
listeners.removeAll(listener)
|
|
231
241
|
end
|
|
232
242
|
|
|
233
|
-
def
|
|
234
|
-
|
|
235
|
-
|
|
243
|
+
def replace_listeners(*listener)
|
|
244
|
+
listeners.clear
|
|
245
|
+
add_listener(*listener)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def notify(event, *args)
|
|
236
249
|
listeners.each do |listener|
|
|
237
|
-
|
|
250
|
+
next unless listener.respond_to?(event.to_sym)
|
|
251
|
+
log.trace "Notifying listener #{listener} of event: #{event}"
|
|
252
|
+
listener.send(event.to_sym, *args)
|
|
238
253
|
end
|
|
239
254
|
end
|
|
240
255
|
end
|
|
256
|
+
# module Listenable
|
|
241
257
|
end
|
|
258
|
+
# module Client
|
|
242
259
|
|
|
243
260
|
# The Client module
|
|
244
261
|
module Client
|
|
@@ -342,13 +359,7 @@ module Client
|
|
|
342
359
|
|
|
343
360
|
# The ChannelInitializer class
|
|
344
361
|
class ChannelInitializer < Java::io.netty.channel.ChannelInitializer
|
|
345
|
-
|
|
346
|
-
FrameDecoderBufferSize = 8192 # bytes
|
|
347
|
-
# The encoder and decoder are sharable. If they were not, then
|
|
348
|
-
# constant definitions could not be used.
|
|
349
|
-
Decoder = StringDecoder.new
|
|
350
|
-
Encoder = StringEncoder.new
|
|
351
|
-
attr_accessor :handlers
|
|
362
|
+
attr_accessor :decoder, :encoder, :handlers
|
|
352
363
|
|
|
353
364
|
def initialize(options = {})
|
|
354
365
|
super()
|
|
@@ -356,6 +367,8 @@ module Client
|
|
|
356
367
|
@host = @options[:host]
|
|
357
368
|
@port = @options[:port]
|
|
358
369
|
@handlers = []
|
|
370
|
+
@decoder = StringDecoder.new
|
|
371
|
+
@encoder = StringEncoder.new
|
|
359
372
|
end
|
|
360
373
|
|
|
361
374
|
def <<(handler)
|
|
@@ -365,13 +378,19 @@ module Client
|
|
|
365
378
|
def initChannel(channel)
|
|
366
379
|
pipeline = channel.pipeline
|
|
367
380
|
pipeline.addLast(ssl_handler(channel)) if @options[:ssl]
|
|
368
|
-
pipeline.addLast(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
Encoder
|
|
372
|
-
)
|
|
381
|
+
# pipeline.addLast(frame_decoder)
|
|
382
|
+
pipeline.addLast(decoder)
|
|
383
|
+
pipeline.addLast(encoder)
|
|
373
384
|
add_user_handlers(pipeline)
|
|
374
|
-
pipeline.addLast(
|
|
385
|
+
pipeline.addLast(default_handler)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def frame_decoder
|
|
389
|
+
DelimiterBasedFrameDecoder.new(@options[:max_frame_length], @options[:delimiter])
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def default_handler
|
|
393
|
+
@default_handler ||= ::Client::ModularHandler.new
|
|
375
394
|
end
|
|
376
395
|
|
|
377
396
|
protected
|
|
@@ -424,7 +443,7 @@ module TCP
|
|
|
424
443
|
include ::Client::Listenable
|
|
425
444
|
|
|
426
445
|
def initialize(options = {}, *handlers, &block)
|
|
427
|
-
init(::Client
|
|
446
|
+
init(::Client.client_config.merge(options))
|
|
428
447
|
configure_handlers(*handlers, &block)
|
|
429
448
|
connect
|
|
430
449
|
session
|
|
@@ -444,32 +463,32 @@ end
|
|
|
444
463
|
module Client
|
|
445
464
|
# The ArgumentsParser class
|
|
446
465
|
class ArgumentsParser
|
|
447
|
-
Flags = %i[banner port ssl log_level help version].freeze
|
|
448
466
|
attr_reader :parser, :options
|
|
449
467
|
|
|
450
|
-
def initialize(parser = OptionParser.new, options = ::Client
|
|
468
|
+
def initialize(parser = OptionParser.new, options = ::Client.client_config.dup)
|
|
451
469
|
@parser = parser
|
|
452
470
|
@options = options
|
|
453
|
-
|
|
471
|
+
@flags = %i[banner port ssl log_level help version]
|
|
472
|
+
@flags.each { |method_name| method(method_name)&.call if respond_to?(method_name) }
|
|
454
473
|
end
|
|
455
474
|
|
|
456
475
|
def banner
|
|
457
|
-
@parser.banner = "Usage: #{File.basename($PROGRAM_NAME)} [
|
|
476
|
+
@parser.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] <hostname> [port]"
|
|
458
477
|
@parser.separator ''
|
|
459
478
|
@parser.separator 'Options:'
|
|
460
479
|
end
|
|
461
480
|
|
|
462
481
|
def host
|
|
463
482
|
description = "Connect to server at this host; default: #{@options[:host]}"
|
|
464
|
-
@parser.on('-h', '--
|
|
483
|
+
@parser.on('-h', '--hostname=<hostname>', description) do |v|
|
|
465
484
|
@options[:host] = v
|
|
466
485
|
end
|
|
467
486
|
end
|
|
468
487
|
|
|
469
|
-
|
|
488
|
+
def validated_port(val, integer_pattern = /^\d+$/)
|
|
489
|
+
raise OptionParser::InvalidArgument, "Invalid port: #{val}" unless \
|
|
490
|
+
integer_pattern.match?(val.to_s) && val.positive? && val < 65_536
|
|
470
491
|
|
|
471
|
-
def validated_port(val)
|
|
472
|
-
raise "Invalid port: #{v}" unless IntegerPattern.match?(val.to_s) && val.positive? && val < 65_536
|
|
473
492
|
val
|
|
474
493
|
end
|
|
475
494
|
|
|
@@ -488,6 +507,7 @@ module Client
|
|
|
488
507
|
|
|
489
508
|
def log_level
|
|
490
509
|
@parser.on_tail('-v', '--verbose', 'Increase verbosity') do
|
|
510
|
+
@options[:log_level] ||= 0
|
|
491
511
|
@options[:log_level] -= 1
|
|
492
512
|
end
|
|
493
513
|
end
|
|
@@ -505,19 +525,28 @@ module Client
|
|
|
505
525
|
exit
|
|
506
526
|
end
|
|
507
527
|
end
|
|
528
|
+
|
|
529
|
+
def parse_positional_arguments!
|
|
530
|
+
@options[:host] = ARGV.shift or raise OptionParser::MissingArgument, 'hostname'
|
|
531
|
+
return if (given_port = ARGV.shift&.to_i).nil?
|
|
532
|
+
|
|
533
|
+
@options[:port] = validated_port(given_port).to_i
|
|
534
|
+
end
|
|
508
535
|
end
|
|
509
536
|
# class ArgumentsParser
|
|
510
537
|
|
|
511
|
-
# rubocop: disable Metrics/AbcSize
|
|
512
538
|
def parse_arguments(arguments_parser = ArgumentsParser.new)
|
|
513
539
|
arguments_parser.parser.parse!(ARGV)
|
|
514
|
-
arguments_parser.
|
|
515
|
-
arguments_parser.options[:port] = validated_port(v).to_i unless (v = ARGV.shift).nil?
|
|
540
|
+
arguments_parser.parse_positional_arguments!
|
|
516
541
|
arguments_parser.options
|
|
517
|
-
rescue OptionParser::
|
|
542
|
+
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption,
|
|
543
|
+
OptionParser::MissingArgument, OptionParser::NeedlessArgument => e
|
|
544
|
+
puts e.message
|
|
545
|
+
puts parser
|
|
546
|
+
exit
|
|
547
|
+
rescue OptionParser::AmbiguousOption => e
|
|
518
548
|
abort e.message
|
|
519
549
|
end
|
|
520
|
-
# rubocop: enable Metrics/AbcSize
|
|
521
550
|
end
|
|
522
551
|
# module Client
|
|
523
552
|
|
|
@@ -538,12 +567,12 @@ end
|
|
|
538
567
|
|
|
539
568
|
# The Client module
|
|
540
569
|
module Client
|
|
541
|
-
# The
|
|
542
|
-
class
|
|
570
|
+
# The Console class
|
|
571
|
+
class Console
|
|
543
572
|
def message_received(ctx, message)
|
|
544
573
|
log.trace "##{__method__} channel: #{ctx.channel}, message: #{message}"
|
|
545
574
|
log.debug "Received message: #{message}"
|
|
546
|
-
|
|
575
|
+
$stdout.print message unless message.nil?
|
|
547
576
|
end
|
|
548
577
|
end
|
|
549
578
|
end
|
|
@@ -557,8 +586,7 @@ module Client
|
|
|
557
586
|
# rubocop: disable Metrics/MethodLength
|
|
558
587
|
def main(args = parse_arguments)
|
|
559
588
|
Logging.log_level = args[:log_level]
|
|
560
|
-
|
|
561
|
-
::TCP::Client.new(args, *handlers)
|
|
589
|
+
::TCP::Client.new(args, ::Client::Console.new, ::Client::Monitor.new)
|
|
562
590
|
rescue Interrupt => e
|
|
563
591
|
warn format(InterruptTemplate, class: e.class)
|
|
564
592
|
exit
|
data/lib/log.rb
CHANGED
|
@@ -12,56 +12,98 @@ require 'logger'
|
|
|
12
12
|
|
|
13
13
|
require 'log4j-2'
|
|
14
14
|
|
|
15
|
+
require 'fileutils'
|
|
16
|
+
|
|
17
|
+
# The Logging module
|
|
18
|
+
module Logging
|
|
19
|
+
# rubocop: disable Metrics/MethodLength
|
|
20
|
+
def config
|
|
21
|
+
@config ||= begin
|
|
22
|
+
lib_dir_path = File.expand_path(__dir__)
|
|
23
|
+
project_dir_path = File.expand_path(File.dirname(lib_dir_path))
|
|
24
|
+
logs_dir_path = File.expand_path(File.join(project_dir_path, 'logs'))
|
|
25
|
+
server_log_file = File.expand_path(File.join(logs_dir_path, 'server.log'))
|
|
26
|
+
{
|
|
27
|
+
level: :info,
|
|
28
|
+
name: 'tcp-server',
|
|
29
|
+
lib_dir_path: lib_dir_path,
|
|
30
|
+
project_dir_path: project_dir_path,
|
|
31
|
+
logs_dir_path: logs_dir_path,
|
|
32
|
+
server_log_file: server_log_file,
|
|
33
|
+
rolling_log_file_name_template: 'server-%d{yyyy-MM-dd}.log.gz',
|
|
34
|
+
logger_pattern_template: '%d{ABSOLUTE} %-5p [%c{1}] %m%n',
|
|
35
|
+
schedule: '0 0 0 * * ?',
|
|
36
|
+
size: '100M'
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
module_function :config
|
|
41
|
+
# rubocop: enable Metrics/MethodLength
|
|
42
|
+
end
|
|
43
|
+
|
|
15
44
|
# The LogInitialization module
|
|
16
45
|
module LogInitialization
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
46
|
+
def init
|
|
47
|
+
init_log_file
|
|
48
|
+
init_log4j if defined? Java
|
|
49
|
+
end
|
|
50
|
+
module_function :init
|
|
51
|
+
|
|
52
|
+
def init_log_file
|
|
53
|
+
FileUtils.mkdir_p(Logging.config[:logs_dir_path])
|
|
54
|
+
return if File.file?(Logging.config[:server_log_file])
|
|
55
|
+
|
|
56
|
+
File.write(Logging.config[:server_log_file], '')
|
|
57
|
+
end
|
|
58
|
+
module_function :init_log_file
|
|
26
59
|
|
|
27
60
|
# rubocop: disable Metrics/AbcSize
|
|
28
61
|
# rubocop: disable Metrics/MethodLength
|
|
29
|
-
def
|
|
62
|
+
def init_log4j(log_level = org.apache.logging.log4j.Level::INFO)
|
|
63
|
+
server_log_file = Logging.config[:server_log_file]
|
|
64
|
+
logs_dir_path = Logging.config[:logs_dir_path]
|
|
65
|
+
rolling_log_file_name_template = Logging.config[:rolling_log_file_name_template]
|
|
66
|
+
rolling_log_file_path = File.join(logs_dir_path, rolling_log_file_name_template)
|
|
67
|
+
|
|
30
68
|
java.lang::System.setProperty('log4j.shutdownHookEnabled', java.lang::Boolean.toString(false))
|
|
31
69
|
factory = org.apache.logging.log4j.core.config.builder.api::ConfigurationBuilderFactory
|
|
32
70
|
config = factory.newConfigurationBuilder()
|
|
33
71
|
|
|
34
|
-
|
|
72
|
+
if log_level.is_a?(Symbol)
|
|
73
|
+
log_level = org.apache.logging.log4j.Level.to_level(
|
|
74
|
+
log_level.to_s.upcase
|
|
75
|
+
)
|
|
76
|
+
end
|
|
35
77
|
config.setStatusLevel(log_level)
|
|
36
|
-
config.setConfigurationName('
|
|
78
|
+
config.setConfigurationName(Logging.config['name'])
|
|
37
79
|
|
|
38
|
-
#
|
|
80
|
+
# Create a console appender
|
|
39
81
|
target = org.apache.logging.log4j.core.appender::ConsoleAppender::Target::SYSTEM_OUT
|
|
40
82
|
layout = config.newLayout('PatternLayout')
|
|
41
|
-
layout = layout.addAttribute('pattern',
|
|
83
|
+
layout = layout.addAttribute('pattern', Logging.config[:logger_pattern_template])
|
|
42
84
|
appender = config.newAppender('stdout', 'CONSOLE')
|
|
43
85
|
appender = appender.addAttribute('target', target)
|
|
44
86
|
appender = appender.add(layout)
|
|
45
87
|
config.add(appender)
|
|
46
88
|
|
|
47
|
-
#
|
|
89
|
+
# Create a root logger
|
|
48
90
|
root_logger = config.newRootLogger(log_level)
|
|
49
91
|
root_logger = root_logger.add(config.newAppenderRef('stdout'))
|
|
50
92
|
|
|
51
|
-
#
|
|
93
|
+
# Create a rolling file appender
|
|
52
94
|
cron = config.newComponent('CronTriggeringPolicy')
|
|
53
|
-
cron = cron.addAttribute('schedule',
|
|
95
|
+
cron = cron.addAttribute('schedule', Logging.config[:schedule])
|
|
54
96
|
|
|
55
97
|
size = config.newComponent('SizeBasedTriggeringPolicy')
|
|
56
|
-
size = size.addAttribute('size',
|
|
98
|
+
size = size.addAttribute('size', Logging.config[:size])
|
|
57
99
|
|
|
58
100
|
policies = config.newComponent('Policies')
|
|
59
101
|
policies = policies.addComponent(cron)
|
|
60
102
|
policies = policies.addComponent(size)
|
|
61
103
|
|
|
62
104
|
appender = config.newAppender('rolling_file', 'RollingFile')
|
|
63
|
-
appender = appender.addAttribute('fileName',
|
|
64
|
-
appender = appender.addAttribute('filePattern',
|
|
105
|
+
appender = appender.addAttribute('fileName', server_log_file)
|
|
106
|
+
appender = appender.addAttribute('filePattern', rolling_log_file_path)
|
|
65
107
|
appender = appender.add(layout)
|
|
66
108
|
appender = appender.addComponent(policies)
|
|
67
109
|
config.add(appender)
|
|
@@ -77,10 +119,11 @@ module LogInitialization
|
|
|
77
119
|
# rubocop: enable Metrics/AbcSize
|
|
78
120
|
# rubocop: enable Metrics/MethodLength
|
|
79
121
|
# def init_log4j
|
|
122
|
+
module_function :init_log4j
|
|
80
123
|
end
|
|
81
124
|
# module LogInitialization
|
|
82
125
|
|
|
83
|
-
::LogInitialization.
|
|
126
|
+
::LogInitialization.init
|
|
84
127
|
|
|
85
128
|
# The Apache log4j Logger class
|
|
86
129
|
# rubocop: disable Style/ClassAndModuleChildren
|
|
@@ -113,35 +156,14 @@ end
|
|
|
113
156
|
|
|
114
157
|
# The Logging module
|
|
115
158
|
module Logging
|
|
116
|
-
|
|
117
|
-
Configuration = {
|
|
118
|
-
level: Logger::INFO
|
|
119
|
-
}
|
|
120
|
-
# rubocop: enable Style/MutableConstant
|
|
121
|
-
|
|
122
|
-
def self.log_level=(log_level)
|
|
123
|
-
Logging::Configuration[:level] = log_level
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def self.log_level
|
|
127
|
-
Logging::Configuration[:level]
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def log(level = Logging.log_level, log_name = nil)
|
|
131
|
-
@log ||= init_logger(level, log_name)
|
|
132
|
-
end
|
|
133
|
-
alias logger log
|
|
134
|
-
|
|
135
|
-
protected
|
|
136
|
-
|
|
137
|
-
def init_logger(level = Logging.log_level, logger_name = nil)
|
|
159
|
+
def init_logger(level = :all, logger_name = nil)
|
|
138
160
|
return init_java_logger(level, logger_name, caller[2]) if defined?(Java)
|
|
139
|
-
|
|
161
|
+
init_ruby_logger(level)
|
|
140
162
|
end
|
|
141
163
|
|
|
142
|
-
def init_ruby_logger(level
|
|
143
|
-
logger_instance = Logger.new
|
|
144
|
-
logger_instance.level = level
|
|
164
|
+
def init_ruby_logger(level)
|
|
165
|
+
logger_instance = Logger.new
|
|
166
|
+
logger_instance.level = Logging::Level.to_level(level.to_s.upcase)
|
|
145
167
|
logger_instance
|
|
146
168
|
end
|
|
147
169
|
|
|
@@ -149,9 +171,8 @@ module Logging
|
|
|
149
171
|
def init_java_logger(level, logger_name = nil, source_location = nil)
|
|
150
172
|
logger_name = get_formatted_logger_name(logger_name)
|
|
151
173
|
logger_name = source_location.split(/\//).last if logger_name.empty?
|
|
152
|
-
level_name = symbolize_numeric_log_level(level).to_s.upcase
|
|
153
174
|
logger_instance = org.apache.logging.log4j.LogManager.getLogger(logger_name)
|
|
154
|
-
logger_instance.level = org.apache.logging.log4j.Level.to_level(
|
|
175
|
+
logger_instance.level = org.apache.logging.log4j.Level.to_level(level.to_s.upcase)
|
|
155
176
|
logger_instance
|
|
156
177
|
end
|
|
157
178
|
# rubocop: enable Metrics/AbcSize
|
|
@@ -185,6 +206,22 @@ module Logging
|
|
|
185
206
|
end
|
|
186
207
|
end
|
|
187
208
|
# rubocop: enable Metrics/CyclomaticComplexity
|
|
209
|
+
module_function :symbolize_numeric_log_level
|
|
210
|
+
|
|
211
|
+
def log_level=(log_level)
|
|
212
|
+
Logging.config[:level] = symbolize_numeric_log_level(log_level)
|
|
213
|
+
end
|
|
214
|
+
module_function :log_level=
|
|
215
|
+
|
|
216
|
+
def log_level
|
|
217
|
+
Logging.config[:level]
|
|
218
|
+
end
|
|
219
|
+
module_function :log_level
|
|
220
|
+
|
|
221
|
+
def log(level = Logging.log_level, log_name = nil)
|
|
222
|
+
@log ||= init_logger(level, log_name)
|
|
223
|
+
end
|
|
224
|
+
alias logger log
|
|
188
225
|
end
|
|
189
226
|
# module Logging
|
|
190
227
|
|
|
@@ -18,16 +18,13 @@ require_relative 'config'
|
|
|
18
18
|
module Server
|
|
19
19
|
# The ArgumentsParser class
|
|
20
20
|
class ArgumentsParser
|
|
21
|
-
Flags = %i[
|
|
22
|
-
banner port ssl idle_reading idle_writing log_requests log_level help
|
|
23
|
-
version
|
|
24
|
-
].freeze
|
|
25
21
|
attr_reader :parser, :options
|
|
26
22
|
|
|
27
23
|
def initialize(option_parser = OptionParser.new)
|
|
28
24
|
@parser = option_parser
|
|
29
|
-
@options = ::Server
|
|
30
|
-
|
|
25
|
+
@options = ::Server.server_config.dup
|
|
26
|
+
@flags = %i[banner port ssl idle_reading idle_writing log_requests log_level help version]
|
|
27
|
+
@flags.each { |method_name| method(method_name).call }
|
|
31
28
|
end
|
|
32
29
|
|
|
33
30
|
def banner
|
|
@@ -36,10 +33,10 @@ module Server
|
|
|
36
33
|
@parser.separator 'Options:'
|
|
37
34
|
end
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
def validated_port(val, integer_pattern = /^\d+$/)
|
|
37
|
+
raise OptionParser::InvalidArgument, "Invalid port: #{v}" unless \
|
|
38
|
+
integer_pattern.match?(val.to_s) && val.positive? && val < 65_536
|
|
40
39
|
|
|
41
|
-
def validated_port(val)
|
|
42
|
-
raise "Invalid port: #{v}" unless IntegerPattern.match?(val.to_s) && val.positive? && val < 65_536
|
|
43
40
|
val
|
|
44
41
|
end
|
|
45
42
|
|
|
@@ -76,7 +73,8 @@ module Server
|
|
|
76
73
|
|
|
77
74
|
def log_level
|
|
78
75
|
@parser.on_tail('-v', '--verbose', 'Increase verbosity') do
|
|
79
|
-
@options
|
|
76
|
+
current_level = @options.fetch(:log_level, 0)
|
|
77
|
+
@options[:log_level] = current_level - 1
|
|
80
78
|
end
|
|
81
79
|
end
|
|
82
80
|
|
|
@@ -30,19 +30,16 @@ module Server
|
|
|
30
30
|
|
|
31
31
|
# The ChannelInitializer class
|
|
32
32
|
class ChannelInitializer < Java::io.netty.channel.ChannelInitializer
|
|
33
|
-
|
|
34
|
-
# The encoder and decoder are sharable. If they were not, then
|
|
35
|
-
# constant definitions could not be used.
|
|
36
|
-
Decoder = StringDecoder.new
|
|
37
|
-
Encoder = StringEncoder.new
|
|
38
|
-
attr_accessor :user_handlers
|
|
33
|
+
attr_accessor :decoder, :encoder, :user_handlers
|
|
39
34
|
attr_reader :options
|
|
40
35
|
|
|
41
36
|
def initialize(channel_group, options = {})
|
|
42
37
|
super()
|
|
43
38
|
@channel_group = channel_group
|
|
44
39
|
@options = options
|
|
45
|
-
@user_handlers = []
|
|
40
|
+
@user_handlers = @options.fetch(:handlers, [])
|
|
41
|
+
@decoder = StringDecoder.new
|
|
42
|
+
@encoder = StringEncoder.new
|
|
46
43
|
end
|
|
47
44
|
|
|
48
45
|
def <<(handler)
|
|
@@ -52,21 +49,25 @@ module Server
|
|
|
52
49
|
def initChannel(channel)
|
|
53
50
|
pipeline = channel.pipeline
|
|
54
51
|
pipeline.addLast(ssl_handler(channel)) if @options[:ssl]
|
|
55
|
-
pipeline.addLast(
|
|
56
|
-
DelimiterBasedFrameDecoder.new(FrameDecoderBufferBytesSize, Delimiters.lineDelimiter),
|
|
57
|
-
Decoder,
|
|
58
|
-
Encoder
|
|
59
|
-
)
|
|
52
|
+
pipeline.addLast(frame_decoder, @decoder, @encoder)
|
|
60
53
|
add_user_handlers(pipeline)
|
|
61
54
|
pipeline.addLast(default_handler)
|
|
62
55
|
end
|
|
63
56
|
|
|
57
|
+
def frame_decoder
|
|
58
|
+
DelimiterBasedFrameDecoder.new(@options[:max_frame_length], @options[:delimiter])
|
|
59
|
+
end
|
|
60
|
+
|
|
64
61
|
def default_handler
|
|
65
62
|
@default_handler ||= ::Server::ModularHandler.new(@channel_group)
|
|
66
63
|
end
|
|
67
64
|
|
|
68
|
-
def add_listener(
|
|
69
|
-
default_handler.add_listener(
|
|
65
|
+
def add_listener(*listeners)
|
|
66
|
+
default_handler.add_listener(*listeners)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def replace_listeners(*listeners)
|
|
70
|
+
default_handler.replace_listener(*listeners)
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
protected
|
data/lib/server/config.rb
CHANGED
|
@@ -10,13 +10,15 @@
|
|
|
10
10
|
#
|
|
11
11
|
# =end
|
|
12
12
|
|
|
13
|
+
require 'netty'
|
|
14
|
+
|
|
13
15
|
require 'logger'
|
|
14
16
|
|
|
15
17
|
# The Server module
|
|
16
18
|
module Server
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
# rubocop: disable Metrics/MethodLength
|
|
20
|
+
def server_config
|
|
21
|
+
@server_config ||= {
|
|
20
22
|
host: '0.0.0.0',
|
|
21
23
|
port: 8080,
|
|
22
24
|
ssl: false,
|
|
@@ -24,7 +26,11 @@ module Server
|
|
|
24
26
|
idle_writing: 30, # seconds
|
|
25
27
|
log_requests: false,
|
|
26
28
|
log_level: Logger::INFO,
|
|
27
|
-
quit_commands: %i[bye quit]
|
|
29
|
+
quit_commands: %i[bye cease desist exit leave quit stop terminate],
|
|
30
|
+
max_frame_length: 8192,
|
|
31
|
+
delimiter: Java::io.netty.handler.codec.Delimiters.lineDelimiter
|
|
28
32
|
}.freeze
|
|
29
33
|
end
|
|
34
|
+
module_function :server_config
|
|
35
|
+
# rubocop: enable Metrics/MethodLength
|
|
30
36
|
end
|
|
@@ -96,8 +96,12 @@ module Server
|
|
|
96
96
|
channel_initializer << handler
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
def add_listener(
|
|
100
|
-
channel_initializer.add_listener(
|
|
99
|
+
def add_listener(*listeners)
|
|
100
|
+
channel_initializer.add_listener(*listeners)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def replace_listeners(*listeners)
|
|
104
|
+
channel_initializer.replace_listener(*listeners)
|
|
101
105
|
end
|
|
102
106
|
end
|
|
103
107
|
# module ServerInstanceMethods
|
data/lib/server/listenable.rb
CHANGED
|
@@ -20,19 +20,26 @@ module Server
|
|
|
20
20
|
@listeners ||= java.util.concurrent.CopyOnWriteArrayList.new
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def add_listener(listener)
|
|
24
|
-
listeners
|
|
23
|
+
def add_listener(*listener)
|
|
24
|
+
listeners.addAll(listener)
|
|
25
|
+
ensure
|
|
26
|
+
log.trace "Listeners: #{listeners}"
|
|
25
27
|
end
|
|
26
28
|
|
|
27
|
-
def remove_listener(listener)
|
|
28
|
-
listeners.
|
|
29
|
+
def remove_listener(*listener)
|
|
30
|
+
listeners.removeAll(listener)
|
|
29
31
|
end
|
|
30
32
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
def replace_listeners(*listener)
|
|
34
|
+
listeners.clear
|
|
35
|
+
add_listener(*listener)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def notify(event, *args)
|
|
34
39
|
listeners.each do |listener|
|
|
35
|
-
|
|
40
|
+
next unless listener.respond_to?(event.to_sym)
|
|
41
|
+
log.trace "Notifying listener #{listener} of event: #{event}"
|
|
42
|
+
listener.send(event.to_sym, *args)
|
|
36
43
|
end
|
|
37
44
|
end
|
|
38
45
|
end
|
|
@@ -39,17 +39,17 @@ module Server
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def messageReceived(ctx, msg)
|
|
42
|
-
log.trace "##{
|
|
42
|
+
log.trace "##{__method__} channel: #{ctx.channel}, message: #{msg.inspect}"
|
|
43
43
|
msg&.chomp!
|
|
44
44
|
log.info "Received message: #{msg}"
|
|
45
|
-
return super(ctx, msg) unless respond_to?(:handle_message) && @handler
|
|
45
|
+
return super(ctx, msg) unless respond_to?(:handle_message) && @handler&.arity == 2
|
|
46
46
|
handle_message(ctx, msg)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def handle_message(ctx, message)
|
|
50
50
|
request = message.to_s.strip
|
|
51
51
|
response = @handler.call(ctx.channel, request).to_s.chomp
|
|
52
|
-
log.debug "response: #{response}"
|
|
52
|
+
log.debug "##{__method__} response: #{response}"
|
|
53
53
|
ctx.channel.writeAndFlush("#{response}\n")
|
|
54
54
|
end
|
|
55
55
|
end
|
data/lib/server/server.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Server
|
|
|
25
25
|
attr_reader :options
|
|
26
26
|
|
|
27
27
|
def initialize(params = {}, &block)
|
|
28
|
-
@options = ::Server
|
|
28
|
+
@options = ::Server.server_config.merge(params.fetch(:options, {}))
|
|
29
29
|
configure_handlers(&block)
|
|
30
30
|
end
|
|
31
31
|
|
data/lib/server/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tcp-server
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.9
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Nels Nelson
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-07-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|