adamh-amqp 0.6.3.1
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/README +128 -0
- data/Rakefile +15 -0
- data/amqp.gemspec +83 -0
- data/amqp.todo +32 -0
- data/doc/EXAMPLE_01_PINGPONG +2 -0
- data/doc/EXAMPLE_02_CLOCK +2 -0
- data/doc/EXAMPLE_03_STOCKS +2 -0
- data/doc/EXAMPLE_04_MULTICLOCK +2 -0
- data/doc/EXAMPLE_05_ACK +2 -0
- data/doc/EXAMPLE_05_POP +2 -0
- data/doc/EXAMPLE_06_HASHTABLE +2 -0
- data/examples/amqp/simple.rb +79 -0
- data/examples/mq/ack.rb +45 -0
- data/examples/mq/clock.rb +56 -0
- data/examples/mq/hashtable.rb +52 -0
- data/examples/mq/internal.rb +49 -0
- data/examples/mq/logger.rb +88 -0
- data/examples/mq/multiclock.rb +49 -0
- data/examples/mq/pingpong.rb +45 -0
- data/examples/mq/pop.rb +43 -0
- data/examples/mq/primes-simple.rb +19 -0
- data/examples/mq/primes.rb +99 -0
- data/examples/mq/stocks.rb +58 -0
- data/lib/amqp.rb +115 -0
- data/lib/amqp/buffer.rb +395 -0
- data/lib/amqp/client.rb +210 -0
- data/lib/amqp/frame.rb +124 -0
- data/lib/amqp/protocol.rb +212 -0
- data/lib/amqp/server.rb +99 -0
- data/lib/amqp/spec.rb +832 -0
- data/lib/ext/blankslate.rb +7 -0
- data/lib/ext/em.rb +51 -0
- data/lib/ext/emfork.rb +69 -0
- data/lib/mq.rb +823 -0
- data/lib/mq/exchange.rb +302 -0
- data/lib/mq/header.rb +33 -0
- data/lib/mq/logger.rb +89 -0
- data/lib/mq/queue.rb +433 -0
- data/lib/mq/rpc.rb +100 -0
- data/old/README +30 -0
- data/old/Rakefile +12 -0
- data/old/amqp-0.8.json +606 -0
- data/old/amqp_spec.rb +796 -0
- data/old/amqpc.rb +695 -0
- data/old/codegen.rb +148 -0
- data/protocol/amqp-0.8.json +617 -0
- data/protocol/amqp-0.8.xml +3908 -0
- data/protocol/codegen.rb +173 -0
- data/protocol/doc.txt +281 -0
- data/research/api.rb +88 -0
- data/research/primes-forked.rb +63 -0
- data/research/primes-processes.rb +135 -0
- data/research/primes-threaded.rb +49 -0
- metadata +121 -0
data/lib/amqp/client.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'amqp/frame'
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
module BasicClient
|
7
|
+
def process_frame frame
|
8
|
+
if mq = channels[frame.channel]
|
9
|
+
mq.process_frame(frame)
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
case frame
|
14
|
+
when Frame::Method
|
15
|
+
case method = frame.payload
|
16
|
+
when Protocol::Connection::Start
|
17
|
+
send Protocol::Connection::StartOk.new({:platform => 'Ruby/EventMachine',
|
18
|
+
:product => 'AMQP',
|
19
|
+
:information => 'http://github.com/tmm1/amqp',
|
20
|
+
:version => VERSION},
|
21
|
+
'AMQPLAIN',
|
22
|
+
{:LOGIN => @settings[:user],
|
23
|
+
:PASSWORD => @settings[:pass]},
|
24
|
+
'en_US')
|
25
|
+
|
26
|
+
when Protocol::Connection::Tune
|
27
|
+
send Protocol::Connection::TuneOk.new(:channel_max => 0,
|
28
|
+
:frame_max => 131072,
|
29
|
+
:heartbeat => 0)
|
30
|
+
|
31
|
+
send Protocol::Connection::Open.new(:virtual_host => @settings[:vhost],
|
32
|
+
:capabilities => '',
|
33
|
+
:insist => @settings[:insist])
|
34
|
+
|
35
|
+
when Protocol::Connection::OpenOk
|
36
|
+
succeed(self)
|
37
|
+
|
38
|
+
when Protocol::Connection::Close
|
39
|
+
# raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
|
40
|
+
STDERR.puts "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
|
41
|
+
|
42
|
+
when Protocol::Connection::CloseOk
|
43
|
+
@on_disconnect.call if @on_disconnect
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.client
|
50
|
+
@client ||= BasicClient
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.client= mod
|
54
|
+
mod.__send__ :include, AMQP
|
55
|
+
@client = mod
|
56
|
+
end
|
57
|
+
|
58
|
+
module Client
|
59
|
+
include EM::Deferrable
|
60
|
+
|
61
|
+
def initialize opts = {}
|
62
|
+
@settings = opts
|
63
|
+
extend AMQP.client
|
64
|
+
|
65
|
+
@on_disconnect ||= proc{ raise Error, "Could not connect to server #{opts[:host]}:#{opts[:port]}" }
|
66
|
+
|
67
|
+
timeout @settings[:timeout] if @settings[:timeout]
|
68
|
+
errback{ @on_disconnect.call } unless @reconnecting
|
69
|
+
|
70
|
+
@connected = false
|
71
|
+
end
|
72
|
+
|
73
|
+
def connection_completed
|
74
|
+
start_tls if @settings[:ssl]
|
75
|
+
log 'connected'
|
76
|
+
# @on_disconnect = proc{ raise Error, 'Disconnected from server' }
|
77
|
+
unless @closing
|
78
|
+
@on_disconnect = method(:disconnected)
|
79
|
+
@reconnecting = false
|
80
|
+
end
|
81
|
+
|
82
|
+
@connected = true
|
83
|
+
@connection_status.call(:connected) if @connection_status
|
84
|
+
|
85
|
+
@buf = Buffer.new
|
86
|
+
send_data HEADER
|
87
|
+
send_data [1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4')
|
88
|
+
end
|
89
|
+
|
90
|
+
def connected?
|
91
|
+
@connected
|
92
|
+
end
|
93
|
+
|
94
|
+
def unbind
|
95
|
+
log 'disconnected'
|
96
|
+
@connected = false
|
97
|
+
EM.next_tick{ @on_disconnect.call }
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_channel mq
|
101
|
+
(@_channel_mutex ||= Mutex.new).synchronize do
|
102
|
+
channels[ key = (channels.keys.max || 0) + 1 ] = mq
|
103
|
+
key
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def channels
|
108
|
+
@channels ||= {}
|
109
|
+
end
|
110
|
+
|
111
|
+
def receive_data data
|
112
|
+
# log 'receive_data', data
|
113
|
+
@buf << data
|
114
|
+
|
115
|
+
while frame = Frame.parse(@buf)
|
116
|
+
log 'receive', frame
|
117
|
+
process_frame frame
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def process_frame frame
|
122
|
+
# this is a stub meant to be
|
123
|
+
# replaced by the module passed into initialize
|
124
|
+
end
|
125
|
+
|
126
|
+
def send data, opts = {}
|
127
|
+
channel = opts[:channel] ||= 0
|
128
|
+
data = data.to_frame(channel) unless data.is_a? Frame
|
129
|
+
data.channel = channel
|
130
|
+
|
131
|
+
log 'send', data
|
132
|
+
send_data data.to_s
|
133
|
+
end
|
134
|
+
|
135
|
+
#:stopdoc:
|
136
|
+
# def send_data data
|
137
|
+
# log 'send_data', data
|
138
|
+
# super
|
139
|
+
# end
|
140
|
+
#:startdoc:
|
141
|
+
|
142
|
+
def close &on_disconnect
|
143
|
+
if on_disconnect
|
144
|
+
@closing = true
|
145
|
+
@on_disconnect = proc{
|
146
|
+
on_disconnect.call
|
147
|
+
@closing = false
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
callback{ |c|
|
152
|
+
if c.channels.any?
|
153
|
+
c.channels.each do |ch, mq|
|
154
|
+
mq.close
|
155
|
+
end
|
156
|
+
else
|
157
|
+
send Protocol::Connection::Close.new(:reply_code => 200,
|
158
|
+
:reply_text => 'Goodbye',
|
159
|
+
:class_id => 0,
|
160
|
+
:method_id => 0)
|
161
|
+
end
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
def reconnect force = false
|
166
|
+
if @reconnecting and not force
|
167
|
+
# wait 1 second after first reconnect attempt, in between each subsequent attempt
|
168
|
+
EM.add_timer(1){ reconnect(true) }
|
169
|
+
return
|
170
|
+
end
|
171
|
+
|
172
|
+
unless @reconnecting
|
173
|
+
@reconnecting = true
|
174
|
+
|
175
|
+
@deferred_status = nil
|
176
|
+
initialize(@settings)
|
177
|
+
|
178
|
+
mqs = @channels
|
179
|
+
@channels = {}
|
180
|
+
mqs.each{ |_,mq| mq.reset } if mqs
|
181
|
+
end
|
182
|
+
|
183
|
+
log 'reconnecting'
|
184
|
+
EM.reconnect @settings[:host], @settings[:port], self
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.connect opts = {}
|
188
|
+
opts = AMQP.settings.merge(opts)
|
189
|
+
EM.connect opts[:host], opts[:port], self, opts
|
190
|
+
end
|
191
|
+
|
192
|
+
def connection_status &blk
|
193
|
+
@connection_status = blk
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def disconnected
|
199
|
+
@connection_status.call(:disconnected) if @connection_status
|
200
|
+
reconnect
|
201
|
+
end
|
202
|
+
|
203
|
+
def log *args
|
204
|
+
return unless @settings[:logging] or AMQP.logging
|
205
|
+
require 'pp'
|
206
|
+
pp args
|
207
|
+
puts
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
data/lib/amqp/frame.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'amqp/spec'
|
2
|
+
require 'amqp/buffer'
|
3
|
+
require 'amqp/protocol'
|
4
|
+
|
5
|
+
module AMQP
|
6
|
+
class Frame #:nodoc: all
|
7
|
+
def initialize payload = nil, channel = 0
|
8
|
+
@channel, @payload = channel, payload
|
9
|
+
end
|
10
|
+
attr_accessor :channel, :payload
|
11
|
+
|
12
|
+
def id
|
13
|
+
self.class::ID
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_binary
|
17
|
+
buf = Buffer.new
|
18
|
+
buf.write :octet, id
|
19
|
+
buf.write :short, channel
|
20
|
+
buf.write :longstr, payload
|
21
|
+
buf.write :octet, FOOTER
|
22
|
+
buf.rewind
|
23
|
+
buf
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
to_binary.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def == frame
|
31
|
+
[ :id, :channel, :payload ].inject(true) do |eql, field|
|
32
|
+
eql and __send__(field) == frame.__send__(field)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Invalid < StandardError; end
|
37
|
+
|
38
|
+
class Method
|
39
|
+
def initialize payload = nil, channel = 0
|
40
|
+
super
|
41
|
+
unless @payload.is_a? Protocol::Class::Method or @payload.nil?
|
42
|
+
@payload = Protocol.parse(@payload)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Header
|
48
|
+
def initialize payload = nil, channel = 0
|
49
|
+
super
|
50
|
+
unless @payload.is_a? Protocol::Header or @payload.nil?
|
51
|
+
@payload = Protocol::Header.new(@payload)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Body; end
|
57
|
+
|
58
|
+
def self.parse buf
|
59
|
+
buf = Buffer.new(buf) unless buf.is_a? Buffer
|
60
|
+
buf.extract do
|
61
|
+
id, channel, payload, footer = buf.read(:octet, :short, :longstr, :octet)
|
62
|
+
Frame.types[id].new(payload, channel) if footer == FOOTER
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
if $0 =~ /bacon/ or $0 == __FILE__
|
69
|
+
require 'bacon'
|
70
|
+
include AMQP
|
71
|
+
|
72
|
+
describe Frame do
|
73
|
+
should 'handle basic frame types' do
|
74
|
+
Frame::Method.new.id.should == 1
|
75
|
+
Frame::Header.new.id.should == 2
|
76
|
+
Frame::Body.new.id.should == 3
|
77
|
+
end
|
78
|
+
|
79
|
+
should 'convert method frames to binary' do
|
80
|
+
meth = Protocol::Connection::Secure.new :challenge => 'secret'
|
81
|
+
|
82
|
+
frame = Frame::Method.new(meth)
|
83
|
+
frame.to_binary.should.be.kind_of? Buffer
|
84
|
+
frame.to_s.should == [ 1, 0, meth.to_s.length, meth.to_s, 206 ].pack('CnNa*C')
|
85
|
+
end
|
86
|
+
|
87
|
+
should 'convert binary to method frames' do
|
88
|
+
orig = Frame::Method.new Protocol::Connection::Secure.new(:challenge => 'secret')
|
89
|
+
|
90
|
+
copy = Frame.parse(orig.to_binary)
|
91
|
+
copy.should == orig
|
92
|
+
end
|
93
|
+
|
94
|
+
should 'ignore partial frames until ready' do
|
95
|
+
frame = Frame::Method.new Protocol::Connection::Secure.new(:challenge => 'secret')
|
96
|
+
data = frame.to_s
|
97
|
+
|
98
|
+
buf = Buffer.new
|
99
|
+
Frame.parse(buf).should == nil
|
100
|
+
|
101
|
+
buf << data[0..5]
|
102
|
+
Frame.parse(buf).should == nil
|
103
|
+
|
104
|
+
buf << data[6..-1]
|
105
|
+
Frame.parse(buf).should == frame
|
106
|
+
|
107
|
+
Frame.parse(buf).should == nil
|
108
|
+
end
|
109
|
+
|
110
|
+
should 'convert header frames to binary' do
|
111
|
+
head = Protocol::Header.new(Protocol::Basic, :priority => 1)
|
112
|
+
|
113
|
+
frame = Frame::Header.new(head)
|
114
|
+
frame.to_s.should == [ 2, 0, head.to_s.length, head.to_s, 206 ].pack('CnNa*C')
|
115
|
+
end
|
116
|
+
|
117
|
+
should 'convert binary to header frame' do
|
118
|
+
orig = Frame::Header.new Protocol::Header.new(Protocol::Basic, :priority => 1)
|
119
|
+
|
120
|
+
copy = Frame.parse(orig.to_binary)
|
121
|
+
copy.should == orig
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'amqp/spec'
|
2
|
+
require 'amqp/buffer'
|
3
|
+
|
4
|
+
module AMQP
|
5
|
+
module Protocol
|
6
|
+
#:stopdoc:
|
7
|
+
class Class::Method
|
8
|
+
def initialize *args
|
9
|
+
opts = args.pop if args.last.is_a? Hash
|
10
|
+
opts ||= {}
|
11
|
+
|
12
|
+
@debug = 1 # XXX hack, p(obj) == '' if no instance vars are set
|
13
|
+
|
14
|
+
if args.size == 1 and args.first.is_a? Buffer
|
15
|
+
buf = args.shift
|
16
|
+
else
|
17
|
+
buf = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
self.class.arguments.each do |type, name|
|
21
|
+
val = buf ? buf.read(type) :
|
22
|
+
args.shift || opts[name] || opts[name.to_s]
|
23
|
+
instance_variable_set("@#{name}", val)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def arguments
|
28
|
+
self.class.arguments.inject({}) do |hash, (type, name)|
|
29
|
+
hash.update name => instance_variable_get("@#{name}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_binary
|
34
|
+
buf = Buffer.new
|
35
|
+
buf.write :short, self.class.amqp_parent.amqp_id
|
36
|
+
buf.write :short, self.class.amqp_id
|
37
|
+
|
38
|
+
bits = []
|
39
|
+
|
40
|
+
self.class.arguments.each do |type, name|
|
41
|
+
val = instance_variable_get("@#{name}")
|
42
|
+
if type == :bit
|
43
|
+
bits << (val || false)
|
44
|
+
else
|
45
|
+
unless bits.empty?
|
46
|
+
buf.write :bit, bits
|
47
|
+
bits = []
|
48
|
+
end
|
49
|
+
buf.write type, val
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
buf.write :bit, bits unless bits.empty?
|
54
|
+
buf.rewind
|
55
|
+
|
56
|
+
buf
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
to_binary.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_frame channel = 0
|
64
|
+
Frame::Method.new(self, channel)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#:startdoc:
|
69
|
+
#
|
70
|
+
# Contains a properties hash that holds some potentially interesting
|
71
|
+
# information.
|
72
|
+
# * :delivery_mode
|
73
|
+
# 1 equals transient.
|
74
|
+
# 2 equals persistent. Unconsumed persistent messages will survive
|
75
|
+
# a server restart when they are stored in a durable queue.
|
76
|
+
# * :redelivered
|
77
|
+
# True or False
|
78
|
+
# * :routing_key
|
79
|
+
# The routing string used for matching this message to this queue.
|
80
|
+
# * :priority
|
81
|
+
# An integer in the range of 0 to 9 inclusive.
|
82
|
+
# * :content_type
|
83
|
+
# Always "application/octet-stream" (byte stream)
|
84
|
+
# * :exchange
|
85
|
+
# The source exchange which published this message.
|
86
|
+
# * :message_count
|
87
|
+
# The number of unconsumed messages contained in the queue.
|
88
|
+
# * :delivery_tag
|
89
|
+
# A monotonically increasing integer. This number should not be trusted
|
90
|
+
# as a sequence number. There is no guarantee it won't get reset.
|
91
|
+
class Header
|
92
|
+
def initialize *args
|
93
|
+
opts = args.pop if args.last.is_a? Hash
|
94
|
+
opts ||= {}
|
95
|
+
|
96
|
+
first = args.shift
|
97
|
+
|
98
|
+
if first.is_a? ::Class and first.ancestors.include? Protocol::Class
|
99
|
+
@klass = first
|
100
|
+
@size = args.shift || 0
|
101
|
+
@weight = args.shift || 0
|
102
|
+
@properties = opts
|
103
|
+
|
104
|
+
elsif first.is_a? Buffer or first.is_a? String
|
105
|
+
buf = first
|
106
|
+
buf = Buffer.new(buf) unless buf.is_a? Buffer
|
107
|
+
|
108
|
+
@klass = Protocol.classes[buf.read(:short)]
|
109
|
+
@weight = buf.read(:short)
|
110
|
+
@size = buf.read(:longlong)
|
111
|
+
|
112
|
+
props = buf.read(:properties, *klass.properties.map{|type,_| type })
|
113
|
+
@properties = Hash[*klass.properties.map{|_,name| name }.zip(props).reject{|k,v| v.nil? }.flatten]
|
114
|
+
|
115
|
+
else
|
116
|
+
raise ArgumentError, 'Invalid argument'
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
attr_accessor :klass, :size, :weight, :properties
|
121
|
+
|
122
|
+
def to_binary
|
123
|
+
buf = Buffer.new
|
124
|
+
buf.write :short, klass.amqp_id
|
125
|
+
buf.write :short, weight # XXX rabbitmq only supports weight == 0
|
126
|
+
buf.write :longlong, size
|
127
|
+
buf.write :properties, (klass.properties.map do |type, name|
|
128
|
+
[ type, properties[name] || properties[name.to_s] ]
|
129
|
+
end)
|
130
|
+
buf.rewind
|
131
|
+
buf
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_s
|
135
|
+
to_binary.to_s
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_frame channel = 0
|
139
|
+
Frame::Header.new(self, channel)
|
140
|
+
end
|
141
|
+
|
142
|
+
def == header
|
143
|
+
[ :klass, :size, :weight, :properties ].inject(true) do |eql, field|
|
144
|
+
eql and __send__(field) == header.__send__(field)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def method_missing meth, *args, &blk
|
149
|
+
@properties.has_key?(meth) || @klass.properties.find{|_,name| name == meth } ? @properties[meth] :
|
150
|
+
super
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.parse buf
|
155
|
+
buf = Buffer.new(buf) unless buf.is_a? Buffer
|
156
|
+
class_id, method_id = buf.read(:short, :short)
|
157
|
+
classes[class_id].methods[method_id].new(buf)
|
158
|
+
end
|
159
|
+
#:stopdoc:
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
if $0 =~ /bacon/ or $0 == __FILE__
|
164
|
+
require 'bacon'
|
165
|
+
include AMQP
|
166
|
+
|
167
|
+
describe Protocol do
|
168
|
+
should 'instantiate methods with arguments' do
|
169
|
+
meth = Protocol::Connection::StartOk.new nil, 'PLAIN', nil, 'en_US'
|
170
|
+
meth.locale.should == 'en_US'
|
171
|
+
end
|
172
|
+
|
173
|
+
should 'instantiate methods with named parameters' do
|
174
|
+
meth = Protocol::Connection::StartOk.new :locale => 'en_US',
|
175
|
+
:mechanism => 'PLAIN'
|
176
|
+
meth.locale.should == 'en_US'
|
177
|
+
end
|
178
|
+
|
179
|
+
should 'convert methods to binary' do
|
180
|
+
meth = Protocol::Connection::Secure.new :challenge => 'secret'
|
181
|
+
meth.to_binary.should.be.kind_of? Buffer
|
182
|
+
|
183
|
+
meth.to_s.should == [ 10, 20, 6, 'secret' ].pack('nnNa*')
|
184
|
+
end
|
185
|
+
|
186
|
+
should 'convert binary to method' do
|
187
|
+
orig = Protocol::Connection::Secure.new :challenge => 'secret'
|
188
|
+
copy = Protocol.parse orig.to_binary
|
189
|
+
orig.should == copy
|
190
|
+
end
|
191
|
+
|
192
|
+
should 'convert headers to binary' do
|
193
|
+
head = Protocol::Header.new Protocol::Basic,
|
194
|
+
size = 5,
|
195
|
+
weight = 0,
|
196
|
+
:content_type => 'text/json',
|
197
|
+
:delivery_mode => 1,
|
198
|
+
:priority => 1
|
199
|
+
head.to_s.should == [ 60, weight, 0, size, 0b1001_1000_0000_0000, 9, 'text/json', 1, 1 ].pack('nnNNnCa*CC')
|
200
|
+
end
|
201
|
+
|
202
|
+
should 'convert binary to header' do
|
203
|
+
orig = Protocol::Header.new Protocol::Basic,
|
204
|
+
size = 5,
|
205
|
+
weight = 0,
|
206
|
+
:content_type => 'text/json',
|
207
|
+
:delivery_mode => 1,
|
208
|
+
:priority => 1
|
209
|
+
Protocol::Header.new(orig.to_binary).should == orig
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|