tcp-server 1.0.5-java → 1.0.8-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 +72 -51
- data/lib/log.rb +86 -49
- data/lib/server/argument_parser.rb +8 -10
- data/lib/server/channel_initializer.rb +20 -15
- data/lib/server/config.rb +10 -4
- data/lib/server/instance_methods.rb +11 -5
- data/lib/server/listenable.rb +6 -4
- data/lib/server/message_handler.rb +3 -3
- data/lib/server/modular_handler.rb +9 -12
- 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: 3c55987f7d6c92030cd96937448d5dea7b62c5dd43292e2d47a5b1901cdcbfa8
|
4
|
+
data.tar.gz: 6396aaaa64e18e454801debd881e618558ab65bec55de917fd328f0f4cdc041e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 115f8b7d5bc5f8f48cfdb72eb4b81d6128f4b69af919eeaed79693bfb04aa1d09d893ba8970682f74afd417cb46c4176b73ca2b70ed082d0adec9268e8d53010
|
7
|
+
data.tar.gz: e1c144c6b38c6adca2e96c5e99cbd4a31d52aebe2076e7bd7cc82d2700c0e1f9c11a20d93f32943b6995a6a0e8ba4c20300e6d35170130982403736f64815050
|
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
|
|
@@ -224,17 +232,19 @@ module Client
|
|
224
232
|
|
225
233
|
def add_listener(listener)
|
226
234
|
listeners << listener
|
235
|
+
ensure
|
236
|
+
log.trace "Listeners: #{listeners}"
|
227
237
|
end
|
228
238
|
|
229
239
|
def remove_listener(listener)
|
230
240
|
listeners.delete(listener)
|
231
241
|
end
|
232
242
|
|
233
|
-
def notify(
|
234
|
-
return if listeners.empty?
|
235
|
-
log.trace "Notifying listeners (#{listeners}) of message: #{message}"
|
243
|
+
def notify(event, *args)
|
236
244
|
listeners.each do |listener|
|
237
|
-
|
245
|
+
next unless listener.respond_to?(event.to_sym)
|
246
|
+
log.trace "Notifying listener #{listener} of event: #{event}"
|
247
|
+
listener.send(event.to_sym, *args)
|
238
248
|
end
|
239
249
|
end
|
240
250
|
end
|
@@ -342,13 +352,7 @@ module Client
|
|
342
352
|
|
343
353
|
# The ChannelInitializer class
|
344
354
|
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
|
355
|
+
attr_accessor :decoder, :encoder, :handlers
|
352
356
|
|
353
357
|
def initialize(options = {})
|
354
358
|
super()
|
@@ -356,6 +360,8 @@ module Client
|
|
356
360
|
@host = @options[:host]
|
357
361
|
@port = @options[:port]
|
358
362
|
@handlers = []
|
363
|
+
@decoder = StringDecoder.new
|
364
|
+
@encoder = StringEncoder.new
|
359
365
|
end
|
360
366
|
|
361
367
|
def <<(handler)
|
@@ -365,13 +371,19 @@ module Client
|
|
365
371
|
def initChannel(channel)
|
366
372
|
pipeline = channel.pipeline
|
367
373
|
pipeline.addLast(ssl_handler(channel)) if @options[:ssl]
|
368
|
-
pipeline.addLast(
|
369
|
-
|
370
|
-
|
371
|
-
Encoder
|
372
|
-
)
|
374
|
+
# pipeline.addLast(frame_decoder)
|
375
|
+
pipeline.addLast(decoder)
|
376
|
+
pipeline.addLast(encoder)
|
373
377
|
add_user_handlers(pipeline)
|
374
|
-
pipeline.addLast(
|
378
|
+
pipeline.addLast(default_handler)
|
379
|
+
end
|
380
|
+
|
381
|
+
def frame_decoder
|
382
|
+
DelimiterBasedFrameDecoder.new(@options[:max_frame_length], @options[:delimiter])
|
383
|
+
end
|
384
|
+
|
385
|
+
def default_handler
|
386
|
+
@default_handler ||= ::Client::ModularHandler.new
|
375
387
|
end
|
376
388
|
|
377
389
|
protected
|
@@ -424,7 +436,7 @@ module TCP
|
|
424
436
|
include ::Client::Listenable
|
425
437
|
|
426
438
|
def initialize(options = {}, *handlers, &block)
|
427
|
-
init(::Client
|
439
|
+
init(::Client.client_config.merge(options))
|
428
440
|
configure_handlers(*handlers, &block)
|
429
441
|
connect
|
430
442
|
session
|
@@ -444,32 +456,32 @@ end
|
|
444
456
|
module Client
|
445
457
|
# The ArgumentsParser class
|
446
458
|
class ArgumentsParser
|
447
|
-
Flags = %i[banner port ssl log_level help version].freeze
|
448
459
|
attr_reader :parser, :options
|
449
460
|
|
450
|
-
def initialize(parser = OptionParser.new, options = ::Client
|
461
|
+
def initialize(parser = OptionParser.new, options = ::Client.client_config.dup)
|
451
462
|
@parser = parser
|
452
463
|
@options = options
|
453
|
-
|
464
|
+
@flags = %i[banner port ssl log_level help version]
|
465
|
+
@flags.each { |method_name| method(method_name)&.call if respond_to?(method_name) }
|
454
466
|
end
|
455
467
|
|
456
468
|
def banner
|
457
|
-
@parser.banner = "Usage: #{File.basename($PROGRAM_NAME)} [
|
469
|
+
@parser.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] <hostname> [port]"
|
458
470
|
@parser.separator ''
|
459
471
|
@parser.separator 'Options:'
|
460
472
|
end
|
461
473
|
|
462
474
|
def host
|
463
475
|
description = "Connect to server at this host; default: #{@options[:host]}"
|
464
|
-
@parser.on('-h', '--
|
476
|
+
@parser.on('-h', '--hostname=<hostname>', description) do |v|
|
465
477
|
@options[:host] = v
|
466
478
|
end
|
467
479
|
end
|
468
480
|
|
469
|
-
|
481
|
+
def validated_port(val, integer_pattern = /^\d+$/)
|
482
|
+
raise OptionParser::InvalidArgument, "Invalid port: #{val}" unless \
|
483
|
+
integer_pattern.match?(val.to_s) && val.positive? && val < 65_536
|
470
484
|
|
471
|
-
def validated_port(val)
|
472
|
-
raise "Invalid port: #{v}" unless IntegerPattern.match?(val.to_s) && val.positive? && val < 65_536
|
473
485
|
val
|
474
486
|
end
|
475
487
|
|
@@ -488,6 +500,7 @@ module Client
|
|
488
500
|
|
489
501
|
def log_level
|
490
502
|
@parser.on_tail('-v', '--verbose', 'Increase verbosity') do
|
503
|
+
@options[:log_level] ||= 0
|
491
504
|
@options[:log_level] -= 1
|
492
505
|
end
|
493
506
|
end
|
@@ -505,19 +518,28 @@ module Client
|
|
505
518
|
exit
|
506
519
|
end
|
507
520
|
end
|
521
|
+
|
522
|
+
def parse_positional_arguments!
|
523
|
+
@options[:host] = ARGV.shift or raise OptionParser::MissingArgument, 'hostname'
|
524
|
+
return if (given_port = ARGV.shift&.to_i).nil?
|
525
|
+
|
526
|
+
@options[:port] = validated_port(given_port).to_i
|
527
|
+
end
|
508
528
|
end
|
509
529
|
# class ArgumentsParser
|
510
530
|
|
511
|
-
# rubocop: disable Metrics/AbcSize
|
512
531
|
def parse_arguments(arguments_parser = ArgumentsParser.new)
|
513
532
|
arguments_parser.parser.parse!(ARGV)
|
514
|
-
arguments_parser.
|
515
|
-
arguments_parser.options[:port] = validated_port(v).to_i unless (v = ARGV.shift).nil?
|
533
|
+
arguments_parser.parse_positional_arguments!
|
516
534
|
arguments_parser.options
|
517
|
-
rescue OptionParser::
|
535
|
+
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption,
|
536
|
+
OptionParser::MissingArgument, OptionParser::NeedlessArgument => e
|
537
|
+
puts e.message
|
538
|
+
puts parser
|
539
|
+
exit
|
540
|
+
rescue OptionParser::AmbiguousOption => e
|
518
541
|
abort e.message
|
519
542
|
end
|
520
|
-
# rubocop: enable Metrics/AbcSize
|
521
543
|
end
|
522
544
|
# module Client
|
523
545
|
|
@@ -538,12 +560,12 @@ end
|
|
538
560
|
|
539
561
|
# The Client module
|
540
562
|
module Client
|
541
|
-
# The
|
542
|
-
class
|
563
|
+
# The Console class
|
564
|
+
class Console
|
543
565
|
def message_received(ctx, message)
|
544
566
|
log.trace "##{__method__} channel: #{ctx.channel}, message: #{message}"
|
545
567
|
log.debug "Received message: #{message}"
|
546
|
-
|
568
|
+
$stdout.print message unless message.nil?
|
547
569
|
end
|
548
570
|
end
|
549
571
|
end
|
@@ -557,8 +579,7 @@ module Client
|
|
557
579
|
# rubocop: disable Metrics/MethodLength
|
558
580
|
def main(args = parse_arguments)
|
559
581
|
Logging.log_level = args[:log_level]
|
560
|
-
|
561
|
-
::TCP::Client.new(args, *handlers)
|
582
|
+
::TCP::Client.new(args, ::Client::Console.new, ::Client::Monitor.new)
|
562
583
|
rescue Interrupt => e
|
563
584
|
warn format(InterruptTemplate, class: e.class)
|
564
585
|
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
|
-
FrameDecoderBufferBytesSize = 8192
|
35
|
-
# The encoder and decoder are sharable. If they were not, then
|
36
|
-
# constant definitions could not be used.
|
37
|
-
Decoder = StringDecoder.new
|
38
|
-
Encoder = StringEncoder.new
|
39
|
-
attr_accessor :user_handlers
|
33
|
+
attr_accessor :decoder, :encoder, :user_handlers
|
40
34
|
attr_reader :options
|
41
35
|
|
42
|
-
def initialize(options = {})
|
36
|
+
def initialize(channel_group, options = {})
|
43
37
|
super()
|
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,13 +49,21 @@ 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
|
-
pipeline.addLast(
|
54
|
+
pipeline.addLast(default_handler)
|
55
|
+
end
|
56
|
+
|
57
|
+
def frame_decoder
|
58
|
+
DelimiterBasedFrameDecoder.new(@options[:max_frame_length], @options[:delimiter])
|
59
|
+
end
|
60
|
+
|
61
|
+
def default_handler
|
62
|
+
@default_handler ||= ::Server::ModularHandler.new(@channel_group)
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_listener(listener)
|
66
|
+
default_handler.add_listener(listener)
|
62
67
|
end
|
63
68
|
|
64
69
|
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
|
@@ -20,9 +20,11 @@ require_relative 'shutdown_hook'
|
|
20
20
|
module Server
|
21
21
|
java_import Java::io.netty.bootstrap.ServerBootstrap
|
22
22
|
java_import Java::io.netty.channel.ChannelOption
|
23
|
+
java_import Java::io.netty.channel.group.DefaultChannelGroup
|
23
24
|
java_import Java::io.netty.channel.nio.NioEventLoopGroup
|
24
25
|
java_import Java::io.netty.handler.logging.LogLevel
|
25
26
|
java_import Java::io.netty.handler.logging.LoggingHandler
|
27
|
+
java_import Java::io.netty.util.concurrent.GlobalEventExecutor
|
26
28
|
|
27
29
|
# The InstanceMethods module
|
28
30
|
module InstanceMethods
|
@@ -41,7 +43,7 @@ module Server
|
|
41
43
|
end
|
42
44
|
|
43
45
|
def channel_initializer
|
44
|
-
@channel_initializer ||= ::Server::ChannelInitializer.new(@options)
|
46
|
+
@channel_initializer ||= ::Server::ChannelInitializer.new(channel_group, @options)
|
45
47
|
end
|
46
48
|
|
47
49
|
def boss_group
|
@@ -52,6 +54,10 @@ module Server
|
|
52
54
|
@worker_group ||= NioEventLoopGroup.new
|
53
55
|
end
|
54
56
|
|
57
|
+
def channel_group
|
58
|
+
@channel_group ||= DefaultChannelGroup.new('server_channels', GlobalEventExecutor::INSTANCE)
|
59
|
+
end
|
60
|
+
|
55
61
|
def logging_handler
|
56
62
|
@logging_handler ||= LoggingHandler.new(LogLevel::INFO)
|
57
63
|
end
|
@@ -60,7 +66,7 @@ module Server
|
|
60
66
|
# rubocop: disable Metrics/MethodLength
|
61
67
|
def run(port = @options[:port])
|
62
68
|
channel = bootstrap.bind(port).sync().channel()
|
63
|
-
|
69
|
+
channel_group.add(channel)
|
64
70
|
::Server::ShutdownHook.new(self)
|
65
71
|
log.info "Listening on #{channel.local_address}"
|
66
72
|
channel.closeFuture().sync()
|
@@ -77,8 +83,8 @@ module Server
|
|
77
83
|
# rubocop: enable Metrics/MethodLength
|
78
84
|
|
79
85
|
def shutdown
|
80
|
-
|
81
|
-
|
86
|
+
channel_group.disconnect().awaitUninterruptibly()
|
87
|
+
channel_group.close().awaitUninterruptibly()
|
82
88
|
end
|
83
89
|
|
84
90
|
def stop
|
@@ -91,7 +97,7 @@ module Server
|
|
91
97
|
end
|
92
98
|
|
93
99
|
def add_listener(listener)
|
94
|
-
|
100
|
+
channel_initializer.add_listener(listener)
|
95
101
|
end
|
96
102
|
end
|
97
103
|
# module ServerInstanceMethods
|
data/lib/server/listenable.rb
CHANGED
@@ -22,17 +22,19 @@ module Server
|
|
22
22
|
|
23
23
|
def add_listener(listener)
|
24
24
|
listeners << listener
|
25
|
+
ensure
|
26
|
+
log.trace "Listeners: #{listeners}"
|
25
27
|
end
|
26
28
|
|
27
29
|
def remove_listener(listener)
|
28
30
|
listeners.delete(listener)
|
29
31
|
end
|
30
32
|
|
31
|
-
def notify(
|
32
|
-
return if listeners.empty?
|
33
|
-
log.trace "Notifying listeners (#{listeners}) of message: #{message}"
|
33
|
+
def notify(event, *args)
|
34
34
|
listeners.each do |listener|
|
35
|
-
|
35
|
+
next unless listener.respond_to?(event.to_sym)
|
36
|
+
log.trace "Notifying listener #{listener} of event: #{event}"
|
37
|
+
listener.send(event.to_sym, *args)
|
36
38
|
end
|
37
39
|
end
|
38
40
|
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
|
@@ -18,19 +18,16 @@ require_relative 'listenable'
|
|
18
18
|
# The Server module
|
19
19
|
module Server
|
20
20
|
java_import Java::io.netty.channel.SimpleChannelInboundHandler
|
21
|
-
java_import Java::io.netty.channel.group.DefaultChannelGroup
|
22
|
-
java_import Java::io.netty.util.concurrent.GlobalEventExecutor
|
23
|
-
|
24
|
-
# rubocop: disable Style/IfUnlessModifier
|
25
|
-
unless defined?(::Server::Channels)
|
26
|
-
Channels = DefaultChannelGroup.new('channels', GlobalEventExecutor::INSTANCE)
|
27
|
-
end
|
28
|
-
# rubocop: enable Style/IfUnlessModifier
|
29
21
|
|
30
22
|
# The ModularHandler class notifies listeners about events.
|
31
23
|
class ModularHandler < SimpleChannelInboundHandler
|
32
24
|
include ::Server::Listenable
|
33
25
|
|
26
|
+
def initialize(channel_group)
|
27
|
+
super()
|
28
|
+
@channel_group = channel_group
|
29
|
+
end
|
30
|
+
|
34
31
|
def isSharable
|
35
32
|
true
|
36
33
|
end
|
@@ -48,8 +45,8 @@ module Server
|
|
48
45
|
end
|
49
46
|
|
50
47
|
def channelActive(ctx)
|
51
|
-
log.
|
52
|
-
|
48
|
+
log.info "##{__method__} channel: #{ctx.channel}"
|
49
|
+
@channel_group.add(ctx.channel)
|
53
50
|
notify :channel_active, ctx
|
54
51
|
super(ctx)
|
55
52
|
end
|
@@ -107,10 +104,10 @@ module Server
|
|
107
104
|
super(ctx, cause) if listeners.nil? || listeners.empty?
|
108
105
|
end
|
109
106
|
|
110
|
-
|
107
|
+
IdentifierTemplate = '#<%<class>s:0x%<id>s>'.freeze
|
111
108
|
|
112
109
|
def to_s
|
113
|
-
format(
|
110
|
+
format(IdentifierTemplate, class: self.class.name, id: object_id.to_s(16))
|
114
111
|
end
|
115
112
|
alias inspect to_s
|
116
113
|
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.8
|
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-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|