stomp 1.2.4 → 1.2.5
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.
- data/CHANGELOG.rdoc +11 -0
- data/README.rdoc +38 -26
- data/Rakefile +3 -0
- data/bin/catstomp +34 -34
- data/bin/stompcat +36 -36
- data/examples/client11_ex1.rb +64 -55
- data/examples/client11_putget1.rb +47 -35
- data/examples/conn11_ex1.rb +59 -51
- data/examples/conn11_ex2.rb +59 -50
- data/examples/conn11_hb1.rb +35 -26
- data/examples/consumer.rb +25 -12
- data/examples/get11conn_ex1.rb +97 -89
- data/examples/get11conn_ex2.rb +55 -47
- data/examples/logexamp.rb +66 -52
- data/examples/logexamp_ssl.rb +66 -52
- data/examples/publisher.rb +21 -10
- data/examples/put11conn_ex1.rb +35 -24
- data/examples/putget11_rh1.rb +66 -56
- data/examples/slogger.rb +65 -52
- data/examples/ssl_uc1.rb +24 -13
- data/examples/ssl_uc1_ciphers.rb +28 -15
- data/examples/ssl_uc2.rb +26 -16
- data/examples/ssl_uc2_ciphers.rb +31 -18
- data/examples/ssl_uc3.rb +25 -14
- data/examples/ssl_uc3_ciphers.rb +31 -18
- data/examples/ssl_uc4.rb +26 -15
- data/examples/ssl_uc4_ciphers.rb +32 -19
- data/examples/ssl_ucx_default_ciphers.rb +25 -12
- data/examples/stomp11_common.rb +16 -15
- data/examples/topic_consumer.rb +23 -10
- data/examples/topic_publisher.rb +22 -8
- data/lib/client/utils.rb +116 -0
- data/lib/connection/heartbeats.rb +173 -0
- data/lib/connection/netio.rb +322 -0
- data/lib/connection/utf8.rb +294 -0
- data/lib/connection/utils.rb +104 -0
- data/lib/stomp/client.rb +127 -179
- data/lib/stomp/codec.rb +5 -2
- data/lib/stomp/connection.rb +109 -865
- data/lib/stomp/constants.rb +52 -33
- data/lib/stomp/errors.rb +56 -5
- data/lib/stomp/ext/hash.rb +4 -0
- data/lib/stomp/message.rb +49 -29
- data/lib/stomp/sslparams.rb +83 -71
- data/lib/stomp/version.rb +3 -1
- data/lib/stomp.rb +18 -9
- data/stomp.gemspec +58 -3
- data/test/test_client.rb +28 -1
- data/test/test_codec.rb +8 -2
- data/test/test_connection.rb +29 -0
- data/test/test_connection1p.rb +31 -16
- data/test/test_helper.rb +20 -3
- data/test/test_message.rb +8 -3
- data/test/test_ssl.rb +10 -4
- data/test/tlogger.rb +16 -15
- metadata +59 -4
data/examples/ssl_uc4_ciphers.rb
CHANGED
@@ -6,30 +6,43 @@
|
|
6
6
|
require "rubygems"
|
7
7
|
require "stomp"
|
8
8
|
#
|
9
|
+
# == SSL Use Case 4 - User Supplied Ciphers
|
10
|
+
#
|
9
11
|
# If you need your own ciphers list, this is how.
|
10
12
|
# Stomp's default list will work in many cases. If you need to use this, you
|
11
13
|
# will know it because SSL connect will fail. In that case, determining
|
12
14
|
# _what_ should be in the list is your responsibility.
|
13
15
|
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
|
16
|
+
class ExampleSSL4C
|
17
|
+
# Initialize.
|
18
|
+
def initialize
|
19
|
+
end
|
20
|
+
# Run example.
|
21
|
+
def run
|
22
|
+
ciphers_list = [["DHE-RSA-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["DHE-DSS-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["AES256-SHA", "TLSv1/SSLv3", 256, 256], ["EDH-RSA-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["EDH-DSS-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DHE-RSA-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["DHE-DSS-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["AES128-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-MD5", "TLSv1/SSLv3", 128, 128], ["EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56],
|
23
|
+
["DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EXP-EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-RC2-CBC-MD5", "TLSv1/SSLv3", 40, 128], ["EXP-RC4-MD5", "TLSv1/SSLv3", 40, 128]]
|
24
|
+
#
|
25
|
+
# SSL Use Case 4
|
26
|
+
#
|
27
|
+
ssl_opts = Stomp::SSLParams.new(:key_file => "/home/gmallard/sslwork/twocas_tj/clientCA/ClientTJ.key",
|
28
|
+
:cert_file => "/home/gmallard/sslwork/twocas_tj/clientCA/ClientTJ.crt",
|
29
|
+
:ts_files => "/home/gmallard/sslwork/twocas_tj/serverCA/ServerTJCA.crt",
|
30
|
+
:ciphers => ciphers_list)
|
31
|
+
#
|
32
|
+
hash = { :hosts => [
|
33
|
+
{:login => 'guest', :passcode => 'guest', :host => 'localhost', :port => 61612, :ssl => ssl_opts},
|
26
34
|
]
|
27
35
|
}
|
28
|
-
#
|
29
|
-
puts "Connect starts, SSL Use Case 4"
|
30
|
-
c = Stomp::Connection.new(hash)
|
31
|
-
puts "Connect completed"
|
32
|
-
puts "SSL Verify Result: #{ssl_opts.verify_result}"
|
33
|
-
# puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}"
|
34
|
-
c.disconnect
|
36
|
+
#
|
37
|
+
puts "Connect starts, SSL Use Case 4"
|
38
|
+
c = Stomp::Connection.new(hash)
|
39
|
+
puts "Connect completed"
|
40
|
+
puts "SSL Verify Result: #{ssl_opts.verify_result}"
|
41
|
+
# puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}"
|
42
|
+
c.disconnect
|
43
|
+
end
|
44
|
+
end
|
45
|
+
#
|
46
|
+
e = ExampleSSL4C.new
|
47
|
+
e.run
|
35
48
|
|
@@ -6,23 +6,36 @@
|
|
6
6
|
require "rubygems"
|
7
7
|
require "stomp"
|
8
8
|
#
|
9
|
+
# == Example: Use Ruby Supplied Ciphers
|
10
|
+
#
|
9
11
|
# If you use SSLParams, and need the _default_ Ruby ciphers, this is how.
|
10
12
|
#
|
11
13
|
# NOTE: JRuby users may find that this is a *required* action. YMMV.
|
12
14
|
#
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
class ExampleRubyCiphers
|
16
|
+
# Initialize.
|
17
|
+
def initialize
|
18
|
+
end
|
19
|
+
# Run example.
|
20
|
+
def run
|
21
|
+
ssl_opts = Stomp::SSLParams.new(:use_ruby_ciphers => true) # Plus other parameters as needed
|
22
|
+
#
|
23
|
+
# SSL Use Case: Using default Stomp ciphers
|
24
|
+
#
|
25
|
+
hash = { :hosts => [
|
26
|
+
{:login => 'guest', :passcode => 'guest', :host => 'localhost',
|
19
27
|
:port => 61612, :ssl => ssl_opts},
|
20
28
|
]
|
21
29
|
}
|
22
|
-
#
|
23
|
-
puts "Connect starts, SSL , Use Default Ruby Ciphers"
|
24
|
-
c = Stomp::Connection.new(hash)
|
25
|
-
puts "Connect completed"
|
26
|
-
#
|
27
|
-
c.disconnect
|
30
|
+
#
|
31
|
+
puts "Connect starts, SSL , Use Default Ruby Ciphers"
|
32
|
+
c = Stomp::Connection.new(hash)
|
33
|
+
puts "Connect completed"
|
34
|
+
#
|
35
|
+
c.disconnect
|
36
|
+
end
|
37
|
+
end
|
38
|
+
#
|
39
|
+
e = ExampleRubyCiphers.new
|
40
|
+
e.run
|
28
41
|
|
data/examples/stomp11_common.rb
CHANGED
@@ -7,39 +7,40 @@ require "rubygems" if RUBY_VERSION < "1.9"
|
|
7
7
|
require "stomp"
|
8
8
|
#
|
9
9
|
module Stomp11Common
|
10
|
-
#
|
10
|
+
# User id
|
11
11
|
def login()
|
12
12
|
ENV['STOMP_USER'] || 'guest'
|
13
13
|
end
|
14
|
-
#
|
14
|
+
# Password
|
15
15
|
def passcode()
|
16
16
|
ENV['STOMP_PASSCODE'] || 'guest'
|
17
17
|
end
|
18
|
-
#
|
18
|
+
# Server host
|
19
19
|
def host()
|
20
20
|
ENV['STOMP_HOST'] || "localhost" # The connect host name
|
21
21
|
end
|
22
|
-
#
|
22
|
+
# Server port
|
23
23
|
def port()
|
24
24
|
(ENV['STOMP_PORT'] || 62613).to_i # !! The author runs Apollo listening here
|
25
25
|
end
|
26
|
-
#
|
26
|
+
# Required vhost name
|
27
27
|
def virt_host()
|
28
|
-
|
28
|
+
ENV['STOMP_VHOST'] || "localhost" # The 1.1 virtual host name
|
29
29
|
end
|
30
|
-
#
|
30
|
+
# Create a 1.1 commection
|
31
31
|
def get_connection()
|
32
32
|
conn_hdrs = {"accept-version" => "1.1", # 1.1 only
|
33
|
-
|
34
|
-
|
35
|
-
conn_hash = { :hosts => [
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
"host" => virt_host, # the vhost
|
34
|
+
}
|
35
|
+
conn_hash = { :hosts => [
|
36
|
+
{:login => login, :passcode => passcode, :host => host, :port => port},
|
37
|
+
],
|
38
|
+
:connect_headers => conn_hdrs,
|
39
|
+
}
|
40
40
|
conn = Stomp::Connection.new(conn_hash)
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
|
+
# Number of messages
|
43
44
|
def nmsgs()
|
44
45
|
(ENV['STOMP_NMSGS'] || 1).to_i # Number of messages
|
45
46
|
end
|
data/examples/topic_consumer.rb
CHANGED
@@ -2,18 +2,31 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'stomp'
|
5
|
+
#
|
6
|
+
# == Example topic consumer.
|
7
|
+
#
|
8
|
+
class ExampleTopicConsumer
|
9
|
+
# Initialize.
|
10
|
+
def initialize
|
11
|
+
end
|
12
|
+
# Run example.
|
13
|
+
def run
|
14
|
+
client = Stomp::Client.new("failover://(stomp://:@localhost:61613,stomp://:@remotehost:61613)?initialReconnectDelay=5000&randomize=false&useExponentialBackOff=false")
|
5
15
|
|
6
|
-
|
16
|
+
puts "Subscribing to /topic/ronaldo"
|
7
17
|
|
8
|
-
|
18
|
+
client.subscribe("/topic/ronaldo") do |msg|
|
19
|
+
puts msg.to_s
|
20
|
+
puts "----------------"
|
21
|
+
end
|
9
22
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
loop do
|
16
|
-
sleep(1)
|
17
|
-
puts "."
|
23
|
+
loop do
|
24
|
+
sleep(1)
|
25
|
+
puts "."
|
26
|
+
end
|
27
|
+
end
|
18
28
|
end
|
29
|
+
#
|
30
|
+
e = ExampleTopicConsumer.new
|
31
|
+
e.run
|
19
32
|
|
data/examples/topic_publisher.rb
CHANGED
@@ -3,13 +3,27 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'stomp'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
#
|
7
|
+
# == Example topic publisher.
|
8
|
+
#
|
9
|
+
class ExampleTopicPublisher
|
10
|
+
# Initialize.
|
11
|
+
def initialize
|
12
|
+
end
|
13
|
+
# Run example.
|
14
|
+
def run
|
15
|
+
client = Stomp::Client.new("failover://(stomp://:@localhost:61613,stomp://:@remotehost:61613)?initialReconnectDelay=5000&randomize=false&useExponentialBackOff=false")
|
16
|
+
message = "ronaldo #{ARGV[0]}"
|
17
|
+
|
18
|
+
for i in (1..50)
|
19
|
+
puts "Sending message"
|
20
|
+
client.publish("/topic/ronaldo", "#{i}: #{message}")
|
21
|
+
puts "(#{Time.now})Message sent: #{i}"
|
22
|
+
sleep 0.2
|
23
|
+
end
|
24
|
+
end
|
14
25
|
end
|
26
|
+
#
|
27
|
+
e = ExampleTopicPublisher.new
|
28
|
+
e.run
|
15
29
|
|
data/lib/client/utils.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
require 'digest/sha1'
|
5
|
+
|
6
|
+
module Stomp
|
7
|
+
|
8
|
+
class Client
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# Set a subscription id in the headers hash if one does not already exist.
|
13
|
+
# For simplicities sake, all subscriptions have a subscription ID.
|
14
|
+
# setting an id in the SUBSCRIPTION header is described in the stomp protocol docs:
|
15
|
+
# http://stomp.github.com/
|
16
|
+
def set_subscription_id_if_missing(destination, headers)
|
17
|
+
headers[:id] = headers[:id] ? headers[:id] : headers['id']
|
18
|
+
if headers[:id] == nil
|
19
|
+
headers[:id] = Digest::SHA1.hexdigest(destination)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Register a receipt listener.
|
24
|
+
def register_receipt_listener(listener)
|
25
|
+
id = -1
|
26
|
+
@id_mutex.synchronize do
|
27
|
+
id = @ids.to_s
|
28
|
+
@ids = @ids.succ
|
29
|
+
end
|
30
|
+
@receipt_listeners[id] = listener
|
31
|
+
id
|
32
|
+
end
|
33
|
+
|
34
|
+
# url_regex defines a regex for e.g. login:passcode@host:port or host:port
|
35
|
+
def url_regex
|
36
|
+
'(([\w\.\-]*):(\w*)@)?([\w\.\-]+):(\d+)'
|
37
|
+
end
|
38
|
+
|
39
|
+
# Parse a stomp URL.
|
40
|
+
def parse_hosts(url)
|
41
|
+
hosts = []
|
42
|
+
|
43
|
+
host_match = /stomp(\+ssl)?:\/\/(([\w\.]*):(\w*)@)?([\w\.]+):(\d+)\)/
|
44
|
+
url.scan(host_match).each do |match|
|
45
|
+
host = {}
|
46
|
+
host[:ssl] = !match[0].nil?
|
47
|
+
host[:login] = match[2] || ""
|
48
|
+
host[:passcode] = match[3] || ""
|
49
|
+
host[:host] = match[4]
|
50
|
+
host[:port] = match[5].to_i
|
51
|
+
|
52
|
+
hosts << host
|
53
|
+
end
|
54
|
+
|
55
|
+
hosts
|
56
|
+
end
|
57
|
+
|
58
|
+
# A very basic check of required arguments.
|
59
|
+
# *NOTE* This method will be made private in the next release.
|
60
|
+
def check_arguments!()
|
61
|
+
raise ArgumentError if @host.nil? || @host.empty?
|
62
|
+
raise ArgumentError if @port.nil? || @port == '' || @port < 1 || @port > 65535
|
63
|
+
raise ArgumentError unless @reliable.is_a?(TrueClass) || @reliable.is_a?(FalseClass)
|
64
|
+
end
|
65
|
+
|
66
|
+
# filter_options returns a new Hash of filtered options.
|
67
|
+
def filter_options(options)
|
68
|
+
new_options = {}
|
69
|
+
new_options[:initial_reconnect_delay] = (options["initialReconnectDelay"] || 10).to_f / 1000 # In ms
|
70
|
+
new_options[:max_reconnect_delay] = (options["maxReconnectDelay"] || 30000 ).to_f / 1000 # In ms
|
71
|
+
new_options[:use_exponential_back_off] = !(options["useExponentialBackOff"] == "false") # Default: true
|
72
|
+
new_options[:back_off_multiplier] = (options["backOffMultiplier"] || 2 ).to_i
|
73
|
+
new_options[:max_reconnect_attempts] = (options["maxReconnectAttempts"] || 0 ).to_i
|
74
|
+
new_options[:randomize] = options["randomize"] == "true" # Default: false
|
75
|
+
new_options[:backup] = false # Not implemented yet: I'm using a master X slave solution
|
76
|
+
new_options[:timeout] = -1 # Not implemented yet: a "timeout(5) do ... end" would do the trick, feel free
|
77
|
+
|
78
|
+
new_options
|
79
|
+
end
|
80
|
+
|
81
|
+
# find_listener returns the listener for a given subscription in a given message.
|
82
|
+
def find_listener(message)
|
83
|
+
subscription_id = message.headers['subscription']
|
84
|
+
if subscription_id == nil
|
85
|
+
# For backward compatibility, some messages may already exist with no
|
86
|
+
# subscription id, in which case we can attempt to synthesize one.
|
87
|
+
set_subscription_id_if_missing(message.headers['destination'], message.headers)
|
88
|
+
subscription_id = message.headers[:id]
|
89
|
+
end
|
90
|
+
@listeners[subscription_id]
|
91
|
+
end
|
92
|
+
|
93
|
+
# Start a single listener thread. Misnamed I think.
|
94
|
+
def start_listeners()
|
95
|
+
@listeners = {}
|
96
|
+
@receipt_listeners = {}
|
97
|
+
@replay_messages_by_txn = {}
|
98
|
+
|
99
|
+
@listener_thread = Thread.start do
|
100
|
+
while true
|
101
|
+
message = @connection.receive
|
102
|
+
if message.command == Stomp::CMD_MESSAGE
|
103
|
+
if listener = find_listener(message)
|
104
|
+
listener.call(message)
|
105
|
+
end
|
106
|
+
elsif message.command == Stomp::CMD_RECEIPT
|
107
|
+
if listener = @receipt_listeners[message.headers['receipt-id']]
|
108
|
+
listener.call(message)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'timeout'
|
5
|
+
require 'io/wait'
|
6
|
+
require 'digest/sha1'
|
7
|
+
|
8
|
+
module Stomp
|
9
|
+
|
10
|
+
class Connection
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def _validate_hbheader()
|
15
|
+
return if @connect_headers[:"heart-beat"] == "0,0" # Caller does not want heartbeats. OK.
|
16
|
+
parts = @connect_headers[:"heart-beat"].split(",")
|
17
|
+
if (parts.size != 2) || (parts[0] != parts[0].to_i.to_s) || (parts[1] != parts[1].to_i.to_s)
|
18
|
+
raise Stomp::Error::InvalidHeartBeatHeaderError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def _init_heartbeats()
|
23
|
+
return if @connect_headers[:"heart-beat"] == "0,0" # Caller does not want heartbeats. OK.
|
24
|
+
|
25
|
+
# Init.
|
26
|
+
|
27
|
+
#
|
28
|
+
@cx = @cy = @sx = @sy = 0, # Variable names as in spec
|
29
|
+
|
30
|
+
#
|
31
|
+
@hbsend_interval = @hbrecv_interval = 0.0 # Send/Receive ticker interval.
|
32
|
+
|
33
|
+
#
|
34
|
+
@hbsend_count = @hbrecv_count = 0 # Send/Receive ticker counts.
|
35
|
+
|
36
|
+
#
|
37
|
+
@ls = @lr = -1.0 # Last send/receive time (from Time.now.to_f)
|
38
|
+
|
39
|
+
#
|
40
|
+
@st = @rt = nil # Send/receive ticker thread
|
41
|
+
|
42
|
+
# Handle current client / server capabilities.
|
43
|
+
|
44
|
+
#
|
45
|
+
cfh = @connection_frame.headers.symbolize_keys
|
46
|
+
return if cfh[:"heart-beat"] == "0,0" # Server does not want heartbeats
|
47
|
+
|
48
|
+
# Conect header parts.
|
49
|
+
parts = @connect_headers[:"heart-beat"].split(",")
|
50
|
+
@cx = parts[0].to_i
|
51
|
+
@cy = parts[1].to_i
|
52
|
+
|
53
|
+
# Connected frame header parts.
|
54
|
+
parts = cfh[:"heart-beat"].split(",")
|
55
|
+
@sx = parts[0].to_i
|
56
|
+
@sy = parts[1].to_i
|
57
|
+
|
58
|
+
# Catch odd situations like server has used => heart-beat:000,00000
|
59
|
+
return if (@cx == 0 && @cy == 0) || (@sx == 0 && @sy == 0)
|
60
|
+
|
61
|
+
# See if we are doing anything at all.
|
62
|
+
|
63
|
+
#
|
64
|
+
@hbs = @hbr = true # Sending/Receiving heartbeats. Assume yes at first.
|
65
|
+
# Check if sending is possible.
|
66
|
+
@hbs = false if @cx == 0 || @sy == 0
|
67
|
+
# Check if receiving is possible.
|
68
|
+
@hbr = false if @sx == 0 || @cy == 0
|
69
|
+
# Should not do heartbeats at all
|
70
|
+
return if (!@hbs && !@hbr)
|
71
|
+
|
72
|
+
# If sending
|
73
|
+
if @hbs
|
74
|
+
sm = @cx >= @sy ? @cx : @sy # ticker interval, ms
|
75
|
+
@hbsend_interval = 1000.0 * sm # ticker interval, μs
|
76
|
+
@ls = Time.now.to_f # best guess at start
|
77
|
+
_start_send_ticker()
|
78
|
+
end
|
79
|
+
|
80
|
+
# If receiving
|
81
|
+
if @hbr
|
82
|
+
rm = @sx >= @cy ? @sx : @cy # ticker interval, ms
|
83
|
+
@hbrecv_interval = 1000.0 * rm # ticker interval, μs
|
84
|
+
@lr = Time.now.to_f # best guess at start
|
85
|
+
_start_receive_ticker()
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
# _start_send_ticker starts a thread to send heartbeats when required.
|
91
|
+
def _start_send_ticker()
|
92
|
+
sleeptime = @hbsend_interval / 1000000.0 # Sleep time secs
|
93
|
+
@st = Thread.new {
|
94
|
+
while true do
|
95
|
+
sleep sleeptime
|
96
|
+
curt = Time.now.to_f
|
97
|
+
if @logger && @logger.respond_to?(:on_hbfire)
|
98
|
+
@logger.on_hbfire(log_params, "send_fire", curt)
|
99
|
+
end
|
100
|
+
delta = curt - @ls
|
101
|
+
if delta > (@hbsend_interval - (@hbsend_interval/5.0)) / 1000000.0 # Be tolerant (minus)
|
102
|
+
if @logger && @logger.respond_to?(:on_hbfire)
|
103
|
+
@logger.on_hbfire(log_params, "send_heartbeat", curt)
|
104
|
+
end
|
105
|
+
# Send a heartbeat
|
106
|
+
@transmit_semaphore.synchronize do
|
107
|
+
begin
|
108
|
+
@socket.puts
|
109
|
+
@ls = curt # Update last send
|
110
|
+
@hb_sent = true # Reset if necessary
|
111
|
+
@hbsend_count += 1
|
112
|
+
rescue Exception => sendex
|
113
|
+
@hb_sent = false # Set the warning flag
|
114
|
+
if @logger && @logger.respond_to?(:on_hbwrite_fail)
|
115
|
+
@logger.on_hbwrite_fail(log_params, {"ticker_interval" => @hbsend_interval,
|
116
|
+
"exception" => sendex})
|
117
|
+
end
|
118
|
+
raise # Re-raise. What else could be done here?
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
Thread.pass
|
123
|
+
end
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
# _start_receive_ticker starts a thread that receives heartbeats when required.
|
128
|
+
def _start_receive_ticker()
|
129
|
+
sleeptime = @hbrecv_interval / 1000000.0 # Sleep time secs
|
130
|
+
@rt = Thread.new {
|
131
|
+
while true do
|
132
|
+
sleep sleeptime
|
133
|
+
curt = Time.now.to_f
|
134
|
+
if @logger && @logger.respond_to?(:on_hbfire)
|
135
|
+
@logger.on_hbfire(log_params, "receive_fire", curt)
|
136
|
+
end
|
137
|
+
delta = curt - @lr
|
138
|
+
if delta > ((@hbrecv_interval + (@hbrecv_interval/5.0)) / 1000000.0) # Be tolerant (plus)
|
139
|
+
if @logger && @logger.respond_to?(:on_hbfire)
|
140
|
+
@logger.on_hbfire(log_params, "receive_heartbeat", curt)
|
141
|
+
end
|
142
|
+
# Client code could be off doing something else (that is, no reading of
|
143
|
+
# the socket has been requested by the caller). Try to handle that case.
|
144
|
+
lock = @read_semaphore.try_lock
|
145
|
+
if lock
|
146
|
+
last_char = @socket.getc
|
147
|
+
plc = parse_char(last_char)
|
148
|
+
if plc == "\n" # Server Heartbeat
|
149
|
+
@lr = Time.now.to_f
|
150
|
+
else
|
151
|
+
@socket.ungetc(last_char)
|
152
|
+
end
|
153
|
+
@read_semaphore.unlock
|
154
|
+
@hbrecv_count += 1
|
155
|
+
else
|
156
|
+
# Shrug. Have not received one. Just set warning flag.
|
157
|
+
@hb_received = false
|
158
|
+
if @logger && @logger.respond_to?(:on_hbread_fail)
|
159
|
+
@logger.on_hbread_fail(log_params, {"ticker_interval" => @hbrecv_interval})
|
160
|
+
end
|
161
|
+
end
|
162
|
+
else
|
163
|
+
@hb_received = true # Reset if necessary
|
164
|
+
end
|
165
|
+
Thread.pass
|
166
|
+
end
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
end # class
|
171
|
+
|
172
|
+
end # module
|
173
|
+
|