stomper 2.0.1 → 2.0.2
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.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
|