stomp 1.2.16 → 1.3.0

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