kinetic-ruby 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|