stomper 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/{spec/spec.opts → .rspec} +0 -2
- data/Gemfile +4 -0
- data/LICENSE +201 -201
- data/README.md +130 -0
- data/Rakefile +5 -0
- data/examples/basic.rb +38 -0
- data/examples/events.rb +54 -0
- data/features/acking_messages.feature +147 -0
- data/features/disconnecting.feature +12 -0
- data/features/establish_connection.feature +44 -0
- data/features/protocol_version_negotiation.feature +61 -0
- data/features/receipts.feature +72 -0
- data/features/scopes.feature +32 -0
- data/features/secure_connections.feature +38 -0
- data/features/send_and_message.feature +28 -0
- data/features/steps/acking_messages_steps.rb +39 -0
- data/features/steps/disconnecting_steps.rb +8 -0
- data/features/steps/establish_connection_steps.rb +74 -0
- data/features/steps/frame_transmission_steps.rb +35 -0
- data/features/steps/protocol_version_negotiation_steps.rb +15 -0
- data/features/steps/receipts_steps.rb +79 -0
- data/features/steps/scopes_steps.rb +52 -0
- data/features/steps/secure_connections_steps.rb +41 -0
- data/features/steps/send_and_message_steps.rb +35 -0
- data/features/steps/subscribing_steps.rb +36 -0
- data/features/steps/threaded_receiver_steps.rb +8 -0
- data/features/steps/transactions_steps.rb +0 -0
- data/features/subscribing.feature +151 -0
- data/features/support/env.rb +11 -0
- data/features/support/header_helpers.rb +12 -0
- data/features/support/ssl/README +6 -0
- data/features/support/ssl/broker_cert.csr +17 -0
- data/features/support/ssl/broker_cert.pem +72 -0
- data/features/support/ssl/broker_key.pem +27 -0
- data/features/support/ssl/client_cert.csr +17 -0
- data/features/support/ssl/client_cert.pem +72 -0
- data/features/support/ssl/client_key.pem +27 -0
- data/features/support/ssl/demoCA/cacert.pem +17 -0
- data/features/support/ssl/demoCA/index.txt +2 -0
- data/features/support/ssl/demoCA/index.txt.attr +1 -0
- data/features/support/ssl/demoCA/index.txt.attr.old +1 -0
- data/features/support/ssl/demoCA/index.txt.old +1 -0
- data/features/support/ssl/demoCA/newcerts/01.pem +72 -0
- data/features/support/ssl/demoCA/newcerts/02.pem +72 -0
- data/features/support/ssl/demoCA/private/cakey.pem +17 -0
- data/features/support/ssl/demoCA/serial +1 -0
- data/features/support/ssl/demoCA/serial.old +1 -0
- data/features/support/test_stomp_server.rb +150 -0
- data/features/threaded_receiver.feature +11 -0
- data/features/transactions.feature +66 -0
- data/lib/stomper.rb +30 -20
- data/lib/stomper/connection.rb +442 -102
- data/lib/stomper/errors.rb +59 -0
- data/lib/stomper/extensions.rb +10 -0
- data/lib/stomper/extensions/common.rb +258 -0
- data/lib/stomper/extensions/events.rb +213 -0
- data/lib/stomper/extensions/heartbeat.rb +101 -0
- data/lib/stomper/extensions/scoping.rb +56 -0
- data/lib/stomper/frame.rb +54 -0
- data/lib/stomper/frame_serializer.rb +217 -0
- data/lib/stomper/headers.rb +15 -0
- data/lib/stomper/receipt_manager.rb +36 -0
- data/lib/stomper/receivers.rb +7 -0
- data/lib/stomper/receivers/threaded.rb +71 -0
- data/lib/stomper/scopes.rb +9 -0
- data/lib/stomper/scopes/header_scope.rb +49 -0
- data/lib/stomper/scopes/receipt_scope.rb +44 -0
- data/lib/stomper/scopes/transaction_scope.rb +109 -0
- data/lib/stomper/sockets.rb +66 -28
- data/lib/stomper/subscription_manager.rb +79 -0
- data/lib/stomper/support.rb +68 -0
- data/lib/stomper/support/1.8/frame_serializer.rb +53 -0
- data/lib/stomper/support/1.8/headers.rb +183 -0
- data/lib/stomper/support/1.9/frame_serializer.rb +64 -0
- data/lib/stomper/support/1.9/headers.rb +172 -0
- data/lib/stomper/support/ruby.rb +13 -0
- data/lib/stomper/uris.rb +49 -0
- data/lib/stomper/version.rb +7 -0
- data/spec/spec_helper.rb +13 -9
- data/spec/stomper/connection_spec.rb +712 -0
- data/spec/stomper/extensions/common_spec.rb +187 -0
- data/spec/stomper/extensions/events_spec.rb +78 -0
- data/spec/stomper/extensions/heartbeat_spec.rb +103 -0
- data/spec/stomper/extensions/scoping_spec.rb +21 -0
- data/spec/stomper/frame_serializer_1.8_spec.rb +318 -0
- data/spec/stomper/frame_serializer_spec.rb +316 -0
- data/spec/stomper/frame_spec.rb +36 -0
- data/spec/stomper/headers_spec.rb +224 -0
- data/spec/stomper/receipt_manager_spec.rb +91 -0
- data/spec/stomper/receivers/threaded_spec.rb +116 -0
- data/spec/stomper/scopes/header_scope_spec.rb +42 -0
- data/spec/stomper/scopes/receipt_scope_spec.rb +51 -0
- data/spec/stomper/scopes/transaction_scope_spec.rb +183 -0
- data/spec/stomper/sockets_spec.rb +113 -0
- data/spec/stomper/subscription_manager_spec.rb +107 -0
- data/spec/stomper/support_spec.rb +69 -0
- data/spec/stomper/uris_spec.rb +54 -0
- data/spec/stomper_spec.rb +9 -0
- data/spec/support/custom_argument_matchers.rb +57 -0
- data/spec/support/existential_frame_matchers.rb +19 -0
- data/spec/support/frame_header_matchers.rb +10 -0
- data/stomper.gemspec +30 -0
- metadata +272 -97
- data/AUTHORS +0 -21
- data/CHANGELOG +0 -20
- data/README.rdoc +0 -120
- data/lib/stomper/client.rb +0 -34
- data/lib/stomper/frame_reader.rb +0 -73
- data/lib/stomper/frame_writer.rb +0 -21
- data/lib/stomper/frames.rb +0 -39
- data/lib/stomper/frames/abort.rb +0 -10
- data/lib/stomper/frames/ack.rb +0 -25
- data/lib/stomper/frames/begin.rb +0 -11
- data/lib/stomper/frames/client_frame.rb +0 -89
- data/lib/stomper/frames/commit.rb +0 -10
- data/lib/stomper/frames/connect.rb +0 -10
- data/lib/stomper/frames/connected.rb +0 -30
- data/lib/stomper/frames/disconnect.rb +0 -10
- data/lib/stomper/frames/error.rb +0 -21
- data/lib/stomper/frames/message.rb +0 -48
- data/lib/stomper/frames/receipt.rb +0 -19
- data/lib/stomper/frames/send.rb +0 -10
- data/lib/stomper/frames/server_frame.rb +0 -38
- data/lib/stomper/frames/subscribe.rb +0 -42
- data/lib/stomper/frames/unsubscribe.rb +0 -19
- data/lib/stomper/open_uri_interface.rb +0 -41
- data/lib/stomper/receipt_handlers.rb +0 -23
- data/lib/stomper/receiptor.rb +0 -38
- data/lib/stomper/subscriber.rb +0 -76
- data/lib/stomper/subscription.rb +0 -128
- data/lib/stomper/subscriptions.rb +0 -95
- data/lib/stomper/threaded_receiver.rb +0 -59
- data/lib/stomper/transaction.rb +0 -185
- data/lib/stomper/transactor.rb +0 -50
- data/lib/stomper/uri.rb +0 -55
- data/spec/client_spec.rb +0 -29
- data/spec/connection_spec.rb +0 -22
- data/spec/frame_reader_spec.rb +0 -37
- data/spec/frame_writer_spec.rb +0 -27
- data/spec/frames/client_frame_spec.rb +0 -66
- data/spec/frames/indirect_frame_spec.rb +0 -45
- data/spec/frames/server_frame_spec.rb +0 -85
- data/spec/open_uri_interface_spec.rb +0 -132
- data/spec/receiptor_spec.rb +0 -35
- data/spec/shared_connection_examples.rb +0 -79
- data/spec/subscriber_spec.rb +0 -77
- data/spec/subscription_spec.rb +0 -157
- data/spec/subscriptions_spec.rb +0 -145
- data/spec/threaded_receiver_spec.rb +0 -33
- data/spec/transaction_spec.rb +0 -139
- data/spec/transactor_spec.rb +0 -46
@@ -0,0 +1,187 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Extensions
|
5
|
+
describe Common do
|
6
|
+
before(:each) do
|
7
|
+
@common = mock("common", :version => '1.0')
|
8
|
+
@common.extend Common
|
9
|
+
@subscription_manager = mock('subscription manager')
|
10
|
+
@common.stub!(:subscription_manager).and_return(@subscription_manager)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "Shared default interface" do
|
14
|
+
before(:each) do
|
15
|
+
@common.stub!(:is_a?).with(::Stomper::Connection).and_return(true)
|
16
|
+
end
|
17
|
+
it "should transmit an ABORT frame" do
|
18
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'transaction' => 'tx-1234'}, 'ABORT'))
|
19
|
+
@common.abort('tx-1234')
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should transmit a BEGIN frame" do
|
23
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'transaction' => 'tx-3124'}, 'BEGIN'))
|
24
|
+
@common.begin('tx-3124')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should transmit a COMMIT frame" do
|
28
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'transaction' => 'tx-4321'}, 'COMMIT'))
|
29
|
+
@common.commit('tx-4321')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should transmit a SEND frame with :send" do
|
33
|
+
@common.should_receive(:transmit).with(stomper_frame('testing :send', {'destination' => '/queue/test_send'}, 'SEND'))
|
34
|
+
@common.send('/queue/test_send', 'testing :send')
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should transmit a SEND frame with :puts" do
|
38
|
+
@common.should_receive(:transmit).with(stomper_frame('testing :puts', {'destination' => '/queue/test_puts'}, 'SEND'))
|
39
|
+
@common.puts('/queue/test_puts', 'testing :puts')
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should transmit a SUBSCRIBE frame" do
|
43
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'destination' => '/queue/test_subscribe'}, 'SUBSCRIBE'))
|
44
|
+
@common.subscribe('/queue/test_subscribe')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should transmit an UNSUBSCRIBE frame for a given subscription ID" do
|
48
|
+
@subscription_manager.should_receive(:subscribed_id?).with('subscription-1234').and_return(true)
|
49
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'id' => 'subscription-1234'}, 'UNSUBSCRIBE'))
|
50
|
+
@common.unsubscribe('subscription-1234')
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should transmit an UNSUBSCRIBE frame for a given SUBSCRIBE frame" do
|
54
|
+
@subscription_manager.should_receive(:subscribed_id?).with('id-in-frame-4321').and_return(true)
|
55
|
+
subscribe = ::Stomper::Frame.new('SUBSCRIBE', { :id => 'id-in-frame-4321' })
|
56
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'id' => 'id-in-frame-4321'}, 'UNSUBSCRIBE'))
|
57
|
+
@common.unsubscribe(subscribe)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should raise an error on :nack" do
|
61
|
+
lambda { @common.nack("msg-001", "sub-001") }.should raise_error
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should transmit an ACK for a message-id given MESSAGE frame" do
|
65
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-001'}, 'ACK'))
|
66
|
+
@common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-001' }, 'some body'))
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should transmit an ACK for a message-id given a message-id" do
|
70
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-123'}, 'ACK'))
|
71
|
+
@common.ack('msg-123')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should raise an error when the message-id cannot be inferred" do
|
75
|
+
lambda { @common.ack('') }.should raise_error(ArgumentError)
|
76
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :id => '' }, 'some body')) }.should raise_error(ArgumentError)
|
77
|
+
lambda { @common.ack(nil) }.should raise_error(ArgumentError)
|
78
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', {}, 'some body')) }.should raise_error(ArgumentError)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "subscription handling" do
|
83
|
+
it "should add a callback to the subscription manager on :subscribe" do
|
84
|
+
# In this tests, we do not care about the particulars of the generated
|
85
|
+
# frame. Our only interest is the interaction with the subscription
|
86
|
+
# manager
|
87
|
+
@common.stub!(:transmit).and_return { |f| f }
|
88
|
+
@subscription_manager.should_receive(:add).with(stomper_frame_with_headers({}, 'SUBSCRIBE'), an_instance_of(Proc))
|
89
|
+
@common.subscribe('/queue/test') { |m| true }
|
90
|
+
end
|
91
|
+
it "should unsubscribe by destination" do
|
92
|
+
# As this invocation of unsubscribe will result in multiple UNSUBSCRIBE
|
93
|
+
# frames being generated, we do care about the actual frames generated
|
94
|
+
# as well.
|
95
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({:id => '1234'}, 'UNSUBSCRIBE'))
|
96
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({:id => '4567'}, 'UNSUBSCRIBE'))
|
97
|
+
@subscription_manager.should_receive(:subscribed_id?).with('/queue/test').and_return(false)
|
98
|
+
@subscription_manager.should_receive(:subscribed_destination?).with('/queue/test').and_return(true)
|
99
|
+
@subscription_manager.should_receive(:ids_for_destination).with('/queue/test').and_return(['1234', '4567'])
|
100
|
+
@common.unsubscribe("/queue/test")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "SEND receipt handling" do
|
105
|
+
it "should build a receipt scope when a block is passed to :send" do
|
106
|
+
receipt_scope = mock('receipt scope')
|
107
|
+
@common.should_receive(:with_receipt).and_return(receipt_scope)
|
108
|
+
receipt_scope.should_receive(:transmit).with(stomper_frame('my message', { :destination => '/topic/testing' }, 'SEND'))
|
109
|
+
@common.send('/topic/testing', 'my message') { |r| true }
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should build a receipt scope when a block is passed to :puts" do
|
113
|
+
receipt_scope = mock('receipt scope')
|
114
|
+
@common.should_receive(:with_receipt).and_return(receipt_scope)
|
115
|
+
receipt_scope.should_receive(:transmit).with(stomper_frame('my message', { :destination => '/topic/testing' }, 'SEND'))
|
116
|
+
@common.puts('/topic/testing', 'my message') { |r| true }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "1.1 Protocol Extensions" do
|
121
|
+
before(:each) do
|
122
|
+
@common.stub!(:version).and_return('1.1')
|
123
|
+
Common.extend_by_protocol_version(@common, '1.1')
|
124
|
+
end
|
125
|
+
it "should include V1_1 module" do
|
126
|
+
@common.should be_a_kind_of(Common::V1_1)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should transmit a NACK for a message-id and subscription given MESSAGE frame" do
|
130
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'NACK'))
|
131
|
+
@common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456', :subscription => 'sub-123' }, 'some body'))
|
132
|
+
end
|
133
|
+
it "should transmit a NACK for a message-id and subscription given MESSAGE frame w/o subscription and subscription-id" do
|
134
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'NACK'))
|
135
|
+
@common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456', :subscription => '' }, 'some body'), 'sub-123')
|
136
|
+
end
|
137
|
+
it "should transmit a NACK for a message-id and subscription given message-id and subscription-id" do
|
138
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'NACK'))
|
139
|
+
@common.nack('msg-456', 'sub-123')
|
140
|
+
end
|
141
|
+
it "should raise an error when the subscription ID cannot be inferred" do
|
142
|
+
lambda { @common.nack('msg-456') }.should raise_error(ArgumentError)
|
143
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body')) }.should raise_error(ArgumentError)
|
144
|
+
lambda { @common.nack('msg-456', '') }.should raise_error(ArgumentError)
|
145
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body'), '') }.should raise_error(ArgumentError)
|
146
|
+
lambda { @common.nack('msg-456', { :subscription => ''}) }.should raise_error(ArgumentError)
|
147
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body'), '', { :subscription => nil }) }.should raise_error(ArgumentError)
|
148
|
+
lambda { @common.nack('msg-456', { :subscription => 'sub-123'}) }.should raise_error(ArgumentError)
|
149
|
+
end
|
150
|
+
it "should raise an error when the message-id cannot be inferred" do
|
151
|
+
lambda { @common.nack('', 'sub-123') }.should raise_error(ArgumentError)
|
152
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', { :'message-id' => '' }, 'some body'), 'sub-123') }.should raise_error(ArgumentError)
|
153
|
+
lambda { @common.nack(nil, 'sub-123') }.should raise_error(ArgumentError)
|
154
|
+
lambda { @common.nack(Stomper::Frame.new('MESSAGE', {}, 'some body'), 'sub-123') }.should raise_error(ArgumentError)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should transmit an ACK for a message-id and subscription given MESSAGE frame" do
|
158
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'ACK'))
|
159
|
+
@common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456', :subscription => 'sub-123' }, 'some body'))
|
160
|
+
end
|
161
|
+
it "should transmit an ACK for a message-id and subscription given MESSAGE frame w/o subscription and subscription-id" do
|
162
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'ACK'))
|
163
|
+
@common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456', :subscription => '' }, 'some body'), 'sub-123')
|
164
|
+
end
|
165
|
+
it "should transmit an ACK for a message-id and subscription given message-id and subscription-id" do
|
166
|
+
@common.should_receive(:transmit).with(stomper_frame_with_headers({'message-id' => 'msg-456', 'subscription' => 'sub-123'}, 'ACK'))
|
167
|
+
@common.ack('msg-456', 'sub-123')
|
168
|
+
end
|
169
|
+
it "should raise an error when the subscription ID cannot be inferred" do
|
170
|
+
lambda { @common.ack('msg-456') }.should raise_error(ArgumentError)
|
171
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body')) }.should raise_error(ArgumentError)
|
172
|
+
lambda { @common.ack('msg-456', '') }.should raise_error(ArgumentError)
|
173
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body'), '') }.should raise_error(ArgumentError)
|
174
|
+
lambda { @common.ack('msg-456', { :subscription => ''}) }.should raise_error(ArgumentError)
|
175
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => 'msg-456' }, 'some body'), '', { :subscription => nil }) }.should raise_error(ArgumentError)
|
176
|
+
lambda { @common.ack('msg-456', { :subscription => 'sub-123'}) }.should raise_error(ArgumentError)
|
177
|
+
end
|
178
|
+
it "should raise an error when the message-id cannot be inferred" do
|
179
|
+
lambda { @common.ack('', 'sub-123') }.should raise_error(ArgumentError)
|
180
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', { :'message-id' => '' }, 'some body'), 'sub-123') }.should raise_error(ArgumentError)
|
181
|
+
lambda { @common.ack(nil, 'sub-123') }.should raise_error(ArgumentError)
|
182
|
+
lambda { @common.ack(Stomper::Frame.new('MESSAGE', {}, 'some body'), 'sub-123') }.should raise_error(ArgumentError)
|
183
|
+
lambda { @common.ack('', { :'message-id' => 'msg-456', :subscription => 'sub-123'}) }.should raise_error(ArgumentError)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Extensions
|
5
|
+
describe Events do
|
6
|
+
before(:each) do
|
7
|
+
@events = mock('events')
|
8
|
+
@events.extend Events
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "basic event callbacks" do
|
12
|
+
[:on_abort, :on_ack, :on_begin, :on_commit, :on_connect, :on_stomp,
|
13
|
+
:on_connected, :on_disconnect, :on_error, :on_message, :on_nack,
|
14
|
+
:on_receipt, :on_send, :on_subscribe, :on_unsubscribe, :on_client_beat,
|
15
|
+
:on_broker_beat, :on_connection_established, :on_connection_closed,
|
16
|
+
:on_connection_terminated, :on_connection_disconnected,
|
17
|
+
:on_connection_died, :before_transmitting, :after_transmitting,
|
18
|
+
:before_receiving, :after_receiving].each do |event_name|
|
19
|
+
|
20
|
+
it "should register a callback for #{event_name} and trigger appropriately" do
|
21
|
+
triggered = false
|
22
|
+
@events.__send__(event_name) do
|
23
|
+
triggered = true
|
24
|
+
end
|
25
|
+
@events.__send__(:trigger_event, event_name)
|
26
|
+
triggered.should be_true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "frame event callbacks" do
|
32
|
+
[ :on_connected, :on_error, :on_message, :on_receipt].each do |event_name|
|
33
|
+
|
34
|
+
it "should register a callback for #{event_name} and trigger when the frame is received" do
|
35
|
+
command_name = event_name.to_s.split('_').last.upcase
|
36
|
+
triggered = false
|
37
|
+
@events.__send__(event_name) do
|
38
|
+
triggered = true
|
39
|
+
end
|
40
|
+
@events.__send__(:trigger_received_frame, ::Stomper::Frame.new(command_name))
|
41
|
+
triggered.should be_true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
[:on_abort, :on_ack, :on_begin, :on_commit, :on_connect, :on_stomp,
|
46
|
+
:on_disconnect, :on_nack, :on_send, :on_subscribe, :on_unsubscribe].each do |event_name|
|
47
|
+
|
48
|
+
it "should register a callback for #{event_name} and trigger when the frame is transmitted" do
|
49
|
+
command_name = event_name.to_s.split('_').last.upcase
|
50
|
+
triggered = false
|
51
|
+
@events.__send__(event_name) do
|
52
|
+
triggered = true
|
53
|
+
end
|
54
|
+
@events.__send__(:trigger_transmitted_frame, ::Stomper::Frame.new(command_name))
|
55
|
+
triggered.should be_true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should trigger a broker beat when receiving a frame with no command" do
|
60
|
+
triggered = false
|
61
|
+
@events.on_broker_beat do
|
62
|
+
triggered = true
|
63
|
+
end
|
64
|
+
@events.__send__(:trigger_received_frame, ::Stomper::Frame.new)
|
65
|
+
triggered.should be_true
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should trigger a client beat when receiving a frame with no command" do
|
69
|
+
triggered = false
|
70
|
+
@events.on_client_beat do
|
71
|
+
triggered = true
|
72
|
+
end
|
73
|
+
@events.__send__(:trigger_transmitted_frame, ::Stomper::Frame.new)
|
74
|
+
triggered.should be_true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Extensions
|
5
|
+
describe Heartbeat do
|
6
|
+
before(:each) do
|
7
|
+
@heartbeats = mock('heartbeats')
|
8
|
+
@heartbeats.extend Heartbeat
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "Shared default interface" do
|
12
|
+
it "should be dead? when it is not alive?" do
|
13
|
+
@heartbeats.stub(:alive? => true)
|
14
|
+
@heartbeats.dead?.should be_false
|
15
|
+
|
16
|
+
@heartbeats.stub(:alive? => false)
|
17
|
+
@heartbeats.dead?.should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should do nothing when :beat is called" do
|
21
|
+
@heartbeats.beat.should be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be alive if the connection is connected?" do
|
25
|
+
@heartbeats.should_receive(:connected?).and_return(true)
|
26
|
+
@heartbeats.alive?.should be_true
|
27
|
+
@heartbeats.should_receive(:connected?).and_return(false)
|
28
|
+
@heartbeats.alive?.should be_false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "1.1 Protocol Extensions" do
|
33
|
+
before(:each) do
|
34
|
+
Heartbeat.extend_by_protocol_version(@heartbeats, '1.1')
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should include the 1.1 extensions" do
|
38
|
+
@heartbeats.should be_a_kind_of(Heartbeat::V1_1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should transmit a heartbeat frame through :beat" do
|
42
|
+
@heartbeats.should_receive(:transmit).with(stomper_heartbeat_frame)
|
43
|
+
@heartbeats.beat
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should be alive if client and broker are alive and connected" do
|
47
|
+
@heartbeats.stub(:client_alive? => true, :broker_alive? => true, :connected? => true)
|
48
|
+
@heartbeats.alive?.should be_true
|
49
|
+
@heartbeats.stub(:connected? => false)
|
50
|
+
@heartbeats.alive?.should be_false
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
it "should be dead if connected and broker is alive but client is not" do
|
55
|
+
@heartbeats.stub(:client_alive? => false, :broker_alive? => true, :connected? => true)
|
56
|
+
@heartbeats.alive?.should be_false
|
57
|
+
@heartbeats.stub(:client_alive? => true)
|
58
|
+
@heartbeats.alive?.should be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be dead if connected and client is alive but broker is not" do
|
62
|
+
@heartbeats.stub(:client_alive? => true, :broker_alive? => false, :connected? => true)
|
63
|
+
@heartbeats.alive?.should be_false
|
64
|
+
@heartbeats.stub(:broker_alive? => true)
|
65
|
+
@heartbeats.alive?.should be_true
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should have a living client if client beats are disabled" do
|
69
|
+
@heartbeats.stub(:heartbeating => [0, 10], :connected? => true)
|
70
|
+
@heartbeats.client_alive?.should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should have a living broker if broker beats are disabled" do
|
74
|
+
@heartbeats.stub(:heartbeating => [10, 0], :connected? => true)
|
75
|
+
@heartbeats.broker_alive?.should be_true
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should have a living client if beating is enabled and transmitted within a marin of error" do
|
79
|
+
@heartbeats.stub(:heartbeating => [1_000, 0], :connected? => true)
|
80
|
+
@heartbeats.stub(:duration_since_transmitted => 50)
|
81
|
+
@heartbeats.client_alive?.should be_true
|
82
|
+
@heartbeats.stub(:duration_since_transmitted => 1_000)
|
83
|
+
@heartbeats.client_alive?.should be_true
|
84
|
+
@heartbeats.stub(:duration_since_transmitted => 1_100)
|
85
|
+
@heartbeats.client_alive?.should be_true
|
86
|
+
@heartbeats.stub(:duration_since_transmitted => 1_200)
|
87
|
+
@heartbeats.client_alive?.should be_false
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should have a living broker if beating is enabled and received within a marin of error" do
|
91
|
+
@heartbeats.stub(:heartbeating => [0, 5_000], :connected? => true)
|
92
|
+
@heartbeats.stub(:duration_since_received => 100)
|
93
|
+
@heartbeats.broker_alive?.should be_true
|
94
|
+
@heartbeats.stub(:duration_since_received => 5_000)
|
95
|
+
@heartbeats.broker_alive?.should be_true
|
96
|
+
@heartbeats.stub(:duration_since_received => 5_500)
|
97
|
+
@heartbeats.broker_alive?.should be_true
|
98
|
+
@heartbeats.stub(:duration_since_received => 5_600)
|
99
|
+
@heartbeats.broker_alive?.should be_false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Stomper::Extensions
|
5
|
+
describe Scoping do
|
6
|
+
before(:each) do
|
7
|
+
@scoping = mock("scoping", :version => '1.0')
|
8
|
+
@scoping.extend Scoping
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should return a new HeaderScope" do
|
12
|
+
@scoping.with_headers({}).should be_an_instance_of(::Stomper::Scopes::HeaderScope)
|
13
|
+
end
|
14
|
+
it "should return a new TransactionScope" do
|
15
|
+
@scoping.with_transaction.should be_an_instance_of(::Stomper::Scopes::TransactionScope)
|
16
|
+
end
|
17
|
+
it "should return a new ReceiptScope" do
|
18
|
+
@scoping.with_receipt.should be_an_instance_of(::Stomper::Scopes::ReceiptScope)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,318 @@
|
|
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
|