kinetic-ruby 0.6.0 → 0.6.1
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/Rakefile +8 -8
- data/lib/kinetic-ruby.rb +8 -2
- data/lib/kinetic_constants +2 -0
- data/lib/kinetic_constants.rb +4 -0
- data/lib/kinetic_logger.rb +19 -3
- data/lib/kinetic_pdu.rb +86 -20
- data/lib/kinetic_proto.rb +43 -13
- data/lib/kinetic_server.rb +205 -76
- data/lib/version.rb +1 -1
- data/lib/version.rb.erb +1 -1
- data/tasks/kinetic-ruby.rake +5 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f7c34a6be5838b4d0dfb7fdee59c5e4ce5678da
|
4
|
+
data.tar.gz: be5746db50b0fea02b0f89c91279b7dc74ea0771
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87433d1fa160efdac86af6f9e2eb22e43fbcad3f2def7fc3f309559b319e8a0acbdc6b9d10db007d102fc5e961a3ad92b81f6d049d05da33f7d9d3fc540a2bcc
|
7
|
+
data.tar.gz: 41389bc4b0bbc3cef02b0793e930767796669a09865dee1295c2d3b585fd12317a333035bd5df297c5ab8cd09428bd713fed174643e240e61cdadcd2ad7d9402
|
data/Rakefile
CHANGED
@@ -11,18 +11,17 @@ namespace :test do
|
|
11
11
|
desc "Test Kinetic Ruby server"
|
12
12
|
task :server => 'kinetic:server:start' do
|
13
13
|
report "Started Kinetic Ruby server!"
|
14
|
-
sleep 2.0
|
15
14
|
client = Thread.new do
|
16
|
-
|
17
|
-
|
15
|
+
addr = $kinetic_server.host + ':' + $kinetic_server.port.to_s
|
16
|
+
report "Connecting test client to #{addr}\n"
|
17
|
+
client = TCPSocket.new('localhost', KineticRuby::DEFAULT_KINETIC_PORT)
|
18
|
+
raise "Failed connecting to server!" unless client
|
19
|
+
report "Connected to server!"
|
20
|
+
client.close
|
18
21
|
end
|
19
|
-
|
20
|
-
raise "Failed connecting a client to Kinetic Ruby server!" unless $kinetic_server.connected
|
21
|
-
client.exit
|
22
|
-
client.join(2.0)
|
22
|
+
client.join 5.0
|
23
23
|
$kinetic_server.shutdown unless $kinetic_server.nil?
|
24
24
|
$kinetic_server = nil
|
25
|
-
sleep 2.0
|
26
25
|
report "Kinetic Ruby server test successful!"
|
27
26
|
end
|
28
27
|
end
|
@@ -105,6 +104,7 @@ end
|
|
105
104
|
|
106
105
|
def report(msg='', banner=false)
|
107
106
|
$stderr.flush
|
107
|
+
$stdout.flush
|
108
108
|
if banner
|
109
109
|
len = msg.length
|
110
110
|
msg = "\n#{msg}\n#{'-'*len}"
|
data/lib/kinetic-ruby.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# Preload
|
1
|
+
# Preload library files
|
2
2
|
require 'fileutils'
|
3
|
-
|
3
|
+
require 'socket'
|
4
4
|
FileUtils.cd(File.dirname(__FILE__)) do
|
5
5
|
Dir['./**/*.rb'].each do |f|
|
6
6
|
mod = f.sub(/.rb/, '')
|
@@ -9,11 +9,17 @@ FileUtils.cd(File.dirname(__FILE__)) do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module KineticRuby
|
12
|
+
|
13
|
+
# Rake extensions for kinetic-ruby
|
12
14
|
module Rake
|
15
|
+
# Call from Rakefile to load kinetic-ruby Rake tasks (after requiring this file)
|
13
16
|
def self.load_tasks
|
14
17
|
Dir["#{File.dirname(__FILE__)}/../tasks/**/*.rake"].each do |tasks|
|
15
18
|
load tasks
|
16
19
|
end
|
17
20
|
end
|
21
|
+
|
22
|
+
# Autoload rake tasks if Rake already loaded
|
23
|
+
load_tasks if defined?(Rake)
|
18
24
|
end
|
19
25
|
end
|
data/lib/kinetic_logger.rb
CHANGED
@@ -12,6 +12,7 @@ module KineticRuby
|
|
12
12
|
def initialize(log_level=LOG_LEVEL_INFO, stream=$stdout)
|
13
13
|
set_level log_level
|
14
14
|
@stream = stream
|
15
|
+
@prefix = ''
|
15
16
|
end
|
16
17
|
|
17
18
|
def level=(log_level)
|
@@ -22,6 +23,14 @@ module KineticRuby
|
|
22
23
|
@level.dup
|
23
24
|
end
|
24
25
|
|
26
|
+
def prefix=(msg)
|
27
|
+
@prefix = msg if msg
|
28
|
+
end
|
29
|
+
|
30
|
+
def prefix
|
31
|
+
@prefix.dup if @prefix
|
32
|
+
end
|
33
|
+
|
25
34
|
def log_info(msg='', banner=nil)
|
26
35
|
log_message(msg, banner) if @level >= LOG_LEVEL_INFO
|
27
36
|
end
|
@@ -38,6 +47,14 @@ module KineticRuby
|
|
38
47
|
end
|
39
48
|
alias logv log_verbose
|
40
49
|
|
50
|
+
def log_exception(exception, desc=nil, level=LOG_LEVEL_ERROR)
|
51
|
+
log_err(desc) if (desc && !desc.empty?)
|
52
|
+
log_err "Exception #{exception.class} '#{exception.message}' occured at:"
|
53
|
+
exception.backtrace.each do |l|
|
54
|
+
log_err " #{l}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
41
58
|
private
|
42
59
|
|
43
60
|
def set_level(log_level)
|
@@ -48,10 +65,9 @@ module KineticRuby
|
|
48
65
|
@level = log_level
|
49
66
|
end
|
50
67
|
|
51
|
-
LOG_BANNER = "\n----------------------------------------"
|
52
|
-
|
53
68
|
def log_message(msg, banner)
|
54
|
-
msg
|
69
|
+
msg = @prefix + msg if (@prefix && !@prefix.empty?)
|
70
|
+
msg += "\n" + @prefix + ('-'*40) if (banner && msg && !msg.empty?)
|
55
71
|
@stream.puts msg
|
56
72
|
@stream.flush
|
57
73
|
end
|
data/lib/kinetic_pdu.rb
CHANGED
@@ -4,56 +4,122 @@ module KineticRuby
|
|
4
4
|
|
5
5
|
class PDU
|
6
6
|
|
7
|
+
HEADER_LENGTH = 1 + 4 + 4 # version_prefix + protobuf_length + value_length
|
8
|
+
|
7
9
|
def initialize(logger, raw=nil)
|
8
10
|
raise "Invalid logger specified!" unless logger
|
9
11
|
@logger = logger
|
10
|
-
|
12
|
+
@header = nil
|
13
|
+
@protobuf = nil
|
14
|
+
@value = nil
|
15
|
+
parse(raw) unless raw.nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
def complete?
|
19
|
+
complete = false
|
20
|
+
if @header && @protobuf
|
21
|
+
if @header['valueLength'] == 0
|
22
|
+
complete = true
|
23
|
+
elsif @value && (@value.length >= @header['valueLength'])
|
24
|
+
complete = true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
return complete
|
11
28
|
end
|
12
29
|
|
13
30
|
def parse(raw)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
31
|
+
if parse_header(raw)
|
32
|
+
@logger.log 'PDU Header:'
|
33
|
+
@header.each_pair{|k,v| @logger.log " #{k}: #{v}"}
|
34
|
+
|
35
|
+
if parse_protobuf(raw)
|
36
|
+
@logger.log 'PDU Protobuf:'
|
37
|
+
@protobuf.to_yaml.each_line{|l| @logger.log(' ' + l)}
|
38
|
+
|
39
|
+
if @header['valueLength'] > 0 && parse_value(raw)
|
40
|
+
@logger.log 'PDU Value:'
|
41
|
+
@logger.log " #{@value.to_s}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
return complete?
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.valid_header?(raw)
|
50
|
+
(
|
51
|
+
!raw.nil? &&
|
52
|
+
raw.length >= HEADER_LENGTH &&
|
53
|
+
raw[0] == KineticRuby::Proto::VERSION_PREFIX
|
54
|
+
)
|
18
55
|
end
|
19
56
|
|
20
57
|
def length
|
21
58
|
len = 0
|
22
59
|
len += HEADER_LENGTH if @header
|
23
|
-
len += @header[
|
24
|
-
len += @header[
|
60
|
+
len += @header['protobufLength'] if @protobuf
|
61
|
+
len += @header['valueLength'] if @value
|
25
62
|
return len
|
26
63
|
end
|
27
64
|
alias size length
|
28
65
|
|
29
|
-
|
66
|
+
def dump
|
67
|
+
return unless complete?
|
68
|
+
|
69
|
+
@logger.log('PDU Content', true)
|
30
70
|
|
31
|
-
|
71
|
+
# Log the header
|
72
|
+
@logger.log " header:\n" +
|
73
|
+
" version_prefix: #{@header['versionPrefix']}\n" +
|
74
|
+
" protobuf_length: #{@header['protobufLength']}\n" +
|
75
|
+
" value_length: #{@header['valueLength']}\n"
|
76
|
+
|
77
|
+
# Log the protobuf
|
78
|
+
@logger.log " protobuf:\n" + Proto.to_yaml(@protobuf, ' ')
|
79
|
+
|
80
|
+
# Log the value payload
|
81
|
+
@logger.log " value: (#{value.length} bytes)\n"
|
82
|
+
if @logger.level >= Logger::LOG_LEVEL_VERBOSE
|
83
|
+
val = @value.dup
|
84
|
+
val_string = ''
|
85
|
+
while !val.empty?
|
86
|
+
val_string += " #{val.slice!(i,8)}\n"
|
87
|
+
end
|
88
|
+
@logger.logv val_string
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
32
93
|
|
33
94
|
def parse_header(raw)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
95
|
+
if (!raw || raw.length <= HEADER_LENGTH)
|
96
|
+
@logger.log "Haven't recevived the full PDU header yet..."
|
97
|
+
@logger.log " header: raw: #{raw.inspect}" unless (!raw || raw.empty?)
|
98
|
+
return nil
|
99
|
+
end
|
100
|
+
@header = {
|
101
|
+
'versionPrefix' => raw[0],
|
102
|
+
'protobufLength' => parse_nbo_int32(raw.byteslice(1..4)),
|
103
|
+
'valueLength' => parse_nbo_int32(raw.byteslice(5..8)),
|
39
104
|
}
|
40
105
|
end
|
41
106
|
|
42
107
|
def parse_protobuf(raw)
|
43
|
-
min_length = (HEADER_LENGTH + @header[
|
108
|
+
min_length = (HEADER_LENGTH + @header['protobufLength'])
|
44
109
|
return nil unless (@header && raw.length >= min_length)
|
45
110
|
start_idx = HEADER_LENGTH
|
46
111
|
end_idx = HEADER_LENGTH + min_length - 1
|
47
112
|
protobuf = raw.byteslice(start_idx, end_idx)
|
48
|
-
Seagate::Kinetic::Message.decode(protobuf)
|
113
|
+
@protobuf = Seagate::Kinetic::Message.decode(protobuf)
|
49
114
|
end
|
50
115
|
|
51
116
|
def parse_value(raw)
|
52
|
-
|
117
|
+
preamble_length = HEADER_LENGTH + @header['protobufLength']
|
118
|
+
min_length = preamble_length + 1
|
119
|
+
full_length = preamble_length + @header['valueLength']
|
53
120
|
return nil unless (@header && @protobuf && raw.length >= min_length)
|
54
|
-
|
55
|
-
|
56
|
-
@value = raw.byteslice(start_idx, end_idx)
|
121
|
+
end_idx = preamble_length + @header['protobufLength'] - 1
|
122
|
+
@value = raw.byteslice(preamble_length, end_idx)
|
57
123
|
end
|
58
124
|
|
59
125
|
def parse_nbo_int32(data)
|
data/lib/kinetic_proto.rb
CHANGED
@@ -12,19 +12,43 @@ module KineticRuby
|
|
12
12
|
@message_in = nil
|
13
13
|
end
|
14
14
|
|
15
|
-
def decode(buf)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
def self.decode(buf, indent=0, logger=nil)
|
16
|
+
msg = Seagate::Kinetic::Message.decode(buf)
|
17
|
+
|
18
|
+
# Log the decoded protobuf
|
19
|
+
if logger
|
20
|
+
orig_prefix = logger.prefix
|
21
|
+
add_prefix = (indent.class == Fixnum) ? (' '*indent) : indent
|
22
|
+
add_prefix = ' ' * indent
|
23
|
+
logger.prefix = logger.prefix + add_prefix
|
24
|
+
logger.log 'message:'
|
25
|
+
logger.prefix = logger.prefix + ' '
|
26
|
+
self.to_yaml(msg).each_line{|line| logger.log(line) }
|
27
|
+
logger.prefix = orig_prefix
|
28
|
+
logger.log
|
29
|
+
end
|
30
|
+
|
31
|
+
return msg
|
32
|
+
end
|
33
|
+
|
34
|
+
def decode(buf, indent=0)
|
35
|
+
@message_in = Proto.decode(buf, indent, @logger)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.to_yaml(msg)
|
39
|
+
yaml = "message:\n"
|
40
|
+
msg.to_yaml.each_line{|line| yaml += " #{line}"}
|
41
|
+
return yaml
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_yaml(msg)
|
45
|
+
self.to_yaml(msg)
|
22
46
|
end
|
23
47
|
|
24
48
|
def test_encode
|
25
49
|
pb = Seagate::Kinetic::Message.new
|
26
|
-
|
27
|
-
@logger.log("
|
50
|
+
@logger.log
|
51
|
+
@logger.log("#{pb.class} - Encoding", true)
|
28
52
|
pb.hmac = '0123456789ABCDEF0123'
|
29
53
|
pb.command = Seagate::Kinetic::Message::Command.new(
|
30
54
|
header: Seagate::Kinetic::Message::Header.new(
|
@@ -47,14 +71,16 @@ module KineticRuby
|
|
47
71
|
|
48
72
|
@logger.log_verbose ' fields:'
|
49
73
|
pb.fields.sort.each{|f| @logger.log_verbose(" #{f}")}
|
50
|
-
@logger.log_verbose " hmac
|
74
|
+
@logger.log_verbose " hmac:"
|
75
|
+
@logger.log_verbose " #{pb.hmac}"
|
51
76
|
|
52
77
|
@logger.log ' command:'
|
53
78
|
pb.command.to_yaml.each_line{|l| @logger.log(" #{l}")}
|
54
79
|
@logger.log ' encoded:'
|
55
80
|
@logger.log ' Length: ' + encoded.length.to_s + ' bytes'
|
56
81
|
|
57
|
-
@logger.log_verbose " Raw
|
82
|
+
@logger.log_verbose " Raw:"
|
83
|
+
@logger.log_verbose " #{encoded.inspect}"
|
58
84
|
@logger.log_verbose " Content:"
|
59
85
|
encoded.to_yaml.each_line{|line| @logger.log_verbose " #{line}"}
|
60
86
|
|
@@ -65,10 +91,14 @@ module KineticRuby
|
|
65
91
|
|
66
92
|
def test_kinetic_proto
|
67
93
|
msg = test_encode
|
68
|
-
|
94
|
+
|
95
|
+
@logger.log
|
96
|
+
@logger.log("Decoded Message", true)
|
97
|
+
decode(msg, 2)
|
69
98
|
|
70
99
|
if @message_in != @message_out
|
71
|
-
@logger.log_err "Inbound/outbound messages do not match
|
100
|
+
@logger.log_err "Inbound/outbound messages do not match!"
|
101
|
+
@logger.log_err
|
72
102
|
raise "\nKinetic Protocol message roundtrip FAILED!\n\n"
|
73
103
|
end
|
74
104
|
|
data/lib/kinetic_server.rb
CHANGED
@@ -1,11 +1,144 @@
|
|
1
|
-
require 'socket'
|
2
|
-
|
3
1
|
$kinetic_server = nil
|
2
|
+
require_relative 'kinetic_constants'
|
4
3
|
|
5
4
|
module KineticRuby
|
6
5
|
|
7
|
-
|
8
|
-
|
6
|
+
class Client
|
7
|
+
def initialize(socket, addr_info, logger)
|
8
|
+
@socket = socket
|
9
|
+
@addr = addr_info
|
10
|
+
@logger = logger
|
11
|
+
end
|
12
|
+
|
13
|
+
# Shutdown a client socket
|
14
|
+
def close
|
15
|
+
return unless @socket
|
16
|
+
@logger.log "Client socket #{@socket.inspect} disconnected!"
|
17
|
+
@socket.close
|
18
|
+
@socket = nil
|
19
|
+
@logger.logv "Client #{@socket.inspect} connection shutdown successfully"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return formatted address '<address>:<port>' for the socket
|
23
|
+
def self.address(socket)
|
24
|
+
return nil unless socket
|
25
|
+
raise "Socket appears to be disconnected!" if !socket.remote_address
|
26
|
+
"#{socket.remote_address.ip_address}:#{socket.remote_address.ip_port}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return formatted address '<address>:<port>' of the client
|
30
|
+
def address
|
31
|
+
Client.address(@socket)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Wait to receive data from the client
|
35
|
+
# @param max_len Maximum number of bytes to receive
|
36
|
+
# @returns Received data (length <= max_len) or nil upon failure
|
37
|
+
def receive(max_len=nil)
|
38
|
+
max_len ||= 1024
|
39
|
+
|
40
|
+
begin
|
41
|
+
data = @socket.recv(max_len)
|
42
|
+
rescue IO::WaitReadable
|
43
|
+
@logger.logv 'Retrying receive...'
|
44
|
+
IO.select([@socket])
|
45
|
+
retry
|
46
|
+
rescue Exception => e
|
47
|
+
if e.class != 'IOError' && e.message != 'closed stream'
|
48
|
+
@logger.log_exception(e, 'EXCEPTION during receive!')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if (data.nil? || data.empty?)
|
53
|
+
@logger.log "Client #{@socket.inspect} disconnected!"
|
54
|
+
data = ''
|
55
|
+
else
|
56
|
+
@logger.log "Received #{data.length} bytes"
|
57
|
+
end
|
58
|
+
|
59
|
+
return data
|
60
|
+
end
|
61
|
+
alias recv receive # provide 'standard' socket method as well
|
62
|
+
|
63
|
+
# Send data to the client
|
64
|
+
def send(data)
|
65
|
+
@socket.write(data)
|
66
|
+
end
|
67
|
+
alias write send # provide 'standard' socket method as well
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
class ClientProvider
|
72
|
+
def initialize(host, port, logger)
|
73
|
+
@host = host
|
74
|
+
@port = port
|
75
|
+
@logger = logger
|
76
|
+
@server = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
# @brief Listen for connection
|
80
|
+
# @return Returns a Client upon connection, nil upon failure
|
81
|
+
def accept
|
82
|
+
client = nil
|
83
|
+
@server ||= TCPServer.new(@host, @port)
|
84
|
+
|
85
|
+
begin
|
86
|
+
client = Client.new(@server.accept)
|
87
|
+
rescue Exception => e
|
88
|
+
@logger.log_exception(e, 'EXCEPTION during accept!')
|
89
|
+
end
|
90
|
+
|
91
|
+
@logger.log 'Client dropped off!' if client.nil?
|
92
|
+
|
93
|
+
return client
|
94
|
+
end
|
95
|
+
|
96
|
+
# @brief Shutdown the socket server
|
97
|
+
def shutdown
|
98
|
+
return if @server.nil?
|
99
|
+
@server.close
|
100
|
+
@server = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# @brief Starts a TCP socket server and yields block for each client (sequential)
|
104
|
+
# @param host Host name or IPv4/6 address
|
105
|
+
# @param port Port to listen on
|
106
|
+
# @param logger Logger to output to
|
107
|
+
def self.each_client(host, port, logger)
|
108
|
+
raise "No block supplied!" unless block_given?
|
109
|
+
logger.log "Listening for clients..."
|
110
|
+
|
111
|
+
# Service clients, one at a time (for now at least)
|
112
|
+
begin
|
113
|
+
Socket.tcp_server_loop(host, port) do |socket, addr|
|
114
|
+
|
115
|
+
logger.log "New client connected on socket #{socket.inspect}"
|
116
|
+
|
117
|
+
client = Client.new(socket, addr, logger)
|
118
|
+
raise "Failed to connect to client!" unless client
|
119
|
+
logger.log "Connected to #{client.address}"
|
120
|
+
|
121
|
+
begin
|
122
|
+
# Execute the supplied block with the connected client
|
123
|
+
yield(client, logger)
|
124
|
+
logger.log "Done servicing client #{client.address}"
|
125
|
+
ensure
|
126
|
+
logger.log "Closing client socket..."
|
127
|
+
# Make sure the client gets closed, since tcp_server_loop does NOT!
|
128
|
+
client.close
|
129
|
+
logger.log "Client socket shutdown!"
|
130
|
+
end
|
131
|
+
logger.log "Done with client #{client.address}"
|
132
|
+
end
|
133
|
+
|
134
|
+
rescue Exception => e
|
135
|
+
logger.log_exception(e, 'EXCEPTION during listen!')
|
136
|
+
end
|
137
|
+
|
138
|
+
logger.log "Done listening for clients!"
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
9
142
|
|
10
143
|
class Server
|
11
144
|
|
@@ -14,17 +147,11 @@ module KineticRuby
|
|
14
147
|
def initialize(port = DEFAULT_KINETIC_PORT, log_level=Logger::LOG_LEVEL_INFO)
|
15
148
|
@host = 'localhost'
|
16
149
|
@port ||= DEFAULT_KINETIC_PORT
|
17
|
-
raise "Invalid
|
150
|
+
raise "Invalid server port specified: #{port}" if !@port || @port < 0
|
18
151
|
@logger = Logger.new(log_level)
|
19
|
-
@
|
20
|
-
@server = nil
|
152
|
+
@logger.prefix = 'KineticSim: '
|
21
153
|
@worker = nil
|
22
|
-
@
|
23
|
-
@logger.log "Kinetic Ruby test device server started!"
|
24
|
-
end
|
25
|
-
|
26
|
-
def connected
|
27
|
-
!@server.nil?
|
154
|
+
@logger.log 'Kinetic device test server started!'
|
28
155
|
end
|
29
156
|
|
30
157
|
def report_buffer(buf)
|
@@ -46,103 +173,105 @@ module KineticRuby
|
|
46
173
|
end
|
47
174
|
|
48
175
|
def start
|
49
|
-
return unless @
|
50
|
-
|
51
|
-
@server = TCPServer.new(@host, @port)
|
176
|
+
return unless @worker.nil?
|
52
177
|
|
53
178
|
# Setup handler for signaled shutdown (via ctrl+c)
|
54
179
|
trap("INT") do
|
55
|
-
@logger.log "
|
180
|
+
@logger.log "INT triggered shutdown"
|
56
181
|
shutdown
|
57
182
|
end
|
58
183
|
|
59
|
-
# Create
|
184
|
+
# Create background thread for processing client requests
|
60
185
|
@worker = Thread.new do
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
begin
|
65
|
-
client, client_info = @server.accept
|
66
|
-
rescue Exception => e
|
67
|
-
@logger.log "Kinetic Test Server: EXCEPTION during accept!\n" +
|
68
|
-
" #{e.inspect}\n" +
|
69
|
-
" #{e.message}\n #{e.backtrace.join(" \n")}"
|
70
|
-
next if client.nil?
|
71
|
-
end
|
72
|
-
|
73
|
-
next if client.nil?
|
186
|
+
|
187
|
+
# Service client connections (sequentially)
|
188
|
+
ClientProvider.each_client(@host, @port, @logger) do |client|
|
74
189
|
|
75
|
-
@logger.log "Kinetic Test Server: Connected to #{client.inspect}"
|
76
190
|
request = ''
|
77
|
-
data = nil
|
78
191
|
pdu = nil
|
79
|
-
|
80
|
-
|
81
|
-
while !disconnect
|
82
|
-
begin
|
83
|
-
data = client.recv(1024)
|
84
|
-
rescue IO::WaitReadable
|
85
|
-
@logger.log("IO:WaitReadable");
|
86
|
-
IO.select([client])
|
87
|
-
retry
|
88
|
-
rescue Exception => e
|
89
|
-
@logger.logv "Kinetic Test Server: EXCEPTION during receive!\n" +
|
90
|
-
" #{e.inspect}\n" +
|
91
|
-
" #{e.message}\n #{e.backtrace.join(" \n")}"
|
92
|
-
disconnect = true
|
93
|
-
next
|
94
|
-
end
|
192
|
+
connected = true
|
193
|
+
raw_proto = nil
|
95
194
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
195
|
+
# Process requests while client available
|
196
|
+
while connected && (data = client.receive)
|
197
|
+
if data.nil? || data.empty?
|
198
|
+
connected = false
|
199
|
+
break
|
100
200
|
end
|
101
201
|
|
202
|
+
# Append received data to request for processing
|
203
|
+
request += data
|
204
|
+
|
102
205
|
# Incrementally parse PDU until complete
|
103
|
-
if
|
104
|
-
|
105
|
-
|
206
|
+
if PDU.valid_header? request
|
207
|
+
@logger.log 'Receiving a PDU...'
|
208
|
+
raw_pdu = request.bytes.map{|b| sprintf("%02X", b)}.join('')
|
209
|
+
@logger.log " request[#{request.length}]: #{raw_pdu}"
|
210
|
+
pdu ||= PDU.new(@logger)
|
211
|
+
if pdu.parse(request)
|
212
|
+
@logger.log "Received PDU successfully!"
|
213
|
+
else
|
214
|
+
@logger.log "Waiting on remainder of PDU..."
|
215
|
+
end
|
106
216
|
end
|
107
217
|
|
218
|
+
# Handle raw protobuf.. for tests
|
219
|
+
if raw_proto || request.match(/^\n/)
|
220
|
+
@logger.log "Appears to be a standalone protobuf incoming..."
|
221
|
+
pdu = nil
|
222
|
+
raw_proto ||= []
|
223
|
+
raw_proto += request.bytes
|
224
|
+
@logger.log " protobuf: (#{raw_proto.length} bytes)"
|
225
|
+
|
108
226
|
# Otherwise, handle custom test requests
|
109
|
-
|
227
|
+
elsif pdu.nil?
|
228
|
+
@logger.logv "Checking for custom request: '#{request}'"
|
110
229
|
request_match = request.match(/^read\((\d+)\)/)
|
111
230
|
if request_match
|
112
231
|
len = request_match[1].to_i
|
113
232
|
response = 'G'*len
|
114
|
-
@logger.log "
|
115
|
-
client.
|
233
|
+
@logger.log "Responding to 'read(#{len})' w/ '#{response}'"
|
234
|
+
client.send response
|
116
235
|
request = ''
|
236
|
+
pdu = nil
|
117
237
|
elsif request =~ /^readProto()/
|
118
|
-
response = @
|
119
|
-
@logger.log "
|
120
|
-
client.
|
238
|
+
response = Proto.new(@logger).test_encode
|
239
|
+
@logger.log "Responding to 'read(#{len})' w/ dummy protobuf (#{response.length} bytes)"
|
240
|
+
client.send response
|
121
241
|
request = ''
|
242
|
+
pdu = nil
|
243
|
+
elsif request.match(/^read/) && data.length < 7
|
244
|
+
@logger.log "no command match for request: '#{request}' (...yet)";
|
245
|
+
else
|
246
|
+
@logger.log "Unknown request! Aborting..."
|
247
|
+
request = ''
|
248
|
+
pdu = nil
|
249
|
+
connected = false
|
122
250
|
end
|
123
251
|
end
|
124
252
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
end
|
130
|
-
|
253
|
+
end #request service loop pass
|
254
|
+
|
255
|
+
@logger.log "Disconnecting from client..."
|
256
|
+
|
257
|
+
end #client connection
|
258
|
+
|
259
|
+
@logger.log "Client listener shutting down..."
|
260
|
+
end #worker thread
|
261
|
+
|
262
|
+
@logger.log "Listener shutdown successfully!"
|
263
|
+
|
131
264
|
end
|
132
265
|
|
133
266
|
def shutdown
|
134
|
-
return if @
|
135
|
-
@logger.log
|
267
|
+
return if @worker.nil?
|
268
|
+
@logger.log 'shutting down...'
|
136
269
|
if @worker
|
137
270
|
@worker.exit
|
138
|
-
@worker.join
|
271
|
+
@worker.join 2.0
|
139
272
|
@worker = nil
|
140
273
|
end
|
141
|
-
|
142
|
-
@server.close
|
143
|
-
@server = nil
|
144
|
-
end
|
145
|
-
@logger.log "Kinetic Test Server: shutdown complete"
|
274
|
+
@logger.log 'shutdown complete'
|
146
275
|
end
|
147
276
|
|
148
277
|
end
|
data/lib/version.rb
CHANGED
data/lib/version.rb.erb
CHANGED
data/tasks/kinetic-ruby.rake
CHANGED
@@ -21,8 +21,11 @@ namespace :kinetic do
|
|
21
21
|
|
22
22
|
desc "Shutdown Kinetic Test Server"
|
23
23
|
task :shutdown do
|
24
|
-
|
25
|
-
|
24
|
+
if $kinetic_server
|
25
|
+
$kinetic_server.shutdown
|
26
|
+
sleep 5.0
|
27
|
+
$kinetic_server = nil
|
28
|
+
end
|
26
29
|
end
|
27
30
|
|
28
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kinetic-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -64,6 +64,8 @@ files:
|
|
64
64
|
- Rakefile
|
65
65
|
- LICENSE
|
66
66
|
- lib/kinetic-ruby.rb
|
67
|
+
- lib/kinetic_constants
|
68
|
+
- lib/kinetic_constants.rb
|
67
69
|
- lib/kinetic_logger.rb
|
68
70
|
- lib/kinetic_pdu.rb
|
69
71
|
- lib/kinetic_proto.rb
|