stomper 0.4 → 1.0.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 +7 -0
- data/README.rdoc +63 -11
- data/lib/stomper.rb +13 -1
- data/lib/stomper/client.rb +3 -284
- data/lib/stomper/connection.rb +67 -114
- data/lib/stomper/frame_reader.rb +73 -0
- data/lib/stomper/frame_writer.rb +21 -0
- data/lib/stomper/frames.rb +24 -9
- data/lib/stomper/frames/abort.rb +2 -6
- data/lib/stomper/frames/ack.rb +2 -6
- data/lib/stomper/frames/begin.rb +2 -5
- data/lib/stomper/frames/client_frame.rb +30 -27
- data/lib/stomper/frames/commit.rb +1 -5
- data/lib/stomper/frames/connect.rb +2 -7
- data/lib/stomper/frames/connected.rb +11 -8
- data/lib/stomper/frames/disconnect.rb +1 -4
- data/lib/stomper/frames/error.rb +3 -8
- data/lib/stomper/frames/message.rb +15 -11
- data/lib/stomper/frames/receipt.rb +1 -6
- data/lib/stomper/frames/send.rb +1 -5
- data/lib/stomper/frames/server_frame.rb +13 -23
- data/lib/stomper/frames/subscribe.rb +9 -14
- data/lib/stomper/frames/unsubscribe.rb +3 -7
- data/lib/stomper/open_uri_interface.rb +41 -0
- data/lib/stomper/receipt_handlers.rb +23 -0
- data/lib/stomper/receiptor.rb +38 -0
- data/lib/stomper/sockets.rb +37 -0
- data/lib/stomper/subscriber.rb +76 -0
- data/lib/stomper/subscription.rb +14 -14
- data/lib/stomper/threaded_receiver.rb +59 -0
- data/lib/stomper/transaction.rb +13 -8
- data/lib/stomper/transactor.rb +50 -0
- data/lib/stomper/uri.rb +55 -0
- data/spec/client_spec.rb +7 -158
- data/spec/connection_spec.rb +13 -3
- data/spec/frame_reader_spec.rb +37 -0
- data/spec/frame_writer_spec.rb +27 -0
- data/spec/frames/client_frame_spec.rb +22 -98
- data/spec/frames/indirect_frame_spec.rb +45 -0
- data/spec/frames/server_frame_spec.rb +15 -16
- data/spec/open_uri_interface_spec.rb +132 -0
- data/spec/receiptor_spec.rb +35 -0
- data/spec/shared_connection_examples.rb +12 -17
- data/spec/spec_helper.rb +6 -0
- data/spec/subscriber_spec.rb +77 -0
- data/spec/subscription_spec.rb +11 -11
- data/spec/subscriptions_spec.rb +3 -6
- data/spec/threaded_receiver_spec.rb +33 -0
- data/spec/transaction_spec.rb +5 -5
- data/spec/transactor_spec.rb +46 -0
- metadata +30 -6
- data/lib/stomper/frames/headers.rb +0 -68
- data/spec/frames/headers_spec.rb +0 -54
@@ -0,0 +1,59 @@
|
|
1
|
+
module Stomper
|
2
|
+
module ThreadedReceiver
|
3
|
+
def self.extended(base)
|
4
|
+
base.instance_eval do
|
5
|
+
@receiver_mutex = Mutex.new
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Starts the threaded receiver on a connection, calling receive
|
10
|
+
# on the connection repeatedly in a separate thread until the receiver
|
11
|
+
# is stopped or the connection is closed.
|
12
|
+
#
|
13
|
+
# @return self
|
14
|
+
# @see ThreadedReceiver#stop
|
15
|
+
# @see Connection#receive
|
16
|
+
# @see Connection#connected?
|
17
|
+
def start(opts={})
|
18
|
+
connect unless connected?
|
19
|
+
do_start = false
|
20
|
+
@receiver_mutex.synchronize do
|
21
|
+
do_start = !started?
|
22
|
+
end
|
23
|
+
if do_start
|
24
|
+
@started = true
|
25
|
+
@run_thread = Thread.new() do
|
26
|
+
while started? && connected?
|
27
|
+
receive
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
# Stops the threaded receiver on a connection thereby stopping further
|
35
|
+
# calls to receive.
|
36
|
+
#
|
37
|
+
# @return self
|
38
|
+
# @see ThreadedReceiver#start
|
39
|
+
# @see Connection#receive
|
40
|
+
# @see Connection#connected?
|
41
|
+
def stop
|
42
|
+
do_stop = false
|
43
|
+
@receiver_mutex.synchronize do
|
44
|
+
do_stop = started?
|
45
|
+
end
|
46
|
+
if do_stop
|
47
|
+
@started = false
|
48
|
+
@run_thread.join
|
49
|
+
@run_thread = nil
|
50
|
+
end
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def started?
|
56
|
+
@started
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/stomper/transaction.rb
CHANGED
@@ -61,7 +61,7 @@ module Stomper
|
|
61
61
|
# is an instance of Stomper::Client and is required so that the Transaction
|
62
62
|
# instance has somewhere to forward +begin+, +ack+ and +abort+ methods
|
63
63
|
# to. If the +trans_id+ parameter is not specified, an id is automatically
|
64
|
-
# generated of the form
|
64
|
+
# generated of the form +tx-<Time.now.to_f>+. This name can be accessed
|
65
65
|
# through the +id+ attribute and is used in naming the transaction to
|
66
66
|
# the stomp broker. If +block+ is given, the Transaction instance immediately
|
67
67
|
# calls its perform method with the supplied +block+.
|
@@ -94,6 +94,9 @@ module Stomper
|
|
94
94
|
# will raise a TransactionAborted exception if the +block+ evaluation fails.
|
95
95
|
# This behavior allows for nesting transactions and ensuring that if a nested
|
96
96
|
# transaction fails, so do all of its ancestors.
|
97
|
+
#
|
98
|
+
# @param [Proc] block A block of code that is evaluated as part of the transaction.
|
99
|
+
# @raise [TransactionAborted] raises an exception if the given block raises an exception
|
97
100
|
def perform(&block) #:yields: transaction
|
98
101
|
begin
|
99
102
|
@client.begin(@id)
|
@@ -124,7 +127,7 @@ module Stomper
|
|
124
127
|
# Similar to Stomper::Client#transaction, this method creates a new
|
125
128
|
# Transaction object, nested inside of this one. To prevent name
|
126
129
|
# collisions, this method automatically generates a transaction id,
|
127
|
-
# if one is not specified, of the form
|
130
|
+
# if one is not specified, of the form +<parent_transaction_id>-<Time.now.to_f>+.
|
128
131
|
def transaction(transaction_id=nil,&block)
|
129
132
|
# To get a transaction name guaranteed to not collide with this one
|
130
133
|
# we will supply an explicit id to the constructor unless an id was
|
@@ -137,30 +140,32 @@ module Stomper
|
|
137
140
|
# into the +headers+ hash, thus informing the stomp broker that the message
|
138
141
|
# generated here is part of this transaction.
|
139
142
|
def send(destination, body, headers={})
|
140
|
-
headers
|
141
|
-
@client.send(destination, body, headers)
|
143
|
+
@client.send(destination, body, headers.merge({:transaction => @id }))
|
142
144
|
end
|
143
145
|
|
144
146
|
# Wraps the Stomper::Client#ack method, injecting a "transaction" header
|
145
147
|
# into the +headers+ hash, thus informing the stomp broker that the message
|
146
148
|
# acknowledgement is part of this transaction.
|
147
149
|
def ack(message_or_id, headers={})
|
148
|
-
headers
|
149
|
-
@client.ack(message_or_id, headers)
|
150
|
+
@client.ack(message_or_id, headers.merge({ :transaction => @id }))
|
150
151
|
end
|
151
152
|
|
152
153
|
# Aborts this transaction if it has not already been committed or aborted.
|
153
154
|
# Note that it does so by raising a TransactionAborted exception, allowing
|
154
155
|
# the +abort+ call to force any ancestral transactions to also fail.
|
155
156
|
#
|
156
|
-
#
|
157
|
+
# @see Transaction#commit
|
158
|
+
# @see Transaction#committed?
|
159
|
+
# @see Transaction#aborted?
|
157
160
|
def abort
|
158
161
|
raise TransactionAborted, "transaction '#{@id}' aborted explicitly" if _abort
|
159
162
|
end
|
160
163
|
|
161
164
|
# Commits this transaction unless it has already been committed or aborted.
|
162
165
|
#
|
163
|
-
#
|
166
|
+
# @see Transaction#abort
|
167
|
+
# @see Transaction#committed?
|
168
|
+
# @see Transaction#aborted?
|
164
169
|
def commit
|
165
170
|
# Guard against sending multiple commit messages to the server for a
|
166
171
|
# single transaction.
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Stomper
|
2
|
+
module Transactor
|
3
|
+
# Creates a new Stomper::Transaction object and evaluates
|
4
|
+
# the supplied +block+ within a transactional context. If
|
5
|
+
# the block executes successfully, the transaction is committed,
|
6
|
+
# otherwise it is aborted. This method is meant to provide a less
|
7
|
+
# tedious approach to transactional messaging than the +begin+,
|
8
|
+
# +abort+ and +commit+ methods.
|
9
|
+
#
|
10
|
+
# See also: Stomper::ClientInterface::begin, Stomper::ClientInterface::commit,
|
11
|
+
# Stomper::ClientInterface::abort, Stomper::Transaction
|
12
|
+
def transaction(transaction_id=nil, &block)
|
13
|
+
begin
|
14
|
+
Stomper::Transaction.new(self, transaction_id, &block)
|
15
|
+
rescue Stomper::TransactionAborted
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Tells the stomp broker to commit a transaction named by the
|
21
|
+
# supplied +transaction_id+ parameter. When used in conjunction with
|
22
|
+
# +begin+, and +abort+, a means for manually handling transactional
|
23
|
+
# message passing is provided.
|
24
|
+
#
|
25
|
+
# See Also: transaction
|
26
|
+
def commit(transaction_id)
|
27
|
+
transmit(Stomper::Frames::Commit.new(transaction_id))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Tells the stomp broker to abort a transaction named by the
|
31
|
+
# supplied +transaction_id+ parameter. When used in conjunction with
|
32
|
+
# +begin+, and +commit+, a means for manually handling transactional
|
33
|
+
# message passing is provided.
|
34
|
+
#
|
35
|
+
# See Also: transaction
|
36
|
+
def abort(transaction_id)
|
37
|
+
transmit(Stomper::Frames::Abort.new(transaction_id))
|
38
|
+
end
|
39
|
+
|
40
|
+
# Tells the stomp broker to begin a transaction named by the
|
41
|
+
# supplied +transaction_id+ parameter. When used in conjunction with
|
42
|
+
# +commit+, and +abort+, a means for manually handling transactional
|
43
|
+
# message passing is provided.
|
44
|
+
#
|
45
|
+
# See also: transaction
|
46
|
+
def begin(transaction_id)
|
47
|
+
transmit(Stomper::Frames::Begin.new(transaction_id))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/stomper/uri.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module URI
|
2
|
+
class STOMP < ::URI::Generic
|
3
|
+
# Got to love the magic of URI::Generic.
|
4
|
+
# By setting this constant, you ensure that all
|
5
|
+
# Stomp URI's have this port if one isn't specified.
|
6
|
+
DEFAULT_PORT = 61613
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_socket
|
13
|
+
::Stomper::Sockets::TCP.new(self.host||'localhost', self.port)
|
14
|
+
end
|
15
|
+
|
16
|
+
def open(*args)
|
17
|
+
conx = Stomper::Connection.open(self, :threaded_receiver => false)
|
18
|
+
conx.extend Stomper::OpenUriInterface
|
19
|
+
if block_given?
|
20
|
+
begin
|
21
|
+
yield conx
|
22
|
+
ensure
|
23
|
+
conx.disconnect
|
24
|
+
end
|
25
|
+
end
|
26
|
+
conx
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class STOMP_SSL < STOMP
|
31
|
+
DEFAULT_PORT = 61612
|
32
|
+
|
33
|
+
def initialize(*args)
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
# Creates a socket from the URI
|
38
|
+
def create_socket
|
39
|
+
::Stomper::Sockets::SSL.new(self.host||'localhost', self.port)
|
40
|
+
end
|
41
|
+
|
42
|
+
# The +uri+ standard library resolves string URI's to concrete classes
|
43
|
+
# by matching the string's schema to the name of a subclass of URI::Generic.
|
44
|
+
# Ruby doesn't support '+' symbols in a class name, so the only way to handle
|
45
|
+
# schemas with odd characters is to override the "to_s" function of the class.
|
46
|
+
#
|
47
|
+
# Why do I get the feeling this might be a bad idea?
|
48
|
+
def self.to_s
|
49
|
+
"URI::STOMP+SSL"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
@@schemes['STOMP'] = STOMP
|
54
|
+
@@schemes['STOMP+SSL'] = STOMP_SSL
|
55
|
+
end
|
data/spec/client_spec.rb
CHANGED
@@ -2,179 +2,28 @@ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
|
2
2
|
|
3
3
|
module Stomper
|
4
4
|
describe Client do
|
5
|
+
class MockConcreteClient
|
6
|
+
include Stomper::Client
|
7
|
+
end
|
8
|
+
|
5
9
|
before(:each) do
|
6
|
-
|
7
|
-
@mock_connection = mock("connection")
|
8
|
-
@mock_connection.should_receive(:disconnect).with(no_args()).at_most(:once).and_return(nil)
|
9
|
-
Stomper::Connection.stub!(:new).and_return(@mock_connection)
|
10
|
-
@client = Client.new("stomp:///")
|
10
|
+
@client = MockConcreteClient.new
|
11
11
|
end
|
12
12
|
|
13
13
|
describe "expected interface" do
|
14
14
|
it "should provide a send method" do
|
15
15
|
@client.should respond_to(:send)
|
16
|
-
@
|
16
|
+
@client.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Send)).twice.and_return(nil)
|
17
17
|
@client.send("/queue/to", "message body", {:additional => 'header'})
|
18
18
|
@client.send("/queue/to", "message body")
|
19
19
|
end
|
20
|
-
it "should provide a subscribe method" do
|
21
|
-
@client.should respond_to(:subscribe)
|
22
|
-
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Subscribe)).twice.and_return(nil)
|
23
|
-
@client.subscribe("/queue/to", {:additional => 'header'})
|
24
|
-
@client.subscribe("/queue/to")
|
25
|
-
end
|
26
|
-
it "should provide an unsubscribe method" do
|
27
|
-
@client.should respond_to(:unsubscribe)
|
28
|
-
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Subscribe)).twice.and_return(nil)
|
29
|
-
@client.subscribe("/queue/to", {:id => 'subscription-id'})
|
30
|
-
@client.subscribe("/queue/to")
|
31
|
-
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Unsubscribe)).twice.and_return(nil)
|
32
|
-
@client.unsubscribe("/queue/to", 'subscription-id')
|
33
|
-
@client.unsubscribe("/queue/to")
|
34
|
-
end
|
35
20
|
it "should provide an ack method" do
|
36
21
|
@client.should respond_to(:ack)
|
37
|
-
@
|
22
|
+
@client.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Ack)).exactly(3).times.and_return(nil)
|
38
23
|
@client.ack("message-id", {:additional => "header"})
|
39
24
|
@client.ack("message-id")
|
40
25
|
@client.ack(Stomper::Frames::Message.new({:'message-id' => 'msg-001'}, "body"))
|
41
26
|
end
|
42
|
-
it "should provide a begin method" do
|
43
|
-
@client.should respond_to(:begin)
|
44
|
-
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Begin)).once.and_return(nil)
|
45
|
-
@client.begin("tx-001")
|
46
|
-
end
|
47
|
-
it "should proivde an abort method" do
|
48
|
-
@client.should respond_to(:abort)
|
49
|
-
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Abort)).once.and_return(nil)
|
50
|
-
@client.abort("tx-001")
|
51
|
-
end
|
52
|
-
it "should provide a commit method" do
|
53
|
-
@client.should respond_to(:commit)
|
54
|
-
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Commit)).once.and_return(nil)
|
55
|
-
@client.commit("tx-001")
|
56
|
-
end
|
57
|
-
it "should provide a recieve method" do
|
58
|
-
@client.should respond_to(:receive)
|
59
|
-
end
|
60
|
-
it "should provide a disconnect method" do
|
61
|
-
@client.should respond_to(:disconnect)
|
62
|
-
end
|
63
|
-
it "should provide a close method" do
|
64
|
-
@client.should respond_to(:close)
|
65
|
-
end
|
66
|
-
it "should provide a connectivity test" do
|
67
|
-
@client.should respond_to(:connected?)
|
68
|
-
end
|
69
|
-
it "should provide a connect method" do
|
70
|
-
@client.should respond_to(:connect)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
describe "threaded receiver" do
|
75
|
-
it "should respond to start and stop" do
|
76
|
-
@client.should respond_to(:start)
|
77
|
-
@client.should respond_to(:stop)
|
78
|
-
@client.should respond_to(:receiving?)
|
79
|
-
end
|
80
|
-
it "should only be receiving when it is started" do
|
81
|
-
@mock_connection.stub!(:receive).and_return(nil)
|
82
|
-
@mock_connection.should_receive(:connected?).any_number_of_times.and_return(true)
|
83
|
-
@client.receiving?.should be_false
|
84
|
-
@client.start
|
85
|
-
@client.receiving?.should be_true
|
86
|
-
@client.stop
|
87
|
-
@client.receiving?.should be_false
|
88
|
-
end
|
89
|
-
it "should allow for a blocking threaded receiver" do
|
90
|
-
@mock_connection.should_receive(:receive).with(true).at_least(:once).and_return(nil)
|
91
|
-
@mock_connection.should_receive(:connected?).any_number_of_times.and_return(true)
|
92
|
-
@client.receiving?.should be_false
|
93
|
-
@client.start(:block => true)
|
94
|
-
@client.receiving?.should be_true
|
95
|
-
@client.stop
|
96
|
-
@client.receiving?.should be_false
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
|
101
|
-
describe "subscribing to queue" do
|
102
|
-
before(:each) do
|
103
|
-
@message_sent = Stomper::Frames::Message.new({'destination' => "/queue/test"}, "test message")
|
104
|
-
@mock_connection.should_receive(:connected?).any_number_of_times.and_return(true)
|
105
|
-
@mock_connection.should_receive(:transmit).with(duck_type(:to_stomp)).at_least(:once).and_return(nil)
|
106
|
-
@mock_connection.should_receive(:receive).any_number_of_times.and_return(@message_sent)
|
107
|
-
end
|
108
|
-
|
109
|
-
it "should subscribe to a destination with a block" do
|
110
|
-
wait_for_message = true
|
111
|
-
@message_received = nil
|
112
|
-
@client.start
|
113
|
-
@client.subscribe("/queue/test") do |msg|
|
114
|
-
@message_received = msg
|
115
|
-
wait_for_message = false
|
116
|
-
end
|
117
|
-
true while wait_for_message
|
118
|
-
@client.stop
|
119
|
-
@message_received.should == @message_sent
|
120
|
-
end
|
121
|
-
|
122
|
-
it "should not unsubscribe from all destinations when a subscription id is provided" do
|
123
|
-
@client.subscribe("/queue/test", { 'id' => 'subscription-1' }) do |msg|
|
124
|
-
@message_received = msg
|
125
|
-
end
|
126
|
-
@client.subscribe("/queue/test") do |msg|
|
127
|
-
@message_received = msg
|
128
|
-
end
|
129
|
-
@client.subscribe("/queue/test", :id => 'subscription-2') do |msg|
|
130
|
-
@message_received = msg
|
131
|
-
end
|
132
|
-
@client.unsubscribe("/queue/test", 'subscription-1')
|
133
|
-
@client.subscriptions.size.should == 2
|
134
|
-
end
|
135
|
-
|
136
|
-
it "should not unsubscribe from non-naive subscriptions when only a destination is supplied" do
|
137
|
-
@client.subscribe("/queue/test", { 'id' => 'subscription-1' }) do |msg|
|
138
|
-
@message_received = msg
|
139
|
-
end
|
140
|
-
@client.subscribe("/queue/test") do |msg|
|
141
|
-
@message_received = msg
|
142
|
-
end
|
143
|
-
@client.subscribe("/queue/test") do |msg|
|
144
|
-
@message_received = msg
|
145
|
-
end
|
146
|
-
@client.unsubscribe("/queue/test")
|
147
|
-
@client.subscriptions.size.should == 1
|
148
|
-
end
|
149
|
-
|
150
|
-
# Due to the receiver running in a separate thread, this may not be correct?
|
151
|
-
it "should unsubscribe from a destination and receive no more messages" do
|
152
|
-
@mutex = Mutex.new
|
153
|
-
@last_message_received = nil
|
154
|
-
@client.start
|
155
|
-
@client.subscribe("/queue/test") do |msg|
|
156
|
-
@last_message_received = Time.now
|
157
|
-
end
|
158
|
-
true until @last_message_received
|
159
|
-
@client.unsubscribe("/queue/test")
|
160
|
-
@unsubscribed_at = Time.now
|
161
|
-
@client.stop
|
162
|
-
(@last_message_received < @unsubscribed_at).should be_true
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
describe "transactions" do
|
167
|
-
before(:each) do
|
168
|
-
@mock_connection.should_receive(:transmit).with(duck_type(:to_stomp)).at_least(:once).and_return(nil)
|
169
|
-
end
|
170
|
-
|
171
|
-
it "should provide a transaction method that generates a new Transaction" do
|
172
|
-
@evaluated = false
|
173
|
-
@client.transaction do |t|
|
174
|
-
@evaluated = true
|
175
|
-
end
|
176
|
-
@evaluated.should be_true
|
177
|
-
end
|
178
27
|
end
|
179
28
|
end
|
180
29
|
end
|
data/spec/connection_spec.rb
CHANGED
@@ -3,10 +3,20 @@ require File.expand_path(File.join(File.dirname(__FILE__), 'shared_connection_ex
|
|
3
3
|
|
4
4
|
module Stomper
|
5
5
|
describe Connection do
|
6
|
-
|
7
|
-
|
6
|
+
describe "standard connection" do
|
7
|
+
before(:each) do
|
8
|
+
@connection = Connection.new("stomp:///")
|
9
|
+
end
|
10
|
+
|
11
|
+
it_should_behave_like "All Client Connections"
|
8
12
|
end
|
9
13
|
|
10
|
-
|
14
|
+
describe "ssl connection" do
|
15
|
+
before(:each) do
|
16
|
+
@connection = Connection.new("stomp+ssl:///")
|
17
|
+
end
|
18
|
+
|
19
|
+
it_should_behave_like "All Client Connections"
|
20
|
+
end
|
11
21
|
end
|
12
22
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
module Stomper
|
4
|
+
describe FrameReader do
|
5
|
+
before(:each) do
|
6
|
+
@input_stream = StringIO.new("", "w+")
|
7
|
+
@input_stream.send(:extend, Stomper::FrameReader)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should produce a stomper frame" do
|
11
|
+
@input_stream.string = "CONNECTED\n\n\0"
|
12
|
+
@input_stream.receive_frame.should be_an_instance_of(Stomper::Frames::Connected)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should read headers appropriately" do
|
16
|
+
@input_stream.string = "CONNECTED\nheader_1:a test value\nheader_2:another test value\nblather:47\n\nthe frame body\0"
|
17
|
+
@frame = @input_stream.receive_frame
|
18
|
+
@frame.headers.map { |(k,v)|
|
19
|
+
[k,v]
|
20
|
+
}.sort { |a, b| a.first.to_s <=> b.first.to_s }.should == [ [:blather, '47'], [:header_1, 'a test value'], [:header_2, 'another test value'] ]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should raise an exception when an invalid content-length is specified" do
|
24
|
+
@input_stream.string = "CONNECTED\ncontent-length:3\n\nsomething more than 3 bytes long\0"
|
25
|
+
lambda { @input_stream.receive_frame }.should raise_error(Stomper::MalformedFrameError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should read the body of a message when a content length is specified" do
|
29
|
+
@input_stream.string = "CONNECTED\ncontent-length:6\n\na test\0followed by trailing nonsense"
|
30
|
+
@input_stream.receive_frame.body.should == "a test"
|
31
|
+
end
|
32
|
+
it "should read the body of a message when no content length is specified" do
|
33
|
+
@input_stream.string = "CONNECTED\n\na bit more text and no direction\0followed by trailing nonsense"
|
34
|
+
@input_stream.receive_frame.body.should == "a bit more text and no direction"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|