stomp 1.3.5 → 1.4.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.
- data/{CHANGELOG.rdoc → CHANGELOG.md} +57 -40
- data/README.md +708 -0
- data/Rakefile +10 -11
- data/adhoc/.gitignore +7 -0
- data/adhoc/README.md +16 -0
- data/adhoc/issue121_01.rb +129 -0
- data/adhoc/issue121_01_conn.rb +158 -0
- data/adhoc/issue121_02.rb +152 -0
- data/adhoc/issue121_03.rb +157 -0
- data/adhoc/payload_generator.rb +32 -0
- data/adhoc/payload_generator_adhoctest.rb +41 -0
- data/adhoc/stomp_adhoc_common.rb +99 -0
- data/examples/consume_file.rb +63 -0
- data/examples/contrib.sh +6 -0
- data/examples/contributors.rb +106 -0
- data/examples/lflogger.rb +316 -0
- data/examples/publish_file.rb +76 -0
- data/examples/publish_file_conn.rb +75 -0
- data/examples/stomp11_common.rb +7 -1
- data/lib/client/utils.rb +10 -2
- data/lib/connection/heartbeats.rb +1 -0
- data/lib/connection/netio.rb +384 -309
- data/lib/connection/utils.rb +1 -1
- data/lib/stomp/client.rb +30 -23
- data/lib/stomp/connection.rb +28 -23
- data/lib/stomp/constants.rb +4 -0
- data/lib/stomp/errors.rb +9 -0
- data/lib/stomp/version.rb +2 -2
- data/stomp.gemspec +23 -64
- data/test/.gitignore +3 -0
- data/test/test_anonymous.rb +4 -4
- data/test/test_client.rb +14 -11
- data/test/test_connection.rb +4 -4
- data/test/test_connection1p.rb +4 -4
- data/test/test_helper.rb +4 -2
- data/test/test_message.rb +16 -16
- data/test/test_ssl.rb +8 -8
- data/test/test_urlogin.rb +26 -10
- metadata +26 -67
- data/README.rdoc +0 -163
data/lib/connection/netio.rb
CHANGED
@@ -9,59 +9,83 @@ module Stomp
|
|
9
9
|
|
10
10
|
class Connection
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
12
|
+
private
|
13
|
+
|
14
|
+
# Really read from the wire.
|
15
|
+
def _receive(read_socket, connread = false)
|
16
|
+
# p [ "ioscheck", @iosto, connread ]
|
17
|
+
# _dump_callstack()
|
18
|
+
# drdbg = true
|
19
|
+
drdbg = false
|
20
|
+
@read_semaphore.synchronize do
|
21
|
+
line = nil
|
22
|
+
if connread
|
23
|
+
begin
|
24
|
+
Timeout::timeout(@connread_timeout, Stomp::Error::ConnectReadTimeout) do
|
25
|
+
line = _init_line_read(read_socket)
|
26
|
+
end
|
27
|
+
rescue Stomp::Error::ConnectReadTimeout => ex
|
28
|
+
if @reliable
|
29
|
+
_reconn_prep()
|
30
|
+
end
|
31
|
+
raise ex
|
26
32
|
end
|
27
|
-
|
33
|
+
else
|
34
|
+
p [ "CONR01" ] if drdbg
|
35
|
+
_dump_callstack() if drdbg
|
36
|
+
line = _init_line_read(read_socket)
|
28
37
|
end
|
29
|
-
|
30
|
-
line
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
line = _normalize_line_end(line) if @protocol >= Stomp::SPL_12
|
38
|
-
# If the reading hangs for more than X seconds, abort the parsing process.
|
39
|
-
# X defaults to 5. Override allowed in connection hash parameters.
|
40
|
-
Timeout::timeout(@parse_timeout, Stomp::Error::PacketParsingTimeout) do
|
38
|
+
#
|
39
|
+
p [ "nilcheck", line.nil? ] if drdbg
|
40
|
+
return nil if line.nil?
|
41
|
+
#An extra \n at the beginning of the frame, possibly not caught by is_ready?
|
42
|
+
line = '' if line == "\n"
|
43
|
+
p [ "wiredatain_01A", line, Time.now ] if drdbg
|
44
|
+
line = _normalize_line_end(line) if @protocol >= Stomp::SPL_12
|
45
|
+
p [ "wiredatain_01B", line, Time.now ] if drdbg
|
41
46
|
# Reads the beginning of the message until it runs into a empty line
|
42
47
|
message_header = ''
|
43
48
|
begin
|
44
49
|
message_header += line
|
50
|
+
p [ "wiredatain_02A", line, Time.now ] if drdbg
|
51
|
+
unless connread
|
52
|
+
raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto)
|
53
|
+
end
|
54
|
+
p [ "wiredatain_02B", line, Time.now ] if drdbg
|
45
55
|
line = read_socket.gets
|
46
|
-
|
47
|
-
raise
|
56
|
+
p [ "wiredatain_02C", line ] if drdbg
|
57
|
+
raise if line.nil?
|
48
58
|
line = _normalize_line_end(line) if @protocol >= Stomp::SPL_12
|
59
|
+
p [ "wiredatain_02D", line ] if drdbg
|
49
60
|
end until line =~ /^\s?\n$/
|
50
|
-
|
61
|
+
p [ "wiredatain_03A" ] if drdbg
|
51
62
|
# Checks if it includes content_length header
|
52
|
-
content_length = message_header.match
|
63
|
+
content_length = message_header.match(/content-length\s?:\s?(\d+)\s?\n/)
|
53
64
|
message_body = ''
|
54
65
|
|
66
|
+
p [ "wiredatain_03B", content_length ] if drdbg
|
55
67
|
# If content_length is present, read the specified amount of bytes
|
56
68
|
if content_length
|
69
|
+
unless connread
|
70
|
+
raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto)
|
71
|
+
end
|
72
|
+
p [ "CL01" ] if drdbg
|
57
73
|
message_body = read_socket.read content_length[1].to_i
|
74
|
+
unless connread
|
75
|
+
raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto)
|
76
|
+
end
|
58
77
|
raise Stomp::Error::InvalidMessageLength unless parse_char(read_socket.getc) == "\0"
|
59
78
|
# Else read the rest of the message until the first \0
|
60
79
|
else
|
80
|
+
unless connread
|
81
|
+
raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto)
|
82
|
+
end
|
83
|
+
p [ "NOCL01" ] if drdbg
|
61
84
|
message_body = read_socket.readline("\0")
|
62
85
|
message_body.chop!
|
63
86
|
end
|
64
87
|
|
88
|
+
p [ "wiredatain_04" ] if drdbg
|
65
89
|
# If the buffer isn't empty, reads trailing new lines.
|
66
90
|
#
|
67
91
|
# Note: experiments with JRuby seem to show that socket.ready? never
|
@@ -73,7 +97,12 @@ module Stomp
|
|
73
97
|
# is read. Do _not_ leave them on the wire and attempt to drain them
|
74
98
|
# at the start of the next read. Attempting to do that breaks the
|
75
99
|
# asynchronous nature of the 'poll' method.
|
100
|
+
p [ "wiredatain_05_prep", "isr", _is_ready?(read_socket) ] if drdbg
|
76
101
|
while _is_ready?(read_socket)
|
102
|
+
unless connread
|
103
|
+
raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto)
|
104
|
+
end
|
105
|
+
p [ "WHIR01" ] if drdbg
|
77
106
|
last_char = read_socket.getc
|
78
107
|
break unless last_char
|
79
108
|
if parse_char(last_char) != "\n"
|
@@ -81,337 +110,383 @@ module Stomp
|
|
81
110
|
break
|
82
111
|
end
|
83
112
|
end
|
84
|
-
|
113
|
+
p [ "wiredatain_05A" ] if drdbg
|
85
114
|
if @protocol >= Stomp::SPL_11
|
86
115
|
@lr = Time.now.to_f if @hbr
|
87
116
|
end
|
88
117
|
# Adds the excluded \n and \0 and tries to create a new message with it
|
118
|
+
p [ "wiredatain_05B" ] if drdbg
|
89
119
|
msg = Message.new(message_header + "\n" + message_body + "\0", @protocol >= Stomp::SPL_11)
|
120
|
+
p [ "wiredatain_06", msg.command, msg.headers ] if drdbg
|
90
121
|
#
|
91
122
|
if @protocol >= Stomp::SPL_11 && msg.command != Stomp::CMD_CONNECTED
|
92
123
|
msg.headers = _decodeHeaders(msg.headers)
|
93
124
|
end
|
125
|
+
p [ "wiredatain_99", msg.command, msg.headers ] if drdbg
|
94
126
|
msg
|
95
127
|
end
|
96
128
|
end
|
97
|
-
end
|
98
129
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
rdy =
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
return line unless @usecrlf
|
113
|
-
# p [ "nleln", line ]
|
114
|
-
line_len = line.respond_to?(:bytesize) ? line.bytesize : line.length
|
115
|
-
last2 = line[line_len-2...line_len]
|
116
|
-
# p [ "nlel2", last2 ]
|
117
|
-
return line unless last2 == "\r\n"
|
118
|
-
return line[0...line_len-2] + "\n"
|
119
|
-
end
|
120
|
-
|
121
|
-
# transmit logically puts a Message on the wire.
|
122
|
-
def transmit(command, headers = {}, body = '')
|
123
|
-
# The transmit may fail so we may need to retry.
|
124
|
-
while TRUE
|
125
|
-
begin
|
126
|
-
used_socket = socket()
|
127
|
-
_transmit(used_socket, command, headers, body)
|
128
|
-
return
|
129
|
-
rescue Stomp::Error::MaxReconnectAttempts => e
|
130
|
-
raise
|
131
|
-
rescue
|
132
|
-
@failure = $!
|
133
|
-
raise unless @reliable
|
134
|
-
errstr = "transmit to #{@host} failed: #{$!}\n"
|
135
|
-
unless slog(:on_miscerr, log_params, "es_trans: " + errstr)
|
136
|
-
$stderr.print errstr
|
137
|
-
end
|
138
|
-
# !!! This loop initiates a re-connect !!!
|
139
|
-
_reconn_prep()
|
130
|
+
#
|
131
|
+
# This is a total hack, to try and guess how JRuby will behave today.
|
132
|
+
#
|
133
|
+
def _is_ready?(s)
|
134
|
+
rdy = s.ready?
|
135
|
+
### p [ "isr?", rdy ]
|
136
|
+
return rdy unless @jruby
|
137
|
+
### p [ "jrdychk", rdy.class ]
|
138
|
+
if rdy.class == NilClass
|
139
|
+
# rdy = true
|
140
|
+
rdy = false # A test
|
141
|
+
else
|
142
|
+
rdy = (rdy.class == Fixnum || rdy.class == TrueClass) ? true : false
|
140
143
|
end
|
144
|
+
### p [ "isr?_last", rdy ]
|
145
|
+
rdy
|
141
146
|
end
|
142
|
-
end
|
143
147
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
+
|
149
|
+
# Normalize line ends because 1.2+ brokers can send 'mixed mode' headers, i.e.:
|
150
|
+
# - Some headers end with '\n'
|
151
|
+
# - Other headers end with '\r\n'
|
152
|
+
def _normalize_line_end(line)
|
153
|
+
return line unless @usecrlf
|
154
|
+
# p [ "nleln", line ]
|
155
|
+
line_len = line.respond_to?(:bytesize) ? line.bytesize : line.length
|
156
|
+
last2 = line[line_len-2...line_len]
|
157
|
+
# p [ "nlel2", last2 ]
|
158
|
+
return line unless last2 == "\r\n"
|
159
|
+
return line[0...line_len-2] + "\n"
|
148
160
|
end
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
# The
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
if v.is_a?(Array)
|
168
|
-
v.each do |e|
|
169
|
-
_wire_write(used_socket,"#{k}:#{e}")
|
161
|
+
|
162
|
+
# transmit logically puts a Message on the wire.
|
163
|
+
def transmit(command, headers = {}, body = '')
|
164
|
+
# The transmit may fail so we may need to retry.
|
165
|
+
while TRUE
|
166
|
+
begin
|
167
|
+
used_socket = socket()
|
168
|
+
_transmit(used_socket, command, headers, body)
|
169
|
+
return
|
170
|
+
rescue Stomp::Error::MaxReconnectAttempts => e
|
171
|
+
_ = e
|
172
|
+
raise
|
173
|
+
rescue
|
174
|
+
@failure = $!
|
175
|
+
raise unless @reliable
|
176
|
+
errstr = "transmit to #{@host} failed: #{$!}\n"
|
177
|
+
unless slog(:on_miscerr, log_params, "es_trans: " + errstr)
|
178
|
+
$stderr.print errstr
|
170
179
|
end
|
171
|
-
|
172
|
-
|
180
|
+
# !!! This loop initiates a re-connect !!!
|
181
|
+
_reconn_prep()
|
173
182
|
end
|
174
183
|
end
|
175
|
-
|
176
|
-
used_socket.write body unless body == ''
|
177
|
-
used_socket.write "\0"
|
178
|
-
used_socket.flush if autoflush
|
184
|
+
end
|
179
185
|
|
180
|
-
|
181
|
-
|
186
|
+
# _transmit is the real wire write logic.
|
187
|
+
def _transmit(used_socket, command, headers = {}, body = '')
|
188
|
+
|
189
|
+
# p [ "wirewrite" ]
|
190
|
+
# _dump_callstack()
|
191
|
+
|
192
|
+
if @protocol >= Stomp::SPL_11 && command != Stomp::CMD_CONNECT
|
193
|
+
headers = _encodeHeaders(headers)
|
182
194
|
end
|
195
|
+
@transmit_semaphore.synchronize do
|
196
|
+
# Handle nil body
|
197
|
+
body = '' if body.nil?
|
198
|
+
# The content-length should be expressed in bytes.
|
199
|
+
# Ruby 1.8: String#length => # of bytes; Ruby 1.9: String#length => # of characters
|
200
|
+
# With Unicode strings, # of bytes != # of characters. So, use String#bytesize when available.
|
201
|
+
body_length_bytes = body.respond_to?(:bytesize) ? body.bytesize : body.length
|
202
|
+
|
203
|
+
# ActiveMQ interprets every message as a BinaryMessage
|
204
|
+
# if content_length header is included.
|
205
|
+
# Using :suppress_content_length => true will suppress this behaviour
|
206
|
+
# and ActiveMQ will interpret the message as a TextMessage.
|
207
|
+
# For more information refer to http://juretta.com/log/2009/05/24/activemq-jms-stomp/
|
208
|
+
# Lets send this header in the message, so it can maintain state when using unreceive
|
209
|
+
headers[:'content-length'] = "#{body_length_bytes}" unless headers[:suppress_content_length]
|
210
|
+
headers[:'content-type'] = "text/plain; charset=UTF-8" unless headers[:'content-type']
|
211
|
+
_wire_write(used_socket,command)
|
212
|
+
headers.each do |k,v|
|
213
|
+
if v.is_a?(Array)
|
214
|
+
v.each do |e|
|
215
|
+
_wire_write(used_socket,"#{k}:#{e}")
|
216
|
+
end
|
217
|
+
else
|
218
|
+
_wire_write(used_socket,"#{k}:#{v}")
|
219
|
+
end
|
220
|
+
end
|
221
|
+
_wire_write(used_socket,"")
|
222
|
+
used_socket.write body unless body == ''
|
223
|
+
used_socket.write "\0"
|
224
|
+
used_socket.flush if autoflush
|
225
|
+
|
226
|
+
if @protocol >= Stomp::SPL_11
|
227
|
+
@ls = Time.now.to_f if @hbs
|
228
|
+
end
|
183
229
|
|
230
|
+
end
|
184
231
|
end
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
232
|
+
|
233
|
+
# Use CRLF if protocol is >= 1.2, and the client requested CRLF
|
234
|
+
def _wire_write(sock, data)
|
235
|
+
# p [ "debug_01", @protocol, @usecrlf ]
|
236
|
+
if @protocol >= Stomp::SPL_12 && @usecrlf
|
237
|
+
wiredata = "#{data}#{Stomp::CR}#{Stomp::LF}"
|
238
|
+
# p [ "wiredataout_01:", wiredata ]
|
239
|
+
sock.write(wiredata)
|
240
|
+
else
|
241
|
+
# p [ "wiredataout_02:", "#{data}\n" ]
|
242
|
+
sock.puts data
|
243
|
+
end
|
197
244
|
end
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
245
|
+
|
246
|
+
# open_tcp_socket opens a TCP socket.
|
247
|
+
def open_tcp_socket()
|
248
|
+
|
249
|
+
## $stderr.print("h: #{@host}, p: #{@port}\n")
|
250
|
+
|
251
|
+
tcp_socket = nil
|
252
|
+
slog(:on_connecting, log_params)
|
253
|
+
Timeout::timeout(@connect_timeout, Stomp::Error::SocketOpenTimeout) do
|
254
|
+
tcp_socket = TCPSocket.open(@host, @port)
|
255
|
+
end
|
256
|
+
tcp_socket
|
206
257
|
end
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
ctx = @sslctx_newparm ? OpenSSL::SSL::SSLContext.new(@sslctx_newparm) : OpenSSL::SSL::SSLContext.new
|
215
|
-
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # Assume for now
|
216
|
-
#
|
217
|
-
# Note: if a client uses :ssl => true this would result in the gem using
|
218
|
-
# the _default_ Ruby ciphers list. This is _known_ to fail in later
|
219
|
-
# Ruby releases. The gem now detects :ssl => true, and replaces that
|
220
|
-
# with:
|
221
|
-
# * :ssl => Stomp::SSLParams.new
|
222
|
-
#
|
223
|
-
# The above results in the use of Stomp default parameters.
|
224
|
-
#
|
225
|
-
# To specifically request Stomp default parameters, use:
|
226
|
-
# * :ssl => Stomp::SSLParams.new(..., :ciphers => Stomp::DEFAULT_CIPHERS)
|
227
|
-
#
|
228
|
-
# If connecting with an SSLParams instance, and the _default_ Ruby
|
229
|
-
# ciphers list is actually required, use:
|
230
|
-
# * :ssl => Stomp::SSLParams.new(..., :use_ruby_ciphers => true)
|
231
|
-
#
|
232
|
-
# If a custom ciphers list is required, connect with:
|
233
|
-
# * :ssl => Stomp::SSLParams.new(..., :ciphers => custom_ciphers_list)
|
234
|
-
#
|
235
|
-
if @ssl != true
|
258
|
+
|
259
|
+
# open_ssl_socket opens an SSL socket.
|
260
|
+
def open_ssl_socket()
|
261
|
+
require 'openssl' unless defined?(OpenSSL)
|
262
|
+
begin # Any raised SSL exceptions
|
263
|
+
ctx = @sslctx_newparm ? OpenSSL::SSL::SSLContext.new(@sslctx_newparm) : OpenSSL::SSL::SSLContext.new
|
264
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # Assume for now
|
236
265
|
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
266
|
+
# Note: if a client uses :ssl => true this would result in the gem using
|
267
|
+
# the _default_ Ruby ciphers list. This is _known_ to fail in later
|
268
|
+
# Ruby releases. The gem now detects :ssl => true, and replaces that
|
269
|
+
# with:
|
270
|
+
# * :ssl => Stomp::SSLParams.new
|
240
271
|
#
|
272
|
+
# The above results in the use of Stomp default parameters.
|
273
|
+
#
|
274
|
+
# To specifically request Stomp default parameters, use:
|
275
|
+
# * :ssl => Stomp::SSLParams.new(..., :ciphers => Stomp::DEFAULT_CIPHERS)
|
276
|
+
#
|
277
|
+
# If connecting with an SSLParams instance, and the _default_ Ruby
|
278
|
+
# ciphers list is actually required, use:
|
279
|
+
# * :ssl => Stomp::SSLParams.new(..., :use_ruby_ciphers => true)
|
280
|
+
#
|
281
|
+
# If a custom ciphers list is required, connect with:
|
282
|
+
# * :ssl => Stomp::SSLParams.new(..., :ciphers => custom_ciphers_list)
|
283
|
+
#
|
284
|
+
if @ssl != true
|
285
|
+
#
|
286
|
+
# Here @ssl is:
|
287
|
+
# * an instance of Stomp::SSLParams
|
288
|
+
# Control would not be here if @ssl == false or @ssl.nil?.
|
289
|
+
#
|
290
|
+
|
291
|
+
# Back reference the SSLContext
|
292
|
+
@ssl.ctx = ctx
|
293
|
+
|
294
|
+
# Server authentication parameters if required
|
295
|
+
if @ssl.ts_files
|
296
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
297
|
+
truststores = OpenSSL::X509::Store.new
|
298
|
+
fl = @ssl.ts_files.split(",")
|
299
|
+
fl.each do |fn|
|
300
|
+
# Add next cert file listed
|
301
|
+
raise Stomp::Error::SSLNoTruststoreFileError if !File::exists?(fn)
|
302
|
+
raise Stomp::Error::SSLUnreadableTruststoreFileError if !File::readable?(fn)
|
303
|
+
truststores.add_file(fn)
|
304
|
+
end
|
305
|
+
ctx.cert_store = truststores
|
306
|
+
end
|
241
307
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
308
|
+
# Client authentication parameters.
|
309
|
+
# Both cert file and key file must be present or not, it can not be a mix.
|
310
|
+
raise Stomp::Error::SSLClientParamsError if @ssl.cert_file.nil? && !@ssl.key_file.nil?
|
311
|
+
raise Stomp::Error::SSLClientParamsError if !@ssl.cert_file.nil? && @ssl.key_file.nil?
|
312
|
+
if @ssl.cert_file # Any check will do here
|
313
|
+
raise Stomp::Error::SSLNoCertFileError if !File::exists?(@ssl.cert_file)
|
314
|
+
raise Stomp::Error::SSLUnreadableCertFileError if !File::readable?(@ssl.cert_file)
|
315
|
+
ctx.cert = OpenSSL::X509::Certificate.new(File.read(@ssl.cert_file))
|
316
|
+
raise Stomp::Error::SSLNoKeyFileError if !File::exists?(@ssl.key_file)
|
317
|
+
raise Stomp::Error::SSLUnreadableKeyFileError if !File::readable?(@ssl.key_file)
|
318
|
+
ctx.key = OpenSSL::PKey::RSA.new(File.read(@ssl.key_file), @ssl.key_password)
|
319
|
+
end
|
320
|
+
|
321
|
+
# Cipher list
|
322
|
+
# As of this writing, there are numerous problems with supplying
|
323
|
+
# cipher lists to jruby. So we do not attempt to do that here.
|
324
|
+
if !@ssl.use_ruby_ciphers # No Ruby ciphers (the default)
|
325
|
+
if @ssl.ciphers # User ciphers list?
|
326
|
+
ctx.ciphers = @ssl.ciphers # Accept user supplied ciphers
|
327
|
+
else
|
328
|
+
ctx.ciphers = Stomp::DEFAULT_CIPHERS # Just use Stomp defaults
|
329
|
+
end
|
330
|
+
end unless @jruby
|
331
|
+
|
332
|
+
# Set SSLContext Options if user asks for it in Stomp::SSLParams
|
333
|
+
# and SSL supports it.
|
334
|
+
if @ssl.ssl_ctxopts && ctx.respond_to?(:options=)
|
335
|
+
ctx.options = @ssl.ssl_ctxopts
|
255
336
|
end
|
256
|
-
ctx.cert_store = truststores
|
257
|
-
end
|
258
337
|
|
259
|
-
# Client authentication parameters.
|
260
|
-
# Both cert file and key file must be present or not, it can not be a mix.
|
261
|
-
raise Stomp::Error::SSLClientParamsError if @ssl.cert_file.nil? && !@ssl.key_file.nil?
|
262
|
-
raise Stomp::Error::SSLClientParamsError if !@ssl.cert_file.nil? && @ssl.key_file.nil?
|
263
|
-
if @ssl.cert_file # Any check will do here
|
264
|
-
raise Stomp::Error::SSLNoCertFileError if !File::exists?(@ssl.cert_file)
|
265
|
-
raise Stomp::Error::SSLUnreadableCertFileError if !File::readable?(@ssl.cert_file)
|
266
|
-
ctx.cert = OpenSSL::X509::Certificate.new(File.read(@ssl.cert_file))
|
267
|
-
raise Stomp::Error::SSLNoKeyFileError if !File::exists?(@ssl.key_file)
|
268
|
-
raise Stomp::Error::SSLUnreadableKeyFileError if !File::readable?(@ssl.key_file)
|
269
|
-
ctx.key = OpenSSL::PKey::RSA.new(File.read(@ssl.key_file), @ssl.key_password)
|
270
338
|
end
|
271
339
|
|
272
|
-
#
|
273
|
-
|
274
|
-
|
275
|
-
|
340
|
+
#
|
341
|
+
ssl = nil
|
342
|
+
slog(:on_ssl_connecting, log_params)
|
343
|
+
# _dump_ctx(ctx)
|
344
|
+
Timeout::timeout(@connect_timeout, Stomp::Error::SocketOpenTimeout) do
|
345
|
+
tcp_socket = TCPSocket.open(@host, @port)
|
346
|
+
ssl = OpenSSL::SSL::SSLSocket.new(tcp_socket, ctx)
|
347
|
+
ssl.hostname = @host if ssl.respond_to? :hostname=
|
348
|
+
ssl.sync_close = true # Sync ssl close with underlying TCP socket
|
349
|
+
ssl.connect
|
350
|
+
end
|
351
|
+
def ssl.ready?
|
352
|
+
! @rbuffer.empty? || @io.ready?
|
353
|
+
end
|
354
|
+
if @ssl != true
|
355
|
+
# Pass back results if possible
|
356
|
+
if RUBY_VERSION =~ /1\.8\.[56]/
|
357
|
+
@ssl.verify_result = "N/A for Ruby #{RUBY_VERSION}"
|
276
358
|
else
|
277
|
-
|
359
|
+
@ssl.verify_result = ssl.verify_result
|
278
360
|
end
|
361
|
+
@ssl.peer_cert = ssl.peer_cert
|
279
362
|
end
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
363
|
+
slog(:on_ssl_connected, log_params)
|
364
|
+
ssl
|
365
|
+
rescue Exception => ex
|
366
|
+
lp = log_params.clone
|
367
|
+
lp[:ssl_exception] = ex
|
368
|
+
slog(:on_ssl_connectfail, lp)
|
369
|
+
if ssl
|
370
|
+
# shut down the TCP socket - we just failed to do the SSL handshake in time
|
371
|
+
ssl.close
|
285
372
|
end
|
286
|
-
|
373
|
+
#
|
374
|
+
puts ex.backtrace
|
375
|
+
$sdtout.flush
|
376
|
+
raise # Reraise
|
287
377
|
end
|
378
|
+
end
|
288
379
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
ssl.sync_close = true # Sync ssl close with underlying TCP socket
|
298
|
-
ssl.connect
|
299
|
-
end
|
300
|
-
def ssl.ready?
|
301
|
-
! @rbuffer.empty? || @io.ready?
|
302
|
-
end
|
303
|
-
if @ssl != true
|
304
|
-
# Pass back results if possible
|
305
|
-
if RUBY_VERSION =~ /1\.8\.[56]/
|
306
|
-
@ssl.verify_result = "N/A for Ruby #{RUBY_VERSION}"
|
307
|
-
else
|
308
|
-
@ssl.verify_result = ssl.verify_result
|
380
|
+
# close_socket closes the current open socket, and hence the connection.
|
381
|
+
def close_socket()
|
382
|
+
begin
|
383
|
+
# Need to set @closed = true before closing the socket
|
384
|
+
# within the @read_semaphore thread
|
385
|
+
@closed = true
|
386
|
+
@read_semaphore.synchronize do
|
387
|
+
@socket.close
|
309
388
|
end
|
310
|
-
|
311
|
-
|
312
|
-
slog(:on_ssl_connected, log_params)
|
313
|
-
ssl
|
314
|
-
rescue Exception => ex
|
315
|
-
lp = log_params.clone
|
316
|
-
lp[:ssl_exception] = ex
|
317
|
-
slog(:on_ssl_connectfail, lp)
|
318
|
-
if ssl
|
319
|
-
# shut down the TCP socket - we just failed to do the SSL handshake in time
|
320
|
-
ssl.close
|
389
|
+
rescue
|
390
|
+
#Ignoring if already closed
|
321
391
|
end
|
322
|
-
|
323
|
-
raise # Reraise
|
392
|
+
@closed
|
324
393
|
end
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
@closed =
|
333
|
-
@
|
334
|
-
@
|
394
|
+
|
395
|
+
# open_socket opens a TCP or SSL soclet as required.
|
396
|
+
def open_socket()
|
397
|
+
used_socket = @ssl ? open_ssl_socket : open_tcp_socket
|
398
|
+
# try to close the old connection if any
|
399
|
+
close_socket
|
400
|
+
|
401
|
+
@closed = false
|
402
|
+
if @parameters # nil in some rspec tests
|
403
|
+
unless @reconnect_delay
|
404
|
+
@reconnect_delay = @parameters[:initial_reconnect_delay] || iosto1
|
405
|
+
end
|
335
406
|
end
|
336
|
-
|
337
|
-
|
407
|
+
# Use keepalive
|
408
|
+
used_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
409
|
+
|
410
|
+
# TCP_NODELAY option (disables Nagle's algorithm)
|
411
|
+
used_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, !!(@parameters && @parameters[:tcp_nodelay]))
|
412
|
+
|
413
|
+
@iosto = @parse_timeout ? @parse_timeout.to_f : 0.0
|
414
|
+
|
415
|
+
used_socket
|
338
416
|
end
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
@reconnect_delay = @parameters[:initial_reconnect_delay] || 0.01
|
417
|
+
|
418
|
+
# connect performs a basic STOMP CONNECT operation.
|
419
|
+
def connect(used_socket)
|
420
|
+
@connect_headers = {} unless @connect_headers # Caller said nil/false
|
421
|
+
headers = @connect_headers.clone
|
422
|
+
headers[:login] = @login unless @login.to_s.empty?
|
423
|
+
headers[:passcode] = @passcode unless @login.to_s.empty?
|
424
|
+
_pre_connect
|
425
|
+
if !@hhas10 && @stompconn
|
426
|
+
_transmit(used_socket, Stomp::CMD_STOMP, headers)
|
427
|
+
else
|
428
|
+
_transmit(used_socket, Stomp::CMD_CONNECT, headers)
|
352
429
|
end
|
430
|
+
@connection_frame = _receive(used_socket, true)
|
431
|
+
_post_connect
|
432
|
+
@disconnect_receipt = nil
|
433
|
+
@session = @connection_frame.headers["session"] if @connection_frame
|
434
|
+
# replay any subscriptions.
|
435
|
+
@subscriptions.each {|k,v|
|
436
|
+
_transmit(used_socket, Stomp::CMD_SUBSCRIBE, v)
|
437
|
+
}
|
353
438
|
end
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
@subscriptions.each {|k,v|
|
381
|
-
_transmit(used_socket, Stomp::CMD_SUBSCRIBE, v)
|
382
|
-
}
|
383
|
-
end
|
384
|
-
|
385
|
-
def _init_line_read(read_socket)
|
386
|
-
line = ''
|
387
|
-
if @protocol == Stomp::SPL_10 || (@protocol >= Stomp::SPL_11 && !@hbr)
|
388
|
-
if @jruby
|
389
|
-
# Handle JRuby specific behavior.
|
439
|
+
|
440
|
+
def _init_line_read(read_socket)
|
441
|
+
line = ''
|
442
|
+
if @protocol == Stomp::SPL_10 || (@protocol >= Stomp::SPL_11 && !@hbr)
|
443
|
+
if @jruby
|
444
|
+
# Handle JRuby specific behavior.
|
445
|
+
### p [ "ilrjr00", _is_ready?(read_socket), RUBY_VERSION ]
|
446
|
+
if RUBY_VERSION < "2"
|
447
|
+
while true
|
448
|
+
### p [ "ilrjr01A1", _is_ready?(read_socket) ]
|
449
|
+
line = read_socket.gets # Data from wire
|
450
|
+
break unless line == "\n"
|
451
|
+
line = ''
|
452
|
+
end
|
453
|
+
else # RUBY_VERSION >= "2"
|
454
|
+
while _is_ready?(read_socket)
|
455
|
+
### p [ "ilrjr01B2", _is_ready?(read_socket) ]
|
456
|
+
line = read_socket.gets # Data from wire
|
457
|
+
break unless line == "\n"
|
458
|
+
line = ''
|
459
|
+
end
|
460
|
+
end
|
461
|
+
else
|
462
|
+
line = read_socket.gets # The old way
|
463
|
+
end
|
464
|
+
else # We are >= 1.1 *AND* receiving heartbeats.
|
390
465
|
while true
|
391
466
|
line = read_socket.gets # Data from wire
|
392
467
|
break unless line == "\n"
|
393
468
|
line = ''
|
469
|
+
@lr = Time.now.to_f
|
394
470
|
end
|
395
|
-
else
|
396
|
-
line = read_socket.gets # The old way
|
397
|
-
end
|
398
|
-
else # We are >= 1.1 *AND* receiving heartbeats.
|
399
|
-
while true
|
400
|
-
line = read_socket.gets # Data from wire
|
401
|
-
break unless line == "\n"
|
402
|
-
line = ''
|
403
|
-
@lr = Time.now.to_f
|
404
471
|
end
|
472
|
+
line
|
473
|
+
end
|
474
|
+
|
475
|
+
# Used for debugging
|
476
|
+
def _dump_ctx(ctx)
|
477
|
+
p [ "dc01", ctx.inspect ]
|
478
|
+
p [ "dc02ciphers", ctx.ciphers ]
|
479
|
+
end
|
480
|
+
|
481
|
+
# used for debugging
|
482
|
+
def _dump_callstack()
|
483
|
+
i = 0
|
484
|
+
caller.each do |c|
|
485
|
+
p [ "csn", i, c ]
|
486
|
+
i += 1
|
405
487
|
end
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
# Used for debugging
|
410
|
-
def _dump_ctx(ctx)
|
411
|
-
p [ "dc01", ctx.inspect ]
|
412
|
-
p [ "dc02ciphers", ctx.ciphers ]
|
413
|
-
end
|
488
|
+
end # _dump_callstack
|
489
|
+
|
414
490
|
end # class Connection
|
415
491
|
|
416
492
|
end # module Stomp
|
417
|
-
|