stomper 2.0.1 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +28 -0
- data/README.md +1 -17
- data/features/protocol_version_negotiation.feature +6 -6
- data/features/steps/{acking_messages_steps.rb → ack_and_nack_steps.rb} +9 -1
- data/features/steps/broker_steps.rb +52 -0
- data/features/steps/client_steps.rb +137 -0
- data/features/steps/{receipts_steps.rb → receipt_steps.rb} +0 -0
- data/features/steps/{scopes_steps.rb → scope_steps.rb} +0 -16
- data/features/steps/subscription_steps.rb +80 -0
- data/lib/stomper/connection.rb +29 -29
- data/lib/stomper/receipt_manager.rb +5 -0
- data/lib/stomper/subscription_manager.rb +8 -3
- data/lib/stomper/version.rb +1 -1
- data/spec/stomper/connection_spec.rb +16 -1
- data/spec/stomper/frame_serializer_spec.rb +299 -280
- data/spec/stomper/subscription_manager_spec.rb +2 -3
- data/spec/stomper_spec.rb +0 -1
- data/spec/support/frame_header_matchers.rb +25 -0
- metadata +17 -60
- data/features/steps/disconnecting_steps.rb +0 -8
- data/features/steps/establish_connection_steps.rb +0 -77
- data/features/steps/frame_transmission_steps.rb +0 -40
- data/features/steps/protocol_version_negotiation_steps.rb +0 -15
- data/features/steps/secure_connections_steps.rb +0 -43
- data/features/steps/send_and_message_steps.rb +0 -35
- data/features/steps/subscribing_steps.rb +0 -36
- data/features/steps/threaded_receiver_steps.rb +0 -8
- data/features/steps/transactions_steps.rb +0 -0
- data/spec/stomper/frame_serializer_1.8_spec.rb +0 -318
@@ -1,318 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
module Stomper
|
5
|
-
if RUBY_VERSION < '1.9'
|
6
|
-
describe FrameSerializer do
|
7
|
-
before(:each) do
|
8
|
-
@messages = {
|
9
|
-
:content_type_and_charset => "MESSAGE\ncontent-type:text/plain; charset=ISO-8859-1\ncontent-length:6\na-header: padded \n\nh\xEBllo!\000",
|
10
|
-
:escaped_headers => "MESSAGE\ncontent-type:text/plain;charset=UTF-8\ncontent-length:7\na\\nspecial\\chead\\\\cer: padded\\c and using\\nspecial\\\\\\\\\\\\ncharacters \n\nh\xC3\xABllo!\000",
|
11
|
-
:no_content_length => "MESSAGE\ncontent-type:text/plain\n\nh\xC3\xABllo!\000",
|
12
|
-
:repeated_headers => "MESSAGE\ncontent-type:text/plain\nrepeated header:a value\nrepeated header:alternate value\n\nh\xC3\xABllo!\000",
|
13
|
-
:non_text_content_type => "MESSAGE\ncontent-type:not-text/other\n\nh\xC3\xABllo!\000",
|
14
|
-
:no_content_type => "MESSAGE\n\nh\xC3\xABllo!\000",
|
15
|
-
:invalid_content_length => "MESSAGE\ncontent-length:4\n\n12345\000",
|
16
|
-
:invalid_header_character => "MESSAGE\ngrandpa:he was:anti\n\n12345\000",
|
17
|
-
:invalid_header_sequence => "MESSAGE\ngrandpa:he was\\ranti\n\n12345\000",
|
18
|
-
:malformed_header => "MESSAGE\nearth_below_us\nfloating:weightless\n\n12345\000",
|
19
|
-
:dangling_header_sequence => "MESSAGE\ngrandpa:he was anti\\\n\n12345\000",
|
20
|
-
}
|
21
|
-
|
22
|
-
@frames = {
|
23
|
-
:common => ::Stomper::Frame.new('FRAME', {}, 'body of message'),
|
24
|
-
:no_headers => ::Stomper::Frame.new('FRAME', {}, 'body of message'),
|
25
|
-
:no_body => ::Stomper::Frame.new('FRAME'),
|
26
|
-
:no_command => ::Stomper::Frame.new,
|
27
|
-
:header_name_with_linefeed => ::Stomper::Frame.new('FRAME', { "a\ntest\nheader" => "va\\lue : is\n\nme"}),
|
28
|
-
:header_name_with_colon => ::Stomper::Frame.new('FRAME', { "a:test:header" => "va\\lue : is\n\nme"}),
|
29
|
-
:header_name_with_backslash => ::Stomper::Frame.new('FRAME', { "a\\test\\header" => "va\\lue : is\n\nme"}),
|
30
|
-
:binary_body_no_content_type => ::Stomper::Frame.new('FRAME', {}, 'body of message'),
|
31
|
-
:charset_header_text_body => ::Stomper::Frame.new('FRAME', {:'content-type' => 'text/plain; param="value";charset=ISO-8859-1'}, 'body of message'),
|
32
|
-
:charset_header_binary_body => ::Stomper::Frame.new('FRAME', {:'content-type' => 'application/pdf; param="value";charset=ISO-8859-1'}, 'body of message')
|
33
|
-
}
|
34
|
-
#{ :header_1 => 'value 1', :header_2 => '3', :header_3 => '', :'content-type' => 'text/plain'}
|
35
|
-
@frames[:common][:header_1] = 'value 1'
|
36
|
-
@frames[:common][:header_2] = '3'
|
37
|
-
@frames[:common][:header_3] = ''
|
38
|
-
@frames[:common][:'content-type'] = 'text/plain'
|
39
|
-
# :header_1 => 'val', :musical => '', :offering => '4'
|
40
|
-
@frames[:no_body][:header_1] = 'val'
|
41
|
-
@frames[:no_body][:musical] = ''
|
42
|
-
@frames[:no_body][:offering] = '4'
|
43
|
-
@frame_io = StringIO.new
|
44
|
-
@frame_serializer = FrameSerializer.new(@frame_io)
|
45
|
-
end
|
46
|
-
|
47
|
-
describe "thread safety" do
|
48
|
-
before(:each) do
|
49
|
-
@frame_serializer = FrameSerializer.new(mock('frame io'))
|
50
|
-
end
|
51
|
-
it "should synchronize writing to the underlying IO" do
|
52
|
-
first_called = false
|
53
|
-
call_next = false
|
54
|
-
ordered = []
|
55
|
-
@frame_serializer.stub!(:__write_frame__).and_return do |f|
|
56
|
-
first_called = true
|
57
|
-
ordered << 1
|
58
|
-
Thread.stop
|
59
|
-
ordered << 2
|
60
|
-
f
|
61
|
-
end
|
62
|
-
|
63
|
-
thread_1 = Thread.new do
|
64
|
-
@frame_serializer.write_frame(mock('frame'))
|
65
|
-
end
|
66
|
-
thread_2 = Thread.new do
|
67
|
-
Thread.pass until call_next
|
68
|
-
Thread.pass
|
69
|
-
thread_1.run
|
70
|
-
end
|
71
|
-
Thread.pass until first_called
|
72
|
-
call_next = true
|
73
|
-
|
74
|
-
@frame_serializer.stub!(:__write_frame__).and_return do |f|
|
75
|
-
ordered << 3
|
76
|
-
f
|
77
|
-
end
|
78
|
-
@frame_serializer.write_frame(mock('frame'))
|
79
|
-
thread_1.join
|
80
|
-
thread_2.join
|
81
|
-
ordered.should == [1, 2, 3]
|
82
|
-
end
|
83
|
-
|
84
|
-
it "should synchronize reading from the underlying IO" do
|
85
|
-
first_called = false
|
86
|
-
call_next = false
|
87
|
-
ordered = []
|
88
|
-
@frame_serializer.stub!(:__read_frame__).and_return do
|
89
|
-
first_called = true
|
90
|
-
ordered << 1
|
91
|
-
Thread.stop
|
92
|
-
ordered << 2
|
93
|
-
mock('frame 1')
|
94
|
-
end
|
95
|
-
|
96
|
-
thread_1 = Thread.new do
|
97
|
-
@frame_serializer.read_frame
|
98
|
-
end
|
99
|
-
thread_2 = Thread.new do
|
100
|
-
Thread.pass until call_next
|
101
|
-
Thread.pass
|
102
|
-
thread_1.run
|
103
|
-
end
|
104
|
-
Thread.pass until first_called
|
105
|
-
call_next = true
|
106
|
-
|
107
|
-
@frame_serializer.stub!(:__read_frame__).and_return do
|
108
|
-
ordered << 3
|
109
|
-
mock('frame 2')
|
110
|
-
end
|
111
|
-
@frame_serializer.read_frame
|
112
|
-
thread_1.join
|
113
|
-
thread_2.join
|
114
|
-
ordered.should == [1, 2, 3]
|
115
|
-
end
|
116
|
-
|
117
|
-
it "should not make reading and writing mutually exclusive" do
|
118
|
-
first_called = false
|
119
|
-
call_next = false
|
120
|
-
ordered = []
|
121
|
-
@frame_serializer.stub!(:__write_frame__).and_return do |f|
|
122
|
-
first_called = true
|
123
|
-
ordered << 1
|
124
|
-
Thread.stop
|
125
|
-
ordered << 2
|
126
|
-
f
|
127
|
-
end
|
128
|
-
@frame_serializer.stub!(:__read_frame__).and_return do
|
129
|
-
ordered << 3
|
130
|
-
mock('frame 2')
|
131
|
-
end
|
132
|
-
|
133
|
-
thread_1 = Thread.new do
|
134
|
-
@frame_serializer.write_frame(mock('frame'))
|
135
|
-
end
|
136
|
-
thread_2 = Thread.new do
|
137
|
-
Thread.pass until call_next
|
138
|
-
Thread.pass
|
139
|
-
thread_1.run
|
140
|
-
end
|
141
|
-
Thread.pass until first_called
|
142
|
-
call_next = true
|
143
|
-
@frame_serializer.read_frame
|
144
|
-
thread_1.join
|
145
|
-
thread_2.join
|
146
|
-
ordered.should == [1, 3, 2]
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
describe "Protocol 1.0" do
|
151
|
-
it "should not have extended the V1_1 mixin" do
|
152
|
-
::Stomper::FrameSerializer::EXTEND_BY_VERSION['1.1'].each do |mod|
|
153
|
-
@frame_serializer.should_not be_a_kind_of(mod)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
describe "writing frames" do
|
158
|
-
it "should properly serialize a common frame" do
|
159
|
-
@frame_serializer.write_frame(@frames[:common])
|
160
|
-
@frame_io.string.should == "FRAME\nheader_1:value 1\nheader_2:3\nheader_3:\ncontent-type:text/plain;charset=UTF-8\ncontent-length:15\n\nbody of message\000"
|
161
|
-
end
|
162
|
-
|
163
|
-
it "should properly serialize a frame without headers - content type cannot be inferred in Ruby 1.8.7" do
|
164
|
-
@frame_serializer.write_frame(@frames[:no_headers])
|
165
|
-
@frame_io.string.should == "FRAME\ncontent-length:15\n\nbody of message\000"
|
166
|
-
end
|
167
|
-
|
168
|
-
it "should properly serialize a frame without a body" do
|
169
|
-
@frame_serializer.write_frame(@frames[:no_body])
|
170
|
-
@frame_io.string.should == "FRAME\nheader_1:val\nmusical:\noffering:4\n\n\000"
|
171
|
-
end
|
172
|
-
|
173
|
-
it "should properly serialize a frame without a command as a new line" do
|
174
|
-
@frame_serializer.write_frame(@frames[:no_command])
|
175
|
-
@frame_io.string.should == "\n"
|
176
|
-
end
|
177
|
-
|
178
|
-
it "should properly drop LF from header names and values" do
|
179
|
-
@frame_serializer.write_frame(@frames[:header_name_with_linefeed])
|
180
|
-
@frame_io.string.should == "FRAME\natestheader:va\\lue : isme\n\n\000"
|
181
|
-
end
|
182
|
-
|
183
|
-
it "should not escape backslash characters in header names or values" do
|
184
|
-
@frame_serializer.write_frame(@frames[:header_name_with_backslash])
|
185
|
-
@frame_io.string.should == "FRAME\na\\test\\header:va\\lue : isme\n\n\000"
|
186
|
-
end
|
187
|
-
|
188
|
-
it "should drop colons in header names, but leave them alone in values" do
|
189
|
-
@frame_serializer.write_frame(@frames[:header_name_with_colon])
|
190
|
-
@frame_io.string.should == "FRAME\natestheader:va\\lue : isme\n\n\000"
|
191
|
-
end
|
192
|
-
|
193
|
-
it "should not generate a content-type header if the encoding is binary" do
|
194
|
-
@frame_serializer.write_frame(@frames[:binary_body_no_content_type])
|
195
|
-
@frame_io.string.should == "FRAME\ncontent-length:15\n\nbody of message\000"
|
196
|
-
end
|
197
|
-
|
198
|
-
it "should preserve the charset parameter of text bodies, because we have nothing else to work with in Ruby 1.8.7" do
|
199
|
-
@frame_serializer.write_frame(@frames[:charset_header_text_body])
|
200
|
-
@frame_io.string.should == "FRAME\ncontent-type:text/plain; param=\"value\";charset=ISO-8859-1\ncontent-length:15\n\nbody of message\000"
|
201
|
-
end
|
202
|
-
|
203
|
-
it "should preserve the charset parameter of binary bodies, because we have nothing else to work with in Ruby 1.8.7" do
|
204
|
-
@frame_serializer.write_frame(@frames[:charset_header_binary_body])
|
205
|
-
@frame_io.string.should == "FRAME\ncontent-type:application/pdf; param=\"value\";charset=ISO-8859-1\ncontent-length:15\n\nbody of message\000"
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
describe "Protocol 1.1" do
|
211
|
-
before(:each) do
|
212
|
-
@frame_serializer.extend_for_protocol '1.1'
|
213
|
-
end
|
214
|
-
|
215
|
-
it "should have extended the V1_1 mixin" do
|
216
|
-
::Stomper::FrameSerializer::EXTEND_BY_VERSION['1.1'].each do |mod|
|
217
|
-
@frame_serializer.should be_a_kind_of(mod)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
describe "writing frames" do
|
222
|
-
it "should properly serialize a common frame" do
|
223
|
-
@frame_serializer.write_frame(@frames[:common])
|
224
|
-
@frame_io.string.should == "FRAME\nheader_1:value 1\nheader_2:3\nheader_3:\ncontent-type:text/plain;charset=UTF-8\ncontent-length:15\n\nbody of message\000"
|
225
|
-
end
|
226
|
-
|
227
|
-
it "should properly serialize a frame without headers - content type cannot be inferred in Ruby 1.8.7" do
|
228
|
-
@frame_serializer.write_frame(@frames[:no_headers])
|
229
|
-
@frame_io.string.should == "FRAME\ncontent-length:15\n\nbody of message\000"
|
230
|
-
end
|
231
|
-
|
232
|
-
it "should properly serialize a frame without a body" do
|
233
|
-
@frame_serializer.write_frame(@frames[:no_body])
|
234
|
-
@frame_io.string.should == "FRAME\nheader_1:val\nmusical:\noffering:4\n\n\000"
|
235
|
-
end
|
236
|
-
|
237
|
-
it "should properly serialize a frame without a command as a new line" do
|
238
|
-
@frame_serializer.write_frame(@frames[:no_command])
|
239
|
-
@frame_io.string.should == "\n"
|
240
|
-
end
|
241
|
-
|
242
|
-
it "should escape LF in header names and values" do
|
243
|
-
@frame_serializer.write_frame(@frames[:header_name_with_linefeed])
|
244
|
-
@frame_io.string.should == "FRAME\na\\ntest\\nheader:va\\\\lue \\c is\\n\\nme\n\n\000"
|
245
|
-
end
|
246
|
-
|
247
|
-
it "should escape backslashes in header names and values" do
|
248
|
-
@frame_serializer.write_frame(@frames[:header_name_with_backslash])
|
249
|
-
@frame_io.string.should == "FRAME\na\\\\test\\\\header:va\\\\lue \\c is\\n\\nme\n\n\000"
|
250
|
-
end
|
251
|
-
|
252
|
-
it "should escape colons in header names and values" do
|
253
|
-
@frame_serializer.write_frame(@frames[:header_name_with_colon])
|
254
|
-
@frame_io.string.should == "FRAME\na\\ctest\\cheader:va\\\\lue \\c is\\n\\nme\n\n\000"
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
describe "reading frames" do
|
259
|
-
it "should properly de-serialize a simple frame" do
|
260
|
-
@frame_io.string = @messages[:content_type_and_charset]
|
261
|
-
frame = @frame_serializer.read_frame
|
262
|
-
frame.command.should == "MESSAGE"
|
263
|
-
frame.headers.sort { |a, b| a.first <=> b.first }.should == [
|
264
|
-
['a-header', ' padded '], ['content-length', '6'],
|
265
|
-
['content-type', 'text/plain; charset=ISO-8859-1']
|
266
|
-
]
|
267
|
-
frame.body.should == "h\xEBllo!"
|
268
|
-
end
|
269
|
-
it "should properly read a frame with special characters in its header" do
|
270
|
-
@frame_io.string = @messages[:escaped_headers]
|
271
|
-
frame = @frame_serializer.read_frame
|
272
|
-
frame["a\nspecial:head\\cer"].should == " padded: and using\nspecial\\\\\\ncharacters "
|
273
|
-
end
|
274
|
-
it "should properly read a frame with a body and no content-length" do
|
275
|
-
@frame_io.string = @messages[:no_content_length]
|
276
|
-
frame = @frame_serializer.read_frame
|
277
|
-
frame.body.should == "hëllo!"
|
278
|
-
end
|
279
|
-
it "should assume a binary charset if none is set and the content-type does not match text/*" do
|
280
|
-
@frame_io.string = @messages[:non_text_content_type]
|
281
|
-
frame = @frame_serializer.read_frame
|
282
|
-
end
|
283
|
-
it "should assume a binary charset if the content-type header is not specified" do
|
284
|
-
@frame_io.string = @messages[:no_content_type]
|
285
|
-
frame = @frame_serializer.read_frame
|
286
|
-
end
|
287
|
-
it "should set the value of a header to the first occurrence" do
|
288
|
-
@frame_io.string = @messages[:repeated_headers]
|
289
|
-
frame = @frame_serializer.read_frame
|
290
|
-
end
|
291
|
-
it "should raise a malformed frame error if the frame is not properly terminated" do
|
292
|
-
@frame_io.string = @messages[:invalid_content_length]
|
293
|
-
lambda { @frame_serializer.read_frame }.should raise_error(::Stomper::Errors::MalformedFrameError)
|
294
|
-
end
|
295
|
-
# While the spec suggests that all ":" chars be replaced with "\c", ActiveMQ 5.3.2 sends
|
296
|
-
# a "session" header with a value that contains ":" chars. So, we are NOT going to
|
297
|
-
# freak out if we receive more than one ":" on a header line.
|
298
|
-
it "should not raise an error if the frame contains a header value with a raw ':'" do
|
299
|
-
@frame_io.string = @messages[:invalid_header_character]
|
300
|
-
lambda { @frame_serializer.read_frame }.should_not raise_error
|
301
|
-
end
|
302
|
-
it "should raise an invalid header esacape sequence error if the frame contains a header with an invalid escape sequence" do
|
303
|
-
@frame_io.string = @messages[:invalid_header_sequence]
|
304
|
-
lambda { @frame_serializer.read_frame }.should raise_error(::Stomper::Errors::InvalidHeaderEscapeSequenceError)
|
305
|
-
end
|
306
|
-
it "should raise an malfored header error if the frame contains an incomplete header" do
|
307
|
-
@frame_io.string = @messages[:malformed_header]
|
308
|
-
lambda { @frame_serializer.read_frame }.should raise_error(::Stomper::Errors::MalformedHeaderError)
|
309
|
-
end
|
310
|
-
it "should raise an invalid header esacape sequence error if the frame contains a header with a dangling escape sequence" do
|
311
|
-
@frame_io.string = @messages[:dangling_header_sequence]
|
312
|
-
lambda { @frame_serializer.read_frame }.should raise_error(::Stomper::Errors::InvalidHeaderEscapeSequenceError)
|
313
|
-
end
|
314
|
-
end
|
315
|
-
end
|
316
|
-
end
|
317
|
-
end
|
318
|
-
end
|