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
@@ -0,0 +1,104 @@
|
|
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
|
+
# Support multi-homed servers.
|
15
|
+
def _expand_hosts(hash)
|
16
|
+
new_hash = hash.clone
|
17
|
+
new_hash[:hosts_cloned] = hash[:hosts].clone
|
18
|
+
new_hash[:hosts] = []
|
19
|
+
#
|
20
|
+
hash[:hosts].each do |host_parms|
|
21
|
+
ai = Socket.getaddrinfo(host_parms[:host], nil, nil, Socket::SOCK_STREAM)
|
22
|
+
next if ai.nil? || ai.size == 0
|
23
|
+
info6 = ai.detect {|info| info[4] == Socket::AF_INET6}
|
24
|
+
info4 = ai.detect {|info| info[4] == Socket::AF_INET}
|
25
|
+
if info6
|
26
|
+
new_hostp = host_parms.clone
|
27
|
+
new_hostp[:host] = info6[3]
|
28
|
+
new_hash[:hosts] << new_hostp
|
29
|
+
end
|
30
|
+
if info4
|
31
|
+
new_hostp = host_parms.clone
|
32
|
+
new_hostp[:host] = info4[3]
|
33
|
+
new_hash[:hosts] << new_hostp
|
34
|
+
end
|
35
|
+
end
|
36
|
+
return new_hash
|
37
|
+
end
|
38
|
+
|
39
|
+
# Handle 1.9+ character representation.
|
40
|
+
def parse_char(char)
|
41
|
+
RUBY_VERSION > '1.9' ? char : char.chr
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create parameters for any callback logger.
|
45
|
+
def log_params()
|
46
|
+
lparms = @parameters.clone if @parameters
|
47
|
+
lparms = {} unless lparms
|
48
|
+
lparms[:cur_host] = @host
|
49
|
+
lparms[:cur_port] = @port
|
50
|
+
lparms[:cur_login] = @login
|
51
|
+
lparms[:cur_passcode] = @passcode
|
52
|
+
lparms[:cur_ssl] = @ssl
|
53
|
+
lparms[:cur_recondelay] = @reconnect_delay
|
54
|
+
lparms[:cur_parseto] = @parse_timeout
|
55
|
+
lparms[:cur_conattempts] = @connection_attempts
|
56
|
+
lparms[:openstat] = open?
|
57
|
+
#
|
58
|
+
lparms
|
59
|
+
end
|
60
|
+
|
61
|
+
# _pre_connect handles low level logic just prior to a physical connect.
|
62
|
+
def _pre_connect()
|
63
|
+
@connect_headers = @connect_headers.symbolize_keys
|
64
|
+
raise Stomp::Error::ProtocolErrorConnect if (@connect_headers[:"accept-version"] && !@connect_headers[:host])
|
65
|
+
raise Stomp::Error::ProtocolErrorConnect if (!@connect_headers[:"accept-version"] && @connect_headers[:host])
|
66
|
+
return unless (@connect_headers[:"accept-version"] && @connect_headers[:host]) # 1.0
|
67
|
+
# Try 1.1 or greater
|
68
|
+
okvers = []
|
69
|
+
avers = @connect_headers[:"accept-version"].split(",")
|
70
|
+
avers.each do |nver|
|
71
|
+
if Stomp::SUPPORTED.index(nver)
|
72
|
+
okvers << nver
|
73
|
+
end
|
74
|
+
end
|
75
|
+
raise Stomp::Error::UnsupportedProtocolError if okvers == []
|
76
|
+
@connect_headers[:"accept-version"] = okvers.join(",") # This goes to server
|
77
|
+
# Heartbeats - pre connect
|
78
|
+
return unless @connect_headers[:"heart-beat"]
|
79
|
+
_validate_hbheader()
|
80
|
+
end
|
81
|
+
|
82
|
+
# _post_connect handles low level logic just post a physical connect.
|
83
|
+
def _post_connect()
|
84
|
+
return unless (@connect_headers[:"accept-version"] && @connect_headers[:host])
|
85
|
+
return if @connection_frame.command == Stomp::CMD_ERROR
|
86
|
+
# We are CONNECTed
|
87
|
+
cfh = @connection_frame.headers.symbolize_keys
|
88
|
+
@protocol = cfh[:version]
|
89
|
+
if @protocol
|
90
|
+
# Should not happen, but check anyway
|
91
|
+
raise Stomp::Error::UnsupportedProtocolError unless Stomp::SUPPORTED.index(@protocol)
|
92
|
+
else # CONNECTed to a 1.0 server that does not return *any* 1.1 type headers
|
93
|
+
@protocol = Stomp::SPL_10 # reset
|
94
|
+
return
|
95
|
+
end
|
96
|
+
# Heartbeats
|
97
|
+
return unless @connect_headers[:"heart-beat"]
|
98
|
+
_init_heartbeats()
|
99
|
+
end
|
100
|
+
|
101
|
+
end # class
|
102
|
+
|
103
|
+
end # module
|
104
|
+
|
data/lib/stomp/client.rb
CHANGED
@@ -12,20 +12,59 @@ module Stomp
|
|
12
12
|
# in that thread if you have much message volume.
|
13
13
|
class Client
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
#alias :obj_send :send
|
15
|
+
public
|
18
16
|
|
19
|
-
#
|
17
|
+
# The login ID used by the client.
|
18
|
+
attr_reader :login
|
19
|
+
|
20
|
+
# The login credentials used by the client.
|
21
|
+
attr_reader :passcode
|
22
|
+
|
23
|
+
# The Stomp host specified by the client.
|
24
|
+
attr_reader :host
|
25
|
+
|
26
|
+
# The Stomp host's listening port.
|
27
|
+
attr_reader :port
|
28
|
+
|
29
|
+
# Is this connection reliable?
|
30
|
+
attr_reader :reliable
|
31
|
+
|
32
|
+
# Parameters Hash, possibly nil for a non-hashed connect.
|
33
|
+
attr_reader :parameters
|
34
|
+
|
35
|
+
# A new Client object can be initialized using three forms:
|
20
36
|
#
|
21
|
-
#
|
37
|
+
# Hash (this is the recommended Client initialization method):
|
38
|
+
#
|
39
|
+
# hash = {
|
40
|
+
# :hosts => [
|
41
|
+
# {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false},
|
42
|
+
# {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false}
|
43
|
+
# ],
|
44
|
+
# :reliable => true,
|
45
|
+
# :initial_reconnect_delay => 0.01,
|
46
|
+
# :max_reconnect_delay => 30.0,
|
47
|
+
# :use_exponential_back_off => true,
|
48
|
+
# :back_off_multiplier => 2,
|
49
|
+
# :max_reconnect_attempts => 0,
|
50
|
+
# :randomize => false,
|
51
|
+
# :backup => false,
|
52
|
+
# :connect_timeout => 0,
|
53
|
+
# :connect_headers => {},
|
54
|
+
# :parse_timeout => 5,
|
55
|
+
# :logger => nil,
|
56
|
+
# }
|
57
|
+
#
|
58
|
+
# e.g. c = Stomp::Client.new(hash)
|
59
|
+
#
|
60
|
+
# Positional parameters:
|
22
61
|
# login (String, default : '')
|
23
62
|
# passcode (String, default : '')
|
24
63
|
# host (String, default : 'localhost')
|
25
64
|
# port (Integer, default : 61613)
|
26
65
|
# reliable (Boolean, default : false)
|
27
66
|
#
|
28
|
-
# e.g. c = Client.new('login', 'passcode', 'localhost', 61613, true)
|
67
|
+
# e.g. c = Stomp::Client.new('login', 'passcode', 'localhost', 61613, true)
|
29
68
|
#
|
30
69
|
# Stomp URL :
|
31
70
|
# A Stomp URL must begin with 'stomp://' and can be in one of the following forms:
|
@@ -35,19 +74,21 @@ module Stomp
|
|
35
74
|
# stomp://login:passcode@host:port
|
36
75
|
# stomp://login:passcode@host.domain.tld:port
|
37
76
|
#
|
77
|
+
# e.g. c = Stomp::Client.new(urlstring)
|
78
|
+
#
|
38
79
|
def initialize(login = '', passcode = '', host = 'localhost', port = 61613, reliable = false, autoflush = false)
|
39
80
|
|
40
81
|
# Parse stomp:// URL's or set params
|
41
82
|
if login.is_a?(Hash)
|
42
83
|
@parameters = login
|
43
|
-
|
84
|
+
|
44
85
|
first_host = @parameters[:hosts][0]
|
45
|
-
|
86
|
+
|
46
87
|
@login = first_host[:login]
|
47
88
|
@passcode = first_host[:passcode]
|
48
89
|
@host = first_host[:host]
|
49
90
|
@port = first_host[:port] || Connection::default_port(first_host[:ssl])
|
50
|
-
|
91
|
+
|
51
92
|
@reliable = true
|
52
93
|
elsif login =~ /^stomp:\/\/#{url_regex}/ # e.g. stomp://login:passcode@host:port or stomp://host:port
|
53
94
|
@login = $2 || ""
|
@@ -55,26 +96,26 @@ module Stomp
|
|
55
96
|
@host = $4
|
56
97
|
@port = $5.to_i
|
57
98
|
@reliable = false
|
58
|
-
elsif login =~ /^failover:(\/\/)?\(stomp(\+ssl)?:\/\/#{url_regex}(,stomp(\+ssl)?:\/\/#{url_regex}\))+(\?(.*))?$/
|
59
|
-
|
99
|
+
elsif login =~ /^failover:(\/\/)?\(stomp(\+ssl)?:\/\/#{url_regex}(,stomp(\+ssl)?:\/\/#{url_regex}\))+(\?(.*))?$/
|
100
|
+
# e.g. failover://(stomp://login1:passcode1@localhost:61616,stomp://login2:passcode2@remotehost:61617)?option1=param
|
60
101
|
first_host = {}
|
61
102
|
first_host[:ssl] = !$2.nil?
|
62
103
|
@login = first_host[:login] = $4 || ""
|
63
104
|
@passcode = first_host[:passcode] = $5 || ""
|
64
105
|
@host = first_host[:host] = $6
|
65
106
|
@port = first_host[:port] = $7.to_i || Connection::default_port(first_host[:ssl])
|
66
|
-
|
107
|
+
|
67
108
|
options = $16 || ""
|
68
109
|
parts = options.split(/&|=/)
|
69
110
|
options = Hash[*parts]
|
70
|
-
|
111
|
+
|
71
112
|
hosts = [first_host] + parse_hosts(login)
|
72
|
-
|
113
|
+
|
73
114
|
@parameters = {}
|
74
115
|
@parameters[:hosts] = hosts
|
75
|
-
|
116
|
+
|
76
117
|
@parameters.merge! filter_options(options)
|
77
|
-
|
118
|
+
|
78
119
|
@reliable = true
|
79
120
|
else
|
80
121
|
@login = login
|
@@ -84,9 +125,9 @@ module Stomp
|
|
84
125
|
@reliable = reliable
|
85
126
|
end
|
86
127
|
|
87
|
-
check_arguments!
|
128
|
+
check_arguments!()
|
88
129
|
|
89
|
-
@id_mutex = Mutex.new
|
130
|
+
@id_mutex = Mutex.new()
|
90
131
|
@ids = 1
|
91
132
|
|
92
133
|
if @parameters
|
@@ -95,28 +136,28 @@ module Stomp
|
|
95
136
|
@connection = Connection.new(@login, @passcode, @host, @port, @reliable)
|
96
137
|
@connection.autoflush = autoflush
|
97
138
|
end
|
98
|
-
|
99
|
-
start_listeners
|
139
|
+
|
140
|
+
start_listeners()
|
100
141
|
|
101
142
|
end
|
102
|
-
|
103
|
-
#
|
143
|
+
|
144
|
+
# open is syntactic sugar for 'Client.new' See 'initialize' for usage.
|
104
145
|
def self.open(login = '', passcode = '', host = 'localhost', port = 61613, reliable = false)
|
105
146
|
Client.new(login, passcode, host, port, reliable)
|
106
147
|
end
|
107
148
|
|
108
|
-
#
|
109
|
-
# generally used to wait for a quit signal
|
149
|
+
# join the listener thread for this client,
|
150
|
+
# generally used to wait for a quit signal.
|
110
151
|
def join(limit = nil)
|
111
152
|
@listener_thread.join(limit)
|
112
153
|
end
|
113
154
|
|
114
|
-
# Begin a transaction by name
|
155
|
+
# Begin starts work in a a transaction by name.
|
115
156
|
def begin(name, headers = {})
|
116
157
|
@connection.begin(name, headers)
|
117
158
|
end
|
118
159
|
|
119
|
-
# Abort a transaction by name
|
160
|
+
# Abort aborts work in a transaction by name.
|
120
161
|
def abort(name, headers = {})
|
121
162
|
@connection.abort(name, headers)
|
122
163
|
|
@@ -131,7 +172,7 @@ module Stomp
|
|
131
172
|
end
|
132
173
|
end
|
133
174
|
|
134
|
-
# Commit a transaction by name
|
175
|
+
# Commit commits work in a transaction by name.
|
135
176
|
def commit(name, headers = {})
|
136
177
|
txn_id = headers[:transaction]
|
137
178
|
@replay_messages_by_txn.delete(txn_id)
|
@@ -139,9 +180,8 @@ module Stomp
|
|
139
180
|
end
|
140
181
|
|
141
182
|
# Subscribe to a destination, must be passed a block
|
142
|
-
# which will be used as a callback listener
|
143
|
-
#
|
144
|
-
# Accepts a transaction header ( :transaction => 'some_transaction_id' )
|
183
|
+
# which will be used as a callback listener.
|
184
|
+
# Accepts a transaction header ( :transaction => 'some_transaction_id' ).
|
145
185
|
def subscribe(destination, headers = {})
|
146
186
|
raise "No listener given" unless block_given?
|
147
187
|
# use subscription id to correlate messages to subscription. As described in
|
@@ -155,7 +195,7 @@ module Stomp
|
|
155
195
|
@connection.subscribe(destination, headers)
|
156
196
|
end
|
157
197
|
|
158
|
-
#
|
198
|
+
# Unsubscribe from a subscription by name.
|
159
199
|
def unsubscribe(name, headers = {})
|
160
200
|
set_subscription_id_if_missing(name, headers)
|
161
201
|
@connection.unsubscribe(name, headers)
|
@@ -163,9 +203,8 @@ module Stomp
|
|
163
203
|
end
|
164
204
|
|
165
205
|
# Acknowledge a message, used when a subscription has specified
|
166
|
-
# client acknowledgement ( connection.subscribe
|
167
|
-
#
|
168
|
-
# Accepts a transaction header ( :transaction => 'some_transaction_id' )
|
206
|
+
# client acknowledgement ( connection.subscribe("/queue/a",{:ack => 'client'}).
|
207
|
+
# Accepts a transaction header ( :transaction => 'some_transaction_id' ).
|
169
208
|
def acknowledge(message, headers = {})
|
170
209
|
txn_id = headers[:transaction]
|
171
210
|
if txn_id
|
@@ -180,222 +219,131 @@ module Stomp
|
|
180
219
|
if block_given?
|
181
220
|
headers['receipt'] = register_receipt_listener lambda {|r| yield r}
|
182
221
|
end
|
183
|
-
@connection.ack
|
222
|
+
@connection.ack(message.headers['message-id'], headers)
|
184
223
|
end
|
185
224
|
|
186
|
-
# Stomp 1.1+ NACK
|
225
|
+
# Stomp 1.1+ NACK.
|
187
226
|
def nack(message_id, headers = {})
|
188
|
-
@connection.nack
|
227
|
+
@connection.nack(message_id, headers)
|
189
228
|
end
|
190
229
|
|
191
|
-
# Unreceive a message, sending it back to its queue or to the DLQ
|
192
|
-
#
|
230
|
+
# Unreceive a message, sending it back to its queue or to the DLQ.
|
193
231
|
def unreceive(message, options = {})
|
194
232
|
@connection.unreceive(message, options)
|
195
233
|
end
|
196
|
-
|
197
|
-
# Publishes message to destination
|
198
|
-
#
|
234
|
+
|
235
|
+
# Publishes message to destination.
|
199
236
|
# If a block is given a receipt will be requested and passed to the
|
200
|
-
# block on receipt
|
201
|
-
#
|
202
|
-
# Accepts a transaction header ( :transaction => 'some_transaction_id' )
|
237
|
+
# block on receipt.
|
238
|
+
# Accepts a transaction header ( :transaction => 'some_transaction_id' ).
|
203
239
|
def publish(destination, message, headers = {})
|
204
240
|
if block_given?
|
205
241
|
headers['receipt'] = register_receipt_listener lambda {|r| yield r}
|
206
242
|
end
|
207
243
|
@connection.publish(destination, message, headers)
|
208
244
|
end
|
209
|
-
|
245
|
+
|
246
|
+
# :TODO: This should not be used. Currently only referenced in the
|
247
|
+
# spec tests.
|
248
|
+
# *NOTE* This will be removed in the next release.
|
210
249
|
def obj_send(*args)
|
211
250
|
__send__(*args)
|
212
251
|
end
|
213
|
-
|
214
|
-
|
252
|
+
|
253
|
+
# Return the broker's CONNECTED frame to the client. Misnamed.
|
254
|
+
def connection_frame()
|
215
255
|
@connection.connection_frame
|
216
256
|
end
|
217
257
|
|
218
|
-
|
258
|
+
# Return any RECEIPT frame received by DISCONNECT.
|
259
|
+
def disconnect_receipt()
|
219
260
|
@connection.disconnect_receipt
|
220
261
|
end
|
221
262
|
|
222
|
-
#
|
263
|
+
# open? tests if this client connection is open.
|
223
264
|
def open?
|
224
|
-
@connection.open?
|
265
|
+
@connection.open?()
|
225
266
|
end
|
226
267
|
|
227
|
-
#
|
228
|
-
def closed?
|
229
|
-
@connection.closed?
|
268
|
+
# close? tests if this client connection is closed.
|
269
|
+
def closed?()
|
270
|
+
@connection.closed?()
|
230
271
|
end
|
231
272
|
|
232
|
-
#
|
233
|
-
|
273
|
+
# close frees resources in use by this client. The listener thread is
|
274
|
+
# terminated, and disconnect on the connection is called.
|
275
|
+
def close(headers={})
|
234
276
|
@listener_thread.exit
|
235
|
-
@connection.disconnect
|
277
|
+
@connection.disconnect(headers)
|
236
278
|
end
|
237
279
|
|
238
|
-
#
|
239
|
-
def running
|
280
|
+
# running checks if the thread was created and is not dead.
|
281
|
+
def running()
|
240
282
|
@listener_thread && !!@listener_thread.status
|
241
283
|
end
|
242
284
|
|
243
|
-
#
|
285
|
+
# set_logger identifies a new callback logger.
|
244
286
|
def set_logger(logger)
|
245
287
|
@connection.set_logger(logger)
|
246
288
|
end
|
247
289
|
|
248
|
-
#
|
290
|
+
# protocol returns the current client's protocol level.
|
249
291
|
def protocol()
|
250
|
-
@connection.protocol
|
292
|
+
@connection.protocol()
|
251
293
|
end
|
252
294
|
|
253
|
-
#
|
295
|
+
# valid_utf8? validates any given string for UTF8 compliance.
|
254
296
|
def valid_utf8?(s)
|
255
297
|
@connection.valid_utf8?(s)
|
256
298
|
end
|
257
299
|
|
258
|
-
#
|
300
|
+
# sha1 returns a SHA1 sum of a given string.
|
259
301
|
def sha1(data)
|
260
302
|
@connection.sha1(data)
|
261
303
|
end
|
262
304
|
|
263
|
-
#
|
305
|
+
# uuid returns a type 4 UUID.
|
264
306
|
def uuid()
|
265
307
|
@connection.uuid()
|
266
308
|
end
|
267
309
|
|
268
|
-
#
|
269
|
-
def hbsend_interval
|
270
|
-
@connection.hbsend_interval
|
310
|
+
# hbsend_interval returns the connection's heartbeat send interval.
|
311
|
+
def hbsend_interval()
|
312
|
+
@connection.hbsend_interval()
|
271
313
|
end
|
272
314
|
|
273
|
-
#
|
274
|
-
def hbrecv_interval
|
275
|
-
@connection.hbrecv_interval
|
315
|
+
# hbrecv_interval returns the connection's heartbeat receive interval.
|
316
|
+
def hbrecv_interval()
|
317
|
+
@connection.hbrecv_interval()
|
276
318
|
end
|
277
319
|
|
278
|
-
#
|
279
|
-
def hbsend_count
|
280
|
-
@connection.hbsend_count
|
320
|
+
# hbsend_count returns the current connection's heartbeat send count.
|
321
|
+
def hbsend_count()
|
322
|
+
@connection.hbsend_count()
|
281
323
|
end
|
282
324
|
|
283
|
-
#
|
284
|
-
def hbrecv_count
|
285
|
-
@connection.hbrecv_count
|
325
|
+
# hbrecv_count returns the current connection's heartbeat receive count.
|
326
|
+
def hbrecv_count()
|
327
|
+
@connection.hbrecv_count()
|
286
328
|
end
|
287
329
|
|
288
330
|
# Poll for asynchronous messages issued by broker.
|
289
331
|
# Return nil of no message available, else the message
|
290
|
-
def poll
|
291
|
-
@connection.poll
|
332
|
+
def poll()
|
333
|
+
@connection.poll()
|
292
334
|
end
|
293
335
|
|
336
|
+
# autoflush= sets the current connection's autoflush setting.
|
294
337
|
def autoflush=(af)
|
295
338
|
@connection.autoflush = af
|
296
339
|
end
|
297
340
|
|
298
|
-
|
299
|
-
|
341
|
+
# autoflush returns the current connection's autoflush setting.
|
342
|
+
def autoflush()
|
343
|
+
@connection.autoflush()
|
300
344
|
end
|
301
345
|
|
302
|
-
|
303
|
-
# Set a subscription id in the headers hash if one does not already exist.
|
304
|
-
# For simplicities sake, all subscriptions have a subscription ID.
|
305
|
-
# setting an id in the SUBSCRIPTION header is described in the stomp protocol docs:
|
306
|
-
# http://stomp.github.com/
|
307
|
-
def set_subscription_id_if_missing(destination, headers)
|
308
|
-
headers[:id] = headers[:id] ? headers[:id] : headers['id']
|
309
|
-
if headers[:id] == nil
|
310
|
-
headers[:id] = Digest::SHA1.hexdigest(destination)
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
def register_receipt_listener(listener)
|
315
|
-
id = -1
|
316
|
-
@id_mutex.synchronize do
|
317
|
-
id = @ids.to_s
|
318
|
-
@ids = @ids.succ
|
319
|
-
end
|
320
|
-
@receipt_listeners[id] = listener
|
321
|
-
id
|
322
|
-
end
|
323
|
-
|
324
|
-
# e.g. login:passcode@host:port or host:port
|
325
|
-
def url_regex
|
326
|
-
'(([\w\.\-]*):(\w*)@)?([\w\.\-]+):(\d+)'
|
327
|
-
end
|
328
|
-
|
329
|
-
def parse_hosts(url)
|
330
|
-
hosts = []
|
331
|
-
|
332
|
-
host_match = /stomp(\+ssl)?:\/\/(([\w\.]*):(\w*)@)?([\w\.]+):(\d+)\)/
|
333
|
-
url.scan(host_match).each do |match|
|
334
|
-
host = {}
|
335
|
-
host[:ssl] = !match[0].nil?
|
336
|
-
host[:login] = match[2] || ""
|
337
|
-
host[:passcode] = match[3] || ""
|
338
|
-
host[:host] = match[4]
|
339
|
-
host[:port] = match[5].to_i
|
340
|
-
|
341
|
-
hosts << host
|
342
|
-
end
|
343
|
-
|
344
|
-
hosts
|
345
|
-
end
|
346
|
-
|
347
|
-
def check_arguments!
|
348
|
-
raise ArgumentError if @host.nil? || @host.empty?
|
349
|
-
raise ArgumentError if @port.nil? || @port == '' || @port < 1 || @port > 65535
|
350
|
-
raise ArgumentError unless @reliable.is_a?(TrueClass) || @reliable.is_a?(FalseClass)
|
351
|
-
end
|
352
|
-
|
353
|
-
def filter_options(options)
|
354
|
-
new_options = {}
|
355
|
-
new_options[:initial_reconnect_delay] = (options["initialReconnectDelay"] || 10).to_f / 1000 # In ms
|
356
|
-
new_options[:max_reconnect_delay] = (options["maxReconnectDelay"] || 30000 ).to_f / 1000 # In ms
|
357
|
-
new_options[:use_exponential_back_off] = !(options["useExponentialBackOff"] == "false") # Default: true
|
358
|
-
new_options[:back_off_multiplier] = (options["backOffMultiplier"] || 2 ).to_i
|
359
|
-
new_options[:max_reconnect_attempts] = (options["maxReconnectAttempts"] || 0 ).to_i
|
360
|
-
new_options[:randomize] = options["randomize"] == "true" # Default: false
|
361
|
-
new_options[:backup] = false # Not implemented yet: I'm using a master X slave solution
|
362
|
-
new_options[:timeout] = -1 # Not implemented yet: a "timeout(5) do ... end" would do the trick, feel free
|
363
|
-
|
364
|
-
new_options
|
365
|
-
end
|
366
|
-
|
367
|
-
def find_listener(message)
|
368
|
-
subscription_id = message.headers['subscription']
|
369
|
-
if subscription_id == nil
|
370
|
-
# For backward compatibility, some messages may already exist with no
|
371
|
-
# subscription id, in which case we can attempt to synthesize one.
|
372
|
-
set_subscription_id_if_missing(message.headers['destination'], message.headers)
|
373
|
-
subscription_id = message.headers[:id]
|
374
|
-
end
|
375
|
-
@listeners[subscription_id]
|
376
|
-
end
|
346
|
+
end # Class
|
377
347
|
|
378
|
-
|
379
|
-
@listeners = {}
|
380
|
-
@receipt_listeners = {}
|
381
|
-
@replay_messages_by_txn = {}
|
382
|
-
|
383
|
-
@listener_thread = Thread.start do
|
384
|
-
while true
|
385
|
-
message = @connection.receive
|
386
|
-
if message.command == Stomp::CMD_MESSAGE
|
387
|
-
if listener = find_listener(message)
|
388
|
-
listener.call(message)
|
389
|
-
end
|
390
|
-
elsif message.command == Stomp::CMD_RECEIPT
|
391
|
-
if listener = @receipt_listeners[message.headers['receipt-id']]
|
392
|
-
listener.call(message)
|
393
|
-
end
|
394
|
-
end
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
end
|
399
|
-
end
|
400
|
-
end
|
348
|
+
end # Module
|
401
349
|
|
data/lib/stomp/codec.rb
CHANGED
@@ -14,7 +14,9 @@ module Stomp
|
|
14
14
|
#
|
15
15
|
class HeaderCodec
|
16
16
|
|
17
|
-
|
17
|
+
public
|
18
|
+
|
19
|
+
# encode encodes header data per the STOMP 1.1 specification.
|
18
20
|
def self.encode(in_string = nil)
|
19
21
|
return in_string unless in_string
|
20
22
|
ev = Stomp::ENCODE_VALUES # avoid typing below
|
@@ -25,7 +27,7 @@ module Stomp
|
|
25
27
|
os
|
26
28
|
end
|
27
29
|
|
28
|
-
#
|
30
|
+
# decode decodes header data per the STOMP 1.1 specification.
|
29
31
|
def self.decode(in_string = nil)
|
30
32
|
return in_string unless in_string
|
31
33
|
ev = Stomp::DECODE_VALUES # avoid typing below
|
@@ -37,5 +39,6 @@ module Stomp
|
|
37
39
|
end
|
38
40
|
|
39
41
|
end # of class HeaderCodec
|
42
|
+
|
40
43
|
end # of module Stomp
|
41
44
|
|