stomp 1.2.16 → 1.3.0

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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MDM0Njc0MjJhMzYzOTBjZGQ0NGZiZTM4MDA2MzRlNzgwZmE2M2JmZg==
4
+ NzAwZGNiZmI2NzJkZWZkNWNlZWY4NDZiZTU3NjFiNzhkNjRmNDkyOA==
5
5
  data.tar.gz: !binary |-
6
- YmY5YmNlNTFkMTEwMzFlMDRjYWJlNWQ4MmRjZWEwNWJmNjU3YTZjZA==
6
+ MmUyNDgwOWVmOWYxMmRmMjIxMTZlZDQxMmI3ODcxMTk5ODRlNzQyNA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZmRmZWI0MGUyMDZjNDM2Yjc3YjY5ZmRiODQwZmNhZDIwYmNiOGY0NDczMWI2
10
- Nzg2NWNiMWQ4YmY4YWQ5MzRhNjAwOTZlYWUwZDU4ZDhhYWNkMmJmYmI1MDFl
11
- ZDFkOWNmMDdjZTgxZmQ3NmZkYTk5YmYzMDk2ZTljYmE4ZjhlOTk=
9
+ NjA2MDRjODc2MjEwODVjNTFjZDRkYzFmZDdhNGVmN2FiN2NmYjNkOTE2YmIz
10
+ YzExNWI3OWE1ZjEzMDMwNmJiNDAxYmYxOGFiMjhiYjEzZGY4YWMxMzc5NzI1
11
+ NTUyNjgwYmQwYjQyNzlkNjlkYmM4M2VkN2QwZjYwZDUyMTU4NWI=
12
12
  data.tar.gz: !binary |-
13
- MjQ5YWQ3MjYwMjAyYWVkNDRiY2Q1ZGUxYTU0MzFhOTE1NGNlYjU1MGRjN2I3
14
- NzU4ZDJkNmZiYTZkNjU3YmQ0MTQxOGUzM2ZhMDIxNmY4NGYwNDhjYTcxNGQ5
15
- MWVmNzI5ZjIwMjEwN2E2ZjM0MzBkM2RmZmEwMjk3NTNiNWM2MzM=
13
+ OTNmMjliOTM4NmI3NmRjOTllNzc0NWE2MDM2YTZlZTg2ZTk1YTBlMDEwMjRk
14
+ YTk2MGM3ZDFkNzUxZDc3OGI4ZmRkNWI5OWJhZTdjOGVlMzY0MmU3YzljMDU5
15
+ NWMxZTkwMTk4ZDAwODA2ZDA5YTZiZmQ5N2Q1M2JhMDJkNjZhMmY=
@@ -1,3 +1,13 @@
1
+ == 1.3.0 20130930
2
+
3
+ * ERROR frames now raise an exception in the Stomp::Client thread(#73, #81)
4
+ * Allow anonymous connections (#75)
5
+ * Fix for subscription id handling in STOMP 1.1 (#78)
6
+ * Added a NullLogger (#77)
7
+ * Added :tcp_nodelay option (disable Nagle's algorithm) (#76)
8
+ * Read receipt ids are now UUIDs
9
+ * Added a :start_timeout parameter
10
+
1
11
  == 1.2.16 20130812
2
12
 
3
13
  * Stomp::Client's should expose connection's host params
@@ -12,6 +12,7 @@ An implementation of the Stomp protocol for Ruby. See:
12
12
 
13
13
  See _CHANGELOG.rdoc_ for details.
14
14
 
15
+ * Gem version 1.3.0. Added ERROR frame raising as exception, added anonymous connections, miscellaneous other fixes.
15
16
  * Gem version 1.2.16. Fixed Stomp::Client to expose its connection's host parameters.
16
17
  * Gem version 1.2.15. Timeout cleanup, added license info to gemspec.
17
18
  * Gem version 1.2.14. Cleanup.
@@ -63,6 +64,7 @@ See _CHANGELOG.rdoc_ for details.
63
64
  # For fast heartbeat senders. 'fast' == YMMV. If not
64
65
  # correct for your environment, expect unnecessary fail overs
65
66
  :connread_timeout => 0, # Timeout during CONNECT for read of CONNECTED/ERROR, secs
67
+ :tcp_nodelay => true, # Turns on the TCP_NODELAY socket option; disables Nagle's algorithm
66
68
  }
67
69
 
68
70
  # for client
@@ -71,33 +71,22 @@ module Stomp
71
71
  end
72
72
  end
73
73
 
74
- # Register a receipt listener.
75
- def register_receipt_listener(listener)
76
- id = -1
77
- @id_mutex.synchronize do
78
- id = @ids.to_s
79
- @ids = @ids.succ
74
+ # Parse a stomp URL.
75
+ def parse_hosts(url)
76
+ hosts = []
77
+ host_match = /stomp(\+ssl)?:\/\/#{URL_REPAT}/
78
+ url.scan(host_match).each do |match|
79
+ host = {}
80
+ host[:ssl] = match[0] == "+ssl" ? true : false
81
+ host[:login] = match[3] || ""
82
+ host[:passcode] = match[4] || ""
83
+ host[:host] = match[5]
84
+ host[:port] = match[6].to_i
85
+ hosts << host
80
86
  end
81
- @receipt_listeners[id] = listener
82
- id
87
+ hosts
83
88
  end
84
89
 
85
- # Parse a stomp URL.
86
- def parse_hosts(url)
87
- hosts = []
88
- host_match = /stomp(\+ssl)?:\/\/#{URL_REPAT}/
89
- url.scan(host_match).each do |match|
90
- host = {}
91
- host[:ssl] = match[0] == "+ssl" ? true : false
92
- host[:login] = match[3] || ""
93
- host[:passcode] = match[4] || ""
94
- host[:host] = match[5]
95
- host[:port] = match[6].to_i
96
- hosts << host
97
- end
98
- hosts
99
- end
100
-
101
90
  # A very basic check of required arguments.
102
91
  def check_arguments!()
103
92
  first_host = @parameters && @parameters[:hosts] && @parameters[:hosts].first
@@ -131,35 +120,54 @@ end
131
120
  set_subscription_id_if_missing(message.headers['destination'], message.headers)
132
121
  subscription_id = message.headers[:id]
133
122
  end
134
- @listeners[subscription_id]
123
+
124
+ listener = @listeners[subscription_id]
125
+ listener.call(message) if listener
135
126
  end
136
127
 
137
- # Start a single listener thread. Misnamed I think.
138
- def start_listeners()
128
+ # Register a receipt listener.
129
+ def register_receipt_listener(listener)
130
+ id = uuid
131
+ @receipt_listeners[id] = listener
132
+ id
133
+ end
134
+
135
+ def find_receipt_listener(message)
136
+ listener = @receipt_listeners[message.headers['receipt-id']]
137
+ listener.call(message) if listener
138
+ end
139
+
140
+ def create_listener_maps
139
141
  @listeners = {}
140
142
  @receipt_listeners = {}
141
143
  @replay_messages_by_txn = {}
142
144
 
145
+ @listener_map = Hash.new do |message|
146
+ @logger.on_miscerr(@connection.log_params, "Received unknown frame type: '#{message.command}'\n")
147
+ end
148
+
149
+ @listener_map[Stomp::CMD_MESSAGE] = lambda {|message| find_listener(message) }
150
+ @listener_map[Stomp::CMD_RECEIPT] = lambda {|message| find_receipt_listener(message) }
151
+ @listener_map[Stomp::CMD_ERROR] = @error_listener
152
+ end
153
+
154
+ # Start a single listener thread. Misnamed I think.
155
+ def start_listeners()
156
+ create_listener_maps
157
+
143
158
  @listener_thread = Thread.start do
144
- while true
159
+ loop do
145
160
  message = @connection.receive
146
161
  # AMQ specific behavior
147
162
  if message.nil? && (!@parameters[:reliable])
148
163
  raise Stomp::Error::NilMessageError
149
164
  end
150
- if message # message can be nil on rapid AMQ stop / start sequences
151
- # OK, we have some real data
152
- if message.command == Stomp::CMD_MESSAGE
153
- if listener = find_listener(message)
154
- listener.call(message)
155
- end
156
- elsif message.command == Stomp::CMD_RECEIPT
157
- if listener = @receipt_listeners[message.headers['receipt-id']]
158
- listener.call(message)
159
- end
160
- end
161
- end
162
- end # while true
165
+
166
+ next unless message # message can be nil on rapid AMQ stop/start sequences
167
+
168
+ @listener_map[message.command].call(message)
169
+ end
170
+
163
171
  end
164
172
  end # method start_listeners
165
173
 
@@ -100,20 +100,17 @@ module Stomp
100
100
  sleep(slt)
101
101
  next unless @socket # nil under some circumstances ??
102
102
  curt = Time.now.to_f
103
- if @logger && @logger.respond_to?(:on_hbfire)
104
- @logger.on_hbfire(log_params, "send_fire", :curt => curt, :last_sleep => slt)
105
- end
103
+ @logger.on_hbfire(log_params, "send_fire", :curt => curt, :last_sleep => slt)
104
+
106
105
  delta = curt - @ls
107
106
  # Be tolerant (minus), and always do this the first time through.
108
107
  # Reintroduce logic removed in d922fa.
109
108
  compval = (@hbsend_interval - (@hbsend_interval/5.0)) / 1000000.0
110
109
  if delta > compval || first_time
111
110
  first_time = false
112
- if @logger && @logger.respond_to?(:on_hbfire)
113
- @logger.on_hbfire(log_params, "send_heartbeat", :last_sleep => slt,
114
- :curt => curt, :last_send => @ls, :delta => delta,
115
- :compval => compval)
116
- end
111
+ @logger.on_hbfire(log_params, "send_heartbeat", :last_sleep => slt,
112
+ :curt => curt, :last_send => @ls, :delta => delta,
113
+ :compval => compval)
117
114
  # Send a heartbeat
118
115
  @transmit_semaphore.synchronize do
119
116
  begin
@@ -124,10 +121,8 @@ module Stomp
124
121
  @hbsend_count += 1
125
122
  rescue Exception => sendex
126
123
  @hb_sent = false # Set the warning flag
127
- if @logger && @logger.respond_to?(:on_hbwrite_fail)
128
- @logger.on_hbwrite_fail(log_params, {"ticker_interval" => sleeptime,
129
- "exception" => sendex})
130
- end
124
+ @logger.on_hbwrite_fail(log_params, {"ticker_interval" => sleeptime,
125
+ "exception" => sendex})
131
126
  if @hbser
132
127
  raise # Re-raise if user requested this, otherwise ignore
133
128
  end
@@ -165,16 +160,12 @@ module Stomp
165
160
  next unless @socket # nil under some circumstances ??
166
161
  rdrdy = _is_ready?(@socket)
167
162
  curt = Time.now.to_f
168
- if @logger && @logger.respond_to?(:on_hbfire)
169
- @logger.on_hbfire(log_params, "receive_fire", :curt => curt)
170
- end
163
+ @logger.on_hbfire(log_params, "receive_fire", :curt => curt)
171
164
  #
172
165
  begin
173
166
  delta = curt - @lr
174
167
  if delta > sleeptime
175
- if @logger && @logger.respond_to?(:on_hbfire)
176
- @logger.on_hbfire(log_params, "receive_heartbeat", {})
177
- end
168
+ @logger.on_hbfire(log_params, "receive_heartbeat", {})
178
169
  # Client code could be off doing something else (that is, no reading of
179
170
  # the socket has been requested by the caller). Try to handle that case.
180
171
  lock = @read_semaphore.try_lock
@@ -201,22 +192,20 @@ module Stomp
201
192
  @read_semaphore.unlock # Release read lock
202
193
  @hb_received = false
203
194
  read_fail_count += 1
204
- if @logger && @logger.respond_to?(:on_hbread_fail)
205
- @logger.on_hbread_fail(log_params, {"ticker_interval" => sleeptime,
206
- "read_fail_count" => read_fail_count, "lock_fail" => false,
207
- "lock_fail_count" => lock_fail_count})
208
- end
195
+ @logger.on_hbread_fail(log_params, {"ticker_interval" => sleeptime,
196
+ "read_fail_count" => read_fail_count,
197
+ "lock_fail" => false,
198
+ "lock_fail_count" => lock_fail_count})
209
199
  end
210
200
  else # try_lock failed
211
201
  # Shrug. Could not get lock. Client must be actually be reading.
212
202
  @hb_received = false
213
203
  # But notify caller if possible
214
204
  lock_fail_count += 1
215
- if @logger && @logger.respond_to?(:on_hbread_fail)
216
- @logger.on_hbread_fail(log_params, {"ticker_interval" => sleeptime,
217
- "read_fail_count" => read_fail_count, "lock_fail" => true,
218
- "lock_fail_count" => lock_fail_count})
219
- end
205
+ @logger.on_hbread_fail(log_params, {"ticker_interval" => sleeptime,
206
+ "read_fail_count" => read_fail_count,
207
+ "lock_fail" => true,
208
+ "lock_fail_count" => lock_fail_count})
220
209
  end # of the try_lock
221
210
 
222
211
  else # delta <= sleeptime
@@ -225,11 +214,10 @@ module Stomp
225
214
  lock_fail_count = 0 # reset
226
215
  end # of the if delta > sleeptime
227
216
  rescue Exception => recvex
228
- if @logger && @logger.respond_to?(:on_hbread_fail)
229
- @logger.on_hbread_fail(log_params, {"ticker_interval" => sleeptime,
230
- "exception" => recvex, "read_fail_count" => read_fail_count,
231
- "lock_fail_count" => lock_fail_count})
232
- end
217
+ @logger.on_hbread_fail(log_params, {"ticker_interval" => sleeptime,
218
+ "exception" => recvex,
219
+ "read_fail_count" => read_fail_count,
220
+ "lock_fail_count" => lock_fail_count})
233
221
  fail_hard = true
234
222
  end
235
223
  # Do we want to attempt a retry?
@@ -130,11 +130,8 @@ module Stomp
130
130
  @failure = $!
131
131
  raise unless @reliable
132
132
  errstr = "transmit to #{@host} failed: #{$!}\n"
133
- if @logger && @logger.respond_to?(:on_miscerr)
134
- @logger.on_miscerr(log_params, "es_trans: " + errstr)
135
- else
136
- $stderr.print errstr
137
- end
133
+ @logger.on_miscerr(log_params, "es_trans: " + errstr)
134
+ $stderr.print errstr
138
135
  # !!! This loop initiates a re-connect !!!
139
136
  _reconn_prep()
140
137
  end
@@ -201,9 +198,7 @@ module Stomp
201
198
  def open_tcp_socket()
202
199
  tcp_socket = nil
203
200
 
204
- if @logger && @logger.respond_to?(:on_connecting)
205
- @logger.on_connecting(log_params)
206
- end
201
+ @logger.on_connecting(log_params)
207
202
 
208
203
  Timeout::timeout(@connect_timeout, Stomp::Error::SocketOpenTimeout) do
209
204
  tcp_socket = TCPSocket.open(@host, @port)
@@ -281,9 +276,7 @@ module Stomp
281
276
 
282
277
  #
283
278
  ssl = nil
284
- if @logger && @logger.respond_to?(:on_ssl_connecting)
285
- @logger.on_ssl_connecting(log_params)
286
- end
279
+ @logger.on_ssl_connecting(log_params)
287
280
 
288
281
  Timeout::timeout(@connect_timeout, Stomp::Error::SocketOpenTimeout) do
289
282
  tcp_socket = TCPSocket.open(@host, @port)
@@ -304,16 +297,12 @@ module Stomp
304
297
  end
305
298
  @ssl.peer_cert = ssl.peer_cert
306
299
  end
307
- if @logger && @logger.respond_to?(:on_ssl_connected)
308
- @logger.on_ssl_connected(log_params)
309
- end
300
+ @logger.on_ssl_connected(log_params)
310
301
  ssl
311
302
  rescue Exception => ex
312
- if @logger && @logger.respond_to?(:on_ssl_connectfail)
313
- lp = log_params.clone
314
- lp[:ssl_exception] = ex
315
- @logger.on_ssl_connectfail(lp)
316
- end
303
+ lp = log_params.clone
304
+ lp[:ssl_exception] = ex
305
+ @logger.on_ssl_connectfail(lp)
317
306
  #
318
307
  raise # Reraise
319
308
  end
@@ -343,11 +332,15 @@ module Stomp
343
332
  @closed = false
344
333
  if @parameters # nil in some rspec tests
345
334
  unless @reconnect_delay
346
- @reconnect_delay = @parameters[:initial_reconnect_delay] ? @parameters[:initial_reconnect_delay] : 0.01
335
+ @reconnect_delay = @parameters[:initial_reconnect_delay] || 0.01
347
336
  end
348
337
  end
349
338
  # Use keepalive
350
339
  used_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
340
+
341
+ # TCP_NODELAY option (disables Nagle's algorithm)
342
+ used_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, !!(@parameters && @parameters[:tcp_nodelay]))
343
+
351
344
  used_socket
352
345
  end
353
346
 
@@ -355,8 +348,8 @@ module Stomp
355
348
  def connect(used_socket)
356
349
  @connect_headers = {} unless @connect_headers # Caller said nil/false
357
350
  headers = @connect_headers.clone
358
- headers[:login] = @login
359
- headers[:passcode] = @passcode
351
+ headers[:login] = @login unless @login.to_s.empty?
352
+ headers[:passcode] = @passcode unless @login.to_s.empty?
360
353
  _pre_connect
361
354
  if !@hhas10 && @stompconn
362
355
  _transmit(used_socket, Stomp::CMD_STOMP, headers)
@@ -116,9 +116,7 @@ module Stomp
116
116
  used_socket = open_socket() # sets @closed = false if OK
117
117
  # Open is complete
118
118
  connect(used_socket)
119
- if @logger && @logger.respond_to?(:on_connected)
120
- @logger.on_connected(log_params)
121
- end
119
+ @logger.on_connected(log_params)
122
120
  @connection_attempts = 0
123
121
  rescue
124
122
  @failure = $!
@@ -132,16 +130,13 @@ module Stomp
132
130
  # b) should never be retried
133
131
  raise if @failure.is_a?(Stomp::Error::ProtocolError11p)
134
132
 
135
- if @logger && @logger.respond_to?(:on_connectfail)
136
- # on_connectfail may raise
137
- begin
138
- @logger.on_connectfail(log_params)
139
- rescue Exception => aex
140
- raise if aex.is_a?(Stomp::Error::LoggerConnectionError)
141
- end
142
- else
143
- $stderr.print "connect to #{@host} failed: #{$!} will retry(##{@connection_attempts}) in #{@reconnect_delay}\n"
133
+ # on_connectfail may raise
134
+ begin
135
+ @logger.on_connectfail(log_params)
136
+ rescue Exception => aex
137
+ raise if aex.is_a?(Stomp::Error::LoggerConnectionError)
144
138
  end
139
+ $stderr.print "connect to #{@host} failed: #{$!} will retry(##{@connection_attempts}) in #{@reconnect_delay}\n"
145
140
  raise Stomp::Error::MaxReconnectAttempts if max_reconnect_attempts?
146
141
 
147
142
  sleep(@reconnect_delay)
@@ -183,6 +178,7 @@ module Stomp
183
178
  :max_hbrlck_fails => 0,
184
179
  :fast_hbs_adjust => 0.0,
185
180
  :connread_timeout => 0,
181
+ :tcp_nodelay => true
186
182
  }
187
183
 
188
184
  res_params = default_params.merge(params)
@@ -236,11 +232,9 @@ module Stomp
236
232
  @failure = $!
237
233
  raise unless @reliable
238
234
  errstr = "receive failed: #{$!}"
239
- if @logger && @logger.respond_to?(:on_miscerr)
240
- @logger.on_miscerr(log_params, "es_oldrecv: " + errstr)
241
- else
242
- $stderr.print errstr
243
- end
235
+ @logger.on_miscerr(log_params, "es_oldrecv: " + errstr)
236
+ $stderr.print errstr
237
+
244
238
  # !!! This initiates a re-connect !!!
245
239
  _reconn_prep()
246
240
  end
@@ -24,6 +24,7 @@ require 'stomp/version' # Stomp#Version#STRING
24
24
  require 'stomp/errors' # All Stomp# exceptions
25
25
  require 'stomp/codec' # Stomp 1.1 codec
26
26
  require 'stomp/sslparams' # Stomp SSL support
27
+ require 'stomp/null_logger' # A NullLogger class
27
28
 
28
29
  # Private methods in #Client
29
30
  require 'client/utils' # private Client Utility methods
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'thread'
4
4
  require 'digest/sha1'
5
+ require 'timeout'
5
6
  require 'forwardable'
6
7
 
7
8
  module Stomp
@@ -80,13 +81,31 @@ module Stomp
80
81
 
81
82
  check_arguments!()
82
83
 
83
- @id_mutex = Mutex.new()
84
- @ids = 1
84
+ @logger = @parameters[:logger] ||= Stomp::NullLogger.new
85
85
 
86
- create_connection(autoflush)
86
+ @start_timeout = @parameters[:start_timeout] || 10
87
+ Timeout.timeout(@start_timeout, Stomp::Error::StartTimeoutException.new(@start_timeout)) do
88
+ create_error_handler
89
+ create_connection(autoflush)
90
+ start_listeners()
91
+ end
92
+ end
93
+
94
+ def create_error_handler
95
+ client_thread = Thread.current
87
96
 
88
- start_listeners()
97
+ @error_listener = lambda do |error|
98
+ exception = case error.body
99
+ when /ResourceAllocationException/i
100
+ Stomp::Error::ProducerFlowControlException.new(error)
101
+ when /ProtocolException/i
102
+ Stomp::Error::ProtocolException.new(error)
103
+ else
104
+ Stomp::Error::BrokerException.new(error)
105
+ end
89
106
 
107
+ client_thread.raise exception
108
+ end
90
109
  end
91
110
 
92
111
  def create_connection(autoflush)
@@ -119,9 +138,7 @@ module Stomp
119
138
  replay_list = @replay_messages_by_txn[name]
120
139
  if replay_list
121
140
  replay_list.each do |message|
122
- if listener = find_listener(message)
123
- listener.call(message)
124
- end
141
+ find_listener(message) # find_listener also calls the listener
125
142
  end
126
143
  end
127
144
  end
@@ -159,7 +176,7 @@ module Stomp
159
176
  # Acknowledge a message, used when a subscription has specified
160
177
  # client acknowledgement ( connection.subscribe("/queue/a",{:ack => 'client'}).
161
178
  # Accepts a transaction header ( :transaction => 'some_transaction_id' ).
162
- def acknowledge(message, headers = {})
179
+ def ack(message, headers = {})
163
180
  txn_id = headers[:transaction]
164
181
  if txn_id
165
182
  # lets keep around messages ack'd in this transaction in case we rollback
@@ -175,14 +192,20 @@ module Stomp
175
192
  end
176
193
  if protocol() == Stomp::SPL_12
177
194
  @connection.ack(message.headers['ack'], headers)
195
+ elsif protocol == Stomp::SPL_11
196
+ headers.merge!(:subscription => message.headers['subscription'])
197
+ @connection.ack(message.headers['message-id'], headers)
178
198
  else
179
199
  @connection.ack(message.headers['message-id'], headers)
180
200
  end
181
201
  end
182
202
 
203
+ # For posterity, we alias:
204
+ alias acknowledge ack
205
+
183
206
  # Stomp 1.1+ NACK.
184
- def nack(message_id, headers = {})
185
- @connection.nack(message_id, headers)
207
+ def nack(message, headers = {})
208
+ @connection.nack(message, headers)
186
209
  end
187
210
 
188
211
  # Unreceive a message, sending it back to its queue or to the DLQ.
@@ -240,6 +263,7 @@ module Stomp
240
263
 
241
264
  # set_logger identifies a new callback logger.
242
265
  def set_logger(logger)
266
+ @logger = logger
243
267
  @connection.set_logger(logger)
244
268
  end
245
269