amq-protocol-2.3.0/0000755000004100000410000000000013226473014014163 5ustar www-datawww-dataamq-protocol-2.3.0/Rakefile0000644000004100000410000000215213226473014015630 0ustar www-datawww-datarequire "bundler" Bundler.setup require "rake" require "rspec/core/rake_task" $LOAD_PATH.unshift File.expand_path("../lib", __FILE__) require "amq/protocol/version" task :gem => :build task :build do system "gem build amq-protocol.gemspec" end task :install => :build do system "gem install amq-protocol-#{AMQ::Protocol::VERSION}.gem" end def extension RUBY_PLATFORM =~ /darwin/ ? "bundle" : "so" end def compile! puts "Compiling native extensions..." Dir.chdir(Pathname(__FILE__).dirname + "ext/") do `bundle exec ruby extconf.rb` `make` `cp client.#{extension} ../lib/amq/protocol/native/` end end RSpec::Core::RakeTask.new("spec") do |spec| spec.pattern = "spec/**/*_spec.rb" end RSpec::Core::RakeTask.new("clean_spec") do |spec| spec.pattern = "spec/**/*_spec.rb" end task :compile do compile! end task :clean do puts "Cleaning out native extensions..." begin Dir.chdir(Pathname(__FILE__).dirname + "lib/amq-protocol/native") do `rm client.#{extension}` end rescue Exception => e puts e.message end end task :default => [:compile, :spec, :clean, :clean_spec] amq-protocol-2.3.0/Gemfile0000644000004100000410000000052613226473014015461 0ustar www-datawww-data# encoding: utf-8 source "https://rubygems.org" group :development do # excludes Windows, Rubinius and JRuby gem "ruby-prof", :platforms => [:mri_19, :mri_20, :mri_21] gem "rake" end group :test do gem "rspec", ">= 3.5.0" gem "rspec-its" gem "effin_utf8" gem "simplecov" end group :development, :test do gem "byebug" end amq-protocol-2.3.0/ChangeLog.md0000644000004100000410000000660013226473014016336 0ustar www-datawww-data## Changes between 2.2.0 and 2.3.0 (Jan 8th, 2018) ### Support for Additional URI Query Parameters GitHub issue: [#67](https://github.com/ruby-amqp/amq-protocol/issues/67), [#68](https://github.com/ruby-amqp/amq-protocol/issues/68), [#69](https://github.com/ruby-amqp/amq-protocol/issues/69). Contributed by Andrew Babichev. ## Changes between 2.1.0 and 2.2.0 (May 11th, 2017) ### Timestamps are Encoded as 64-bit Unsigned Integers This is a potentially **breaking change**. It is recommended that all applications that use this gem and pass date/time values in message properties or headers are upgraded at the same time. GitHub issue: [#64](https://github.com/ruby-amqp/amq-protocol/issues/64). Contributed by Carl Hoerberg. ## Changes between 2.0.0 and 2.1.0 (January 28th, 2017) ### Ruby Warnings Squashed Contributed by Akira Matsuda. GitHub issue: [#62](https://github.com/ruby-amqp/amq-protocol/pull/62) ### Byte Array Decoding Byte array values in types now can be decoded (to the extent Ruby type system permits) by this library. GitHub issue: [#58](https://github.com/ruby-amqp/amq-protocol/issues/58) ## Changes between 1.9.x and 2.0.0 2.0.0 has **breaking changes** in header encoding. ### Signed Integer Encoding in Headers Integer values in headers are now encoded as signed 64-bit (was unsigned 32-bit previously, unintentionally). This is a breaking change: consuming messages with integers in headers published with older versions of this library will break! ### Signed 16 Bit Integer Decoding Signed 16 bit integers are now decoded correctly. ### Signed 8 Bit Integer Decoding Signed 8 bit integers are now decoded correctly. Contributed by Benjamin Conlan. ## Changes between 1.8.0 and 1.9.0 ### Performance Improvements in AMQ::BitSet `AMQ::BitSet#next_clear_bit` is now drastically more efficient (down from 6 minutes for 10,000 iterations to 4 seconds for 65,536 iterations). Contributed by Doug Rohrer, Dave Anderson, and Jason Voegele from [Neo](http://www.neo.com). ## Changes between 1.7.0 and 1.8.0 ### Body Framing Fix Messages exactly 128 Kb in size are now framed correctly. Contributed by Nicolas Viennot. ## Changes between 1.6.0 and 1.7.0 ### connection.blocked Support `connection.blocked` AMQP 0.9.1 extension is now supported (should be available as of RabbitMQ 3.2). ## Changes between 1.0.0 and 1.1.0 ### Performance Enhancements Encoding of large payloads is now done more efficiently. Contributed by Greg Brockman. ## Changes between 1.0.0.pre6 and 1.0.0.pre7 ### AMQ::Settings `AMQ::Settings` extracts settings merging logic and AMQP/AMQPS URI parsing from `amq-client`. Parsing follows the same convention amqp gem and RabbitMQ Java client follow. Examples: ``` ruby AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com") # => vhost is nil, so default (/) will be used AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/") # => vhost is an empty string AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/%2Fvault") # => vhost is /vault AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/production") # => vhost is production AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/a.b.c") # => vhost is a.b.c AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/foo/bar") # => ArgumentError ``` ### AMQ::Protocol::TLS_PORT `AMQ::Protocol::TLS_PORT` is a new constant that contains default AMQPS 0.9.1 port, 5671. amq-protocol-2.3.0/.rspec0000644000004100000410000000002613226473014015276 0ustar www-datawww-data--require spec_helper amq-protocol-2.3.0/amq-protocol.gemspec0000755000004100000410000000166513226473014020160 0ustar www-datawww-data#!/usr/bin/env gem build # encoding: utf-8 require "base64" require File.expand_path("../lib/amq/protocol/version", __FILE__) Gem::Specification.new do |s| s.name = "amq-protocol" s.version = AMQ::Protocol::VERSION s.authors = ["Jakub Stastny", "Michael S. Klishin", "Theo Hultberg", "Mark Abramov"] s.homepage = "http://github.com/ruby-amqp/amq-protocol" s.summary = "AMQP 0.9.1 encoding & decoding library." s.description = <<-DESC amq-protocol is an AMQP 0.9.1 serialization library for Ruby. It is not a client: the library only handles serialization and deserialization. DESC s.email = ["michael.s.klishin@gmail.com"] s.licenses = ["MIT"] s.required_ruby_version = Gem::Requirement.new(">= 2.2") # files s.files = `git ls-files`.split("\n").reject { |file| file =~ /^vendor\// } s.require_paths = ["lib"] s.extra_rdoc_files = ["README.md"] + Dir.glob("doc/*") s.rubyforge_project = "amq-protocol" end amq-protocol-2.3.0/spec/0000755000004100000410000000000013226473014015115 5ustar www-datawww-dataamq-protocol-2.3.0/spec/amq/0000755000004100000410000000000013226473014015673 5ustar www-datawww-dataamq-protocol-2.3.0/spec/amq/bit_set_spec.rb0000644000004100000410000001307313226473014020667 0ustar www-datawww-datarequire "amq/bit_set" # extracted from amqp gem. MK. RSpec.describe AMQ::BitSet do # # Environment # let(:nbits) { (1 << 16) - 1 } # # Examples # describe "#new" do it "has no bits set at the start" do bs = AMQ::BitSet.new(128) 0.upto(127) do |i| expect(bs[i]).to be_falsey end end # it end # describe describe "#word_index" do subject do described_class.new(nbits) end it "returns 0 when the word is between 0 and 63" do expect(subject.word_index(0)).to eq(0) expect(subject.word_index(63)).to eq(0) end # it it "returns 1 when the word is between 64 and 127" do expect(subject.word_index(64)).to be(1) expect(subject.word_index(127)).to be(1) end # it it "returns 2 when the word is between 128 and another number" do expect(subject.word_index(128)).to be(2) end # it end # describe describe "#get, #[]" do describe "when bit at given position is set" do subject do o = described_class.new(nbits) o.set(3) o end it "returns true" do expect(subject.get(3)).to be_truthy end # it end # describe describe "when bit at given position is off" do subject do described_class.new(nbits) end it "returns false" do expect(subject.get(5)).to be_falsey end # it end # describe describe "when index out of range" do subject do described_class.new(nbits) end it "should raise IndexError for negative index" do expect { subject.get(-1) }.to raise_error(IndexError) end # it it "should raise IndexError for index >= number of bits" do expect { subject.get(nbits) }.to raise_error(IndexError) end # it end # describe end # describe describe "#set" do describe "when bit at given position is set" do subject do described_class.new(nbits) end it "has no effect" do subject.set(3) expect(subject.get(3)).to be_truthy subject.set(3) expect(subject[3]).to be_truthy end # it end # describe describe "when bit at given position is off" do subject do described_class.new(nbits) end it "sets that bit" do subject.set(3) expect(subject.get(3)).to be_truthy subject.set(33) expect(subject.get(33)).to be_truthy subject.set(3387) expect(subject.get(3387)).to be_truthy end # it end # describe describe "when index out of range" do subject do described_class.new(nbits) end it "should raise IndexError for negative index" do expect { subject.set(-1) }.to raise_error(IndexError) end # it it "should raise IndexError for index >= number of bits" do expect { subject.set(nbits) }.to raise_error(IndexError) end # it end # describe end # describe describe "#unset" do describe "when bit at a given position is set" do subject do described_class.new(nbits) end it "unsets that bit" do subject.set(3) expect(subject.get(3)).to be_truthy subject.unset(3) expect(subject.get(3)).to be_falsey end # it end # describe describe "when bit at a given position is off" do subject do described_class.new(nbits) end it "has no effect" do expect(subject.get(3)).to be_falsey subject.unset(3) expect(subject.get(3)).to be_falsey end # it end # describe describe "when index out of range" do subject do described_class.new(nbits) end it "should raise IndexError for negative index" do expect { subject.unset(-1) }.to raise_error(IndexError) end # it it "should raise IndexError for index >= number of bits" do expect { subject.unset(nbits) }.to raise_error(IndexError) end # it end # describe end # describe describe "#clear" do subject do described_class.new(nbits) end it "clears all bits" do subject.set(3) expect(subject.get(3)).to be_truthy subject.set(7668) expect(subject.get(7668)).to be_truthy subject.clear expect(subject.get(3)).to be_falsey expect(subject.get(7668)).to be_falsey end # it end # describe describe "#number_of_trailing_ones" do it "calculates them" do expect(described_class.number_of_trailing_ones(0)).to eq(0) expect(described_class.number_of_trailing_ones(1)).to eq(1) expect(described_class.number_of_trailing_ones(2)).to eq(0) expect(described_class.number_of_trailing_ones(3)).to eq(2) expect(described_class.number_of_trailing_ones(4)).to eq(0) end # it end # describe describe '#next_clear_bit' do subject do described_class.new(255) end it "returns sequential values when none have been returned" do expect(subject.next_clear_bit).to eq(0) subject.set(0) expect(subject.next_clear_bit).to eq(1) subject.set(1) expect(subject.next_clear_bit).to eq(2) subject.unset(1) expect(subject.next_clear_bit).to eq(1) end # it it "returns the same number as long as nothing is set" do expect(subject.next_clear_bit).to eq(0) expect(subject.next_clear_bit).to eq(0) end # it it "handles more than 128 bits" do 0.upto(254) do |i| subject.set(i) expect(subject.next_clear_bit).to eq(i + 1) end subject.unset(254) expect(subject.get(254)).to be_falsey end # it end # describe end amq-protocol-2.3.0/spec/amq/protocol/0000755000004100000410000000000013226473014017534 5ustar www-datawww-dataamq-protocol-2.3.0/spec/amq/protocol/queue_spec.rb0000644000004100000410000000745213226473014022227 0ustar www-datawww-data# encoding: binary module AMQ module Protocol class Queue RSpec.describe Declare do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 queue = 'hello.world' passive = false durable = true exclusive = true auto_delete = true nowait = false arguments = nil method_frame = Declare.encode(channel, queue, passive, durable, exclusive, auto_delete, nowait, arguments) expect(method_frame.payload).to eq("\x002\x00\n\x00\x00\vhello.world\x0E\x00\x00\x00\x00") expect(method_frame.channel).to eq(1) end end end RSpec.describe DeclareOk do describe '.decode' do subject do DeclareOk.decode(" amq.gen-KduGSqQrpeUo1otnU0TWSA==\x00\x00\x00\x00\x00\x00\x00\x00") end its(:queue) { should eq('amq.gen-KduGSqQrpeUo1otnU0TWSA==') } its(:message_count) { should eq(0) } its(:consumer_count) { should eq(0) } end end RSpec.describe Bind do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 queue = 'hello.world' exchange = 'foo.bar' routing_key = 'xyz' nowait = false arguments = nil method_frame = Bind.encode(channel, queue, exchange, routing_key, nowait, arguments) expect(method_frame.payload).to eq("\x002\x00\x14\x00\x00\vhello.world\afoo.bar\x03xyz\x00\x00\x00\x00\x00") expect(method_frame.channel).to eq(1) end end end # RSpec.describe BindOk do # describe '.decode' do # end # end RSpec.describe Purge do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 queue = 'hello.world' nowait = false method_frame = Purge.encode(channel, queue, nowait) expect(method_frame.payload).to eq("\x002\x00\x1E\x00\x00\vhello.world\x00") expect(method_frame.channel).to eq(1) end end end RSpec.describe PurgeOk do describe '.decode' do subject do PurgeOk.decode("\x00\x00\x00\x02") end its(:message_count) { should eq(2) } end end RSpec.describe Delete do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 queue = 'hello.world' if_unused = false if_empty = false nowait = false method_frame = Delete.encode(channel, queue, if_unused, if_empty, nowait) expect(method_frame.payload).to eq("\x002\x00(\x00\x00\vhello.world\x00") expect(method_frame.channel).to eq(1) end end end RSpec.describe DeleteOk do describe '.decode' do subject do DeleteOk.decode("\x00\x00\x00\x02") end its(:message_count) { should eq(2) } end end RSpec.describe Unbind do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 queue = 'hello.world' exchange = 'foo.bar' routing_key = 'xyz' arguments = nil method_frame = Unbind.encode(channel, queue, exchange, routing_key, arguments) expect(method_frame.payload).to eq("\x002\x002\x00\x00\vhello.world\afoo.bar\x03xyz\x00\x00\x00\x00") expect(method_frame.channel).to eq(1) end end end # RSpec.describe UnbindOk do # describe '.decode' do # end # end end end end amq-protocol-2.3.0/spec/amq/protocol/blank_body_encoding_spec.rb0000644000004100000410000000052613226473014025050 0ustar www-datawww-dataRSpec.describe AMQ::Protocol::Method, ".encode_body" do it "encodes 1-byte long payload as exactly 1 body frame" do expect(described_class.encode_body('1', 1, 65536).size).to eq(1) end it "encodes 0-byte long (blank) payload as exactly 0 body frame" do expect(described_class.encode_body('', 1, 65536).size).to eq(0) end end amq-protocol-2.3.0/spec/amq/protocol/exchange_spec.rb0000644000004100000410000000675713226473014022674 0ustar www-datawww-data# encoding: binary module AMQ module Protocol class Exchange RSpec.describe Declare do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 exchange = 'amqclient.adapters.em.exchange1' type = 'fanout' passive = false durable = false auto_delete = false internal = false nowait = false arguments = nil method_frame = Declare.encode(channel, exchange, type, passive, durable, auto_delete, internal, nowait, arguments) expect(method_frame.payload).to eq("\x00(\x00\n\x00\x00\x1Famqclient.adapters.em.exchange1\x06fanout\x00\x00\x00\x00\x00") expect(method_frame.channel).to eq(1) end end end RSpec.describe Declare, "encoded with a symbol name" do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 exchange = :exchange2 type = 'fanout' passive = false durable = false auto_delete = false internal = false nowait = false arguments = nil method_frame = Declare.encode(channel, exchange, type, passive, durable, auto_delete, internal, nowait, arguments) expect(method_frame.payload).to eq("\x00(\x00\n\x00\x00\texchange2\x06fanout\x00\x00\x00\x00\x00") expect(method_frame.channel).to eq(1) end end end RSpec.describe Delete do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 exchange = 'amqclient.adapters.em.exchange' if_unused = false nowait = false method_frame = Delete.encode(channel, exchange, if_unused, nowait) expect(method_frame.payload).to eq("\x00(\x00\x14\x00\x00\x1Eamqclient.adapters.em.exchange\x00") expect(method_frame.channel).to eq(1) end end end # RSpec.describe DeleteOk do # describe '.decode' do # end # end RSpec.describe Bind do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 destination = 'foo' source = 'bar' routing_key = 'xyz' nowait = false arguments = nil method_frame = Bind.encode(channel, destination, source, routing_key, nowait, arguments) expect(method_frame.payload).to eq("\x00(\x00\x1E\x00\x00\x03foo\x03bar\x03xyz\x00\x00\x00\x00\x00") expect(method_frame.channel).to eq(1) end end end # RSpec.describe BindOk do # describe '.decode' do # end # end RSpec.describe Unbind do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 destination = 'foo' source = 'bar' routing_key = 'xyz' nowait = false arguments = nil method_frame = Unbind.encode(channel, destination, source, routing_key, nowait, arguments) expect(method_frame.payload).to eq("\x00(\x00(\x00\x00\x03foo\x03bar\x03xyz\x00\x00\x00\x00\x00") expect(method_frame.channel).to eq(1) end end end # RSpec.describe UnbindOk do # describe '.decode' do # end # end end end end amq-protocol-2.3.0/spec/amq/protocol/basic_spec.rb0000644000004100000410000002611613226473014022162 0ustar www-datawww-data# encoding: binary module AMQ module Protocol RSpec.describe AMQ::Protocol::Basic do describe '.encode_timestamp' do it 'encodes the timestamp as a 64 byte big endian integer' do expect(Basic.encode_timestamp(12345).last).to eq("\x00\x00\x00\x00\x00\x0009") end end # describe '.decode_timestamp' do # it 'decodes the timestamp from a 64 byte big endian integer and returns a Time object' do # expect(Basic.decode_timestamp("\x00\x00\x00\x00\x00\x0009")).to eq(Time.at(12345)) # end # end describe '.encode_headers' do it 'encodes the headers as a table' do expect(Basic.encode_headers(:hello => 'world').last).to eq("\x00\x00\x00\x10\x05helloS\x00\x00\x00\x05world") end end # describe '.decode_headers' do # it 'decodes the headers from a table' do # expect(Basic.decode_headers("\x00\x00\x00\x10\x05helloS\x00\x00\x00\x05world")).to eq({'hello' => 'world'}) # end # end describe '.encode_properties' do it 'packs the parameters into a byte array using the other encode_* methods' do result = Basic.encode_properties(10, {:priority => 0, :delivery_mode => 2, :content_type => 'application/octet-stream'}) expect(result).to eq("\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x98\x00\x18application/octet-stream\x02\x00") end end describe '.decode_properties' do it 'unpacks the properties from a byte array using the decode_* methods' do result = Basic.decode_properties("\x98@\x18application/octet-stream\x02\x00\x00\x00\x00\x00\x00\x00\x00{") expect(result).to eq({:priority => 0, :delivery_mode => 2, :content_type => 'application/octet-stream', :timestamp => Time.at(123)}) end it 'unpacks the properties from a byte array using the decode_* methods, including a table' do result = Basic.decode_properties("\xB8@\x18application/octet-stream\x00\x00\x00\x10\x05helloS\x00\x00\x00\x05world\x02\x00\x00\x00\x00\x00\x00\x00\x00{") expect(result).to eq({:priority => 0, :delivery_mode => 2, :content_type => 'application/octet-stream', :timestamp => Time.at(123), :headers => {'hello' => 'world'}}) end end %w(content_type content_encoding correlation_id reply_to expiration message_id type user_id app_id cluster_id).each do |method| describe ".encode_#{method}" do it 'encodes the parameter as a Pascal string' do expect(Basic.send("encode_#{method}", 'hello world').last).to eq("\x0bhello world") end end # describe ".decode_#{method}" do # it 'returns the string' do # # the length has been stripped in .decode_properties # expect(Basic.send("decode_#{method}", 'hello world')).to eq('hello world') # end # end end %w(delivery_mode priority).each do |method| describe ".encode_#{method}" do it 'encodes the parameter as a char' do expect(Basic.send("encode_#{method}", 10).last).to eq("\x0a") end end # describe ".decode_#{method}" do # it 'decodes the value from a char' do # expect(Basic.send("decode_#{method}", "\x10")).to eq(16) # end # end end end class Basic RSpec.describe Qos do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 prefetch_size = 3 prefetch_count = 5 global = false method_frame = Qos.encode(channel, prefetch_size, prefetch_count, global) expect(method_frame.payload).to eq("\x00<\x00\n\x00\x00\x00\x03\x00\x05\x00") expect(method_frame.channel).to eq(1) end end end # RSpec.describe QosOk do # describe '.decode' do # end # end RSpec.describe Consume do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 queue = 'foo' consumer_tag = 'me' no_local = false no_ack = false exclusive = true nowait = false arguments = nil method_frame = Consume.encode(channel, queue, consumer_tag, no_local, no_ack, exclusive, nowait, arguments) expect(method_frame.payload).to eq("\x00<\x00\x14\x00\x00\x03foo\x02me\x04\x00\x00\x00\x00") expect(method_frame.channel).to eq(1) end end end RSpec.describe ConsumeOk do describe '.decode' do subject do ConsumeOk.decode("\x03foo") end its(:consumer_tag) { should eq('foo') } end end RSpec.describe Cancel do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 consumer_tag = 'foo' nowait = true method_frame = Cancel.encode(channel, consumer_tag, nowait) expect(method_frame.payload).to eq("\x00<\x00\x1E\x03foo\x01") expect(method_frame.channel).to eq(1) end end describe '.decode' do subject do CancelOk.decode("\x03foo\x01") end its(:consumer_tag) { should eq('foo') } end end RSpec.describe CancelOk do describe '.decode' do subject do CancelOk.decode("\x03foo") end its(:consumer_tag) { should eq('foo') } end end RSpec.describe Publish do describe '.encode' do it 'encodes the parameters into a list of MethodFrames' do channel = 1 payload = 'Hello World!' user_headers = {:priority => 0, :content_type => 'application/octet-stream'} exchange = 'foo' routing_key = 'xyz' mandatory = false immediate = true frame_size = 512 method_frames = Publish.encode(channel, payload, user_headers, exchange, routing_key, mandatory, immediate, frame_size) expect(method_frames[0].payload).to eq("\x00<\x00(\x00\x00\x03foo\x03xyz\x02") expect(method_frames[1].payload).to eq("\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\f\x88\x00\x18application/octet-stream\x00") expect(method_frames[2].payload).to eq("Hello World!") expect(method_frames[0].channel).to eq(1) expect(method_frames[1].channel).to eq(1) expect(method_frames[2].channel).to eq(1) expect(method_frames.size).to eq(3) end end end RSpec.describe Return do describe '.decode' do subject do Return.decode("\x019\fNO_CONSUMERS\namq.fanout\x00") end its(:reply_code) { should eq(313) } its(:reply_text) { should eq('NO_CONSUMERS') } its(:exchange) { should eq('amq.fanout') } its(:routing_key) { should eq('') } end end RSpec.describe Deliver do describe '.decode' do subject do Deliver.decode("\e-1300560114000-445586772970\x00\x00\x00\x00\x00\x00\x00c\x00\namq.fanout\x00") end its(:consumer_tag) { should eq('-1300560114000-445586772970') } its(:delivery_tag) { should eq(99) } its(:redelivered) { should eq(false) } its(:exchange) { should eq('amq.fanout') } its(:routing_key) { should eq('') } end end RSpec.describe Get do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 queue = 'foo' no_ack = true method_frame = Get.encode(channel, queue, no_ack) expect(method_frame.payload).to eq("\x00\x3c\x00\x46\x00\x00\x03foo\x01") expect(method_frame.channel).to eq(1) end end end RSpec.describe GetOk do describe '.decode' do subject do GetOk.decode("\x00\x00\x00\x00\x00\x00\x00\x06\x00\namq.fanout\x00\x00\x00\x00^") end its(:delivery_tag) { should eq(6) } its(:redelivered) { should eq(false) } its(:exchange) { should eq('amq.fanout') } its(:routing_key) { should eq('') } its(:message_count) { should eq(94) } end end RSpec.describe GetEmpty do describe '.decode' do subject do GetEmpty.decode("\x03foo") end its(:cluster_id) { should eq('foo') } end end RSpec.describe Ack do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 delivery_tag = 6 multiple = false method_frame = Ack.encode(channel, delivery_tag, multiple) expect(method_frame.payload).to eq("\x00<\x00P\x00\x00\x00\x00\x00\x00\x00\x06\x00") expect(method_frame.channel).to eq(1) end end end RSpec.describe Reject do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 delivery_tag = 8 requeue = true method_frame = Reject.encode(channel, delivery_tag, requeue) expect(method_frame.payload).to eq("\x00<\x00Z\x00\x00\x00\x00\x00\x00\x00\x08\x01") expect(method_frame.channel).to eq(1) end end end RSpec.describe RecoverAsync do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 requeue = true method_frame = RecoverAsync.encode(channel, requeue) expect(method_frame.payload).to eq("\x00<\x00d\x01") expect(method_frame.channel).to eq(1) end end end RSpec.describe Recover do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 requeue = true method_frame = Recover.encode(channel, requeue) expect(method_frame.payload).to eq("\x00<\x00n\x01") expect(method_frame.channel).to eq(1) end end end # RSpec.describe RecoverOk do # describe '.decode' do # end # end RSpec.describe Nack do describe '.decode' do subject do Nack.decode("\x00\x00\x00\x00\x00\x00\x00\x09\x03") end its(:delivery_tag) { should eq(9) } its(:multiple) { should eq(true) } its(:requeue) { should eq(true) } end describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 delivery_tag = 10 multiple = false requeue = true method_frame = Nack.encode(channel, delivery_tag, multiple, requeue) expect(method_frame.payload).to eq("\x00<\x00x\x00\x00\x00\x00\x00\x00\x00\x0a\x02") expect(method_frame.channel).to eq(1) end end end end end end amq-protocol-2.3.0/spec/amq/protocol/connection_spec.rb0000644000004100000410000001252613226473014023240 0ustar www-datawww-data# encoding: binary module AMQ module Protocol class Connection RSpec.describe Start do describe '.decode' do subject do Start.decode("\x00\t\x00\x00\x00\xFB\tcopyrightS\x00\x00\x00gCopyright (C) 2007-2010 LShift Ltd., Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd.\vinformationS\x00\x00\x005Licensed under the MPL. See http://www.rabbitmq.com/\bplatformS\x00\x00\x00\nErlang/OTP\aproductS\x00\x00\x00\bRabbitMQ\aversionS\x00\x00\x00\x052.2.0\x00\x00\x00\x0EPLAIN AMQPLAIN\x00\x00\x00\x05en_US") end its(:version_major) { should == 0 } its(:version_minor) { should == 9 } its(:server_properties) { should == {'copyright' => 'Copyright (C) 2007-2010 LShift Ltd., Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd.', 'information' => 'Licensed under the MPL. See http://www.rabbitmq.com/', 'platform' => 'Erlang/OTP', 'product' => 'RabbitMQ', 'version' => '2.2.0'} } its(:mechanisms) { should == 'PLAIN AMQPLAIN' } its(:locales) { should == 'en_US' } end end RSpec.describe StartOk do describe '.encode' do it 'encodes the parameters into a MethodFrame' do client_properties = {:platform => 'Ruby 1.9.2', :product => 'AMQ Client', :information => 'http://github.com/ruby-amqp/amq-client', :version => '0.2.0'} mechanism = 'PLAIN' response = "\x00guest\x00guest" locale = 'en_GB' method_frame = StartOk.encode(client_properties, mechanism, response, locale) # the order of the table parts isn't deterministic in Ruby 1.8 expect(method_frame.payload[0, 8]).to eq("\x00\n\x00\v\x00\x00\x00x") expect(method_frame.payload).to include("\bplatformS\x00\x00\x00\nRuby 1.9.2") expect(method_frame.payload).to include("\aproductS\x00\x00\x00\nAMQ Client") expect(method_frame.payload).to include("\vinformationS\x00\x00\x00&http://github.com/ruby-amqp/amq-client") expect(method_frame.payload).to include("\aversionS\x00\x00\x00\x050.2.0") expect(method_frame.payload[-28, 28]).to eq("\x05PLAIN\x00\x00\x00\f\x00guest\x00guest\x05en_GB") expect(method_frame.payload.length).to eq(156) end end end RSpec.describe Secure do describe '.decode' do subject do Secure.decode("\x00\x00\x00\x03foo") end its(:challenge) { should eq('foo') } end end RSpec.describe SecureOk do describe '.encode' do it 'encodes the parameters as a MethodFrame' do response = 'bar' method_frame = SecureOk.encode(response) expect(method_frame.payload).to eq("\x00\x0a\x00\x15\x00\x00\x00\x03bar") end end end RSpec.describe Tune do describe '.decode' do subject do Tune.decode("\x00\x00\x00\x02\x00\x00\x00\x00") end its(:channel_max) { should eq(0) } its(:frame_max) { should eq(131072) } its(:heartbeat) { should eq(0) } end end RSpec.describe TuneOk do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel_max = 0 frame_max = 65536 heartbeat = 1 method_frame = TuneOk.encode(channel_max, frame_max, heartbeat) expect(method_frame.payload).to eq("\x00\n\x00\x1F\x00\x00\x00\x01\x00\x00\x00\x01") end end end RSpec.describe Open do describe '.encode' do it 'encodes the parameters into a MethodFrame' do vhost = '/test' method_frame = Open.encode(vhost) expect(method_frame.payload).to eq("\x00\n\x00(\x05/test\x00\x00") end end end RSpec.describe OpenOk do describe '.decode' do subject do OpenOk.decode("\x00") end its(:known_hosts) { should eq('') } end end RSpec.describe Close do describe '.decode' do context 'with code 200' do subject do Close.decode("\x00\xc8\x07KTHXBAI\x00\x05\x00\x06") end its(:reply_code) { should eq(200) } its(:reply_text) { should eq('KTHXBAI') } its(:class_id) { should eq(5) } its(:method_id) { should eq(6) } end context 'with an error code' do it 'returns method frame and lets calling code handle the issue' do Close.decode("\x01\x38\x08NO_ROUTE\x00\x00") end end end describe '.encode' do it 'encodes the parameters into a MethodFrame' do reply_code = 540 reply_text = 'NOT_IMPLEMENTED' class_id = 0 method_id = 0 method_frame = Close.encode(reply_code, reply_text, class_id, method_id) expect(method_frame.payload).to eq("\x00\x0a\x002\x02\x1c\x0fNOT_IMPLEMENTED\x00\x00\x00\x00") end end end RSpec.describe CloseOk do describe '.encode' do it 'encodes a MethodFrame' do method_frame = CloseOk.encode expect(method_frame.payload).to eq("\x00\n\x003") end end end end end end amq-protocol-2.3.0/spec/amq/protocol/channel_spec.rb0000644000004100000410000000753113226473014022511 0ustar www-datawww-data# encoding: binary module AMQ module Protocol class Channel RSpec.describe Open do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 out_of_band = '' method_frame = Open.encode(channel, out_of_band) expect(method_frame.payload).to eq("\x00\x14\x00\n\x00") expect(method_frame.channel).to eq(1) end end end RSpec.describe OpenOk do describe '.decode' do subject do OpenOk.decode("\x00\x00\x00\x03foo") end its(:channel_id) { should eq('foo') } end end RSpec.describe Flow do describe '.decode' do subject do Flow.decode("\x01") end its(:active) { should be_truthy } end describe '.encode' do it 'encodes the parameters as a MethodFrame' do channel = 1 active = true method_frame = Flow.encode(channel, active) expect(method_frame.payload).to eq("\x00\x14\x00\x14\x01") expect(method_frame.channel).to eq(1) end end end RSpec.describe FlowOk do describe '.decode' do subject do FlowOk.decode("\x00") end its(:active) { should be_falsey } end describe '.encode' do it 'encodes the parameters as a MethodFrame' do channel = 1 active = true method_frame = FlowOk.encode(channel, active) expect(method_frame.payload).to eq("\x00\x14\x00\x15\x01") expect(method_frame.channel).to eq(1) end end end RSpec.describe Close do describe '.decode' do context 'with code 200' do subject do Close.decode("\x00\xc8\x07KTHXBAI\x00\x05\x00\x06") end its(:reply_code) { should eq(200) } its(:reply_text) { should eq('KTHXBAI') } its(:class_id) { should eq(5) } its(:method_id) { should eq(6) } end context 'with code 404 and reply_text length > 127 characters' do subject do raw = "\x01\x94\x80NOT_FOUND - no binding 123456789012345678901234567890123 between exchange 'amq.topic' in vhost '/' and queue 'test' in vhost '/'\x002\x002" Close.decode(raw) end its(:reply_code) { should eq(404) } its(:reply_text) { should eq(%q{NOT_FOUND - no binding 123456789012345678901234567890123 between exchange 'amq.topic' in vhost '/' and queue 'test' in vhost '/'}) } its(:class_id) { should eq(50) } its(:method_id) { should eq(50) } end context 'with an error code' do it 'returns frame and lets calling code handle the issue' do Close.decode("\x01\x38\x08NO_ROUTE\x00\x00") end end end describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 reply_code = 540 reply_text = 'NOT_IMPLEMENTED' class_id = 0 method_id = 0 method_frame = Close.encode(channel, reply_code, reply_text, class_id, method_id) expect(method_frame.payload).to eq("\x00\x14\x00(\x02\x1c\x0fNOT_IMPLEMENTED\x00\x00\x00\x00") expect(method_frame.channel).to eq(1) end end end RSpec.describe CloseOk do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 method_frame = CloseOk.encode(channel) expect(method_frame.payload).to eq("\x00\x14\x00\x29") expect(method_frame.channel).to eq(channel) end end end end end end amq-protocol-2.3.0/spec/amq/protocol/constants_spec.rb0000644000004100000410000000042713226473014023112 0ustar www-datawww-dataRSpec.describe "(Some) AMQ::Protocol constants" do it "include regular port" do expect(AMQ::Protocol::DEFAULT_PORT).to eq(5672) end it "provides TLS/SSL port" do expect(AMQ::Protocol::TLS_PORT).to eq(5671) expect(AMQ::Protocol::SSL_PORT).to eq(5671) end end amq-protocol-2.3.0/spec/amq/protocol/value_encoder_spec.rb0000644000004100000410000001147613226473014023717 0ustar www-datawww-datarequire 'time' require "amq/protocol/table_value_encoder" require "amq/protocol/float_32bit" module AMQ module Protocol RSpec.describe TableValueEncoder do it "calculates size of string field values" do expect(described_class.field_value_size("amqp")).to eq(9) expect(described_class.encode("amqp").bytesize).to eq(9) expect(described_class.field_value_size("amq-protocol")).to eq(17) expect(described_class.encode("amq-protocol").bytesize).to eq(17) expect(described_class.field_value_size("à bientôt")).to eq(16) expect(described_class.encode("à bientôt").bytesize).to eq(16) end it "calculates size of integer field values" do expect(described_class.field_value_size(10)).to eq(9) expect(described_class.encode(10).bytesize).to eq(9) end it "calculates size of float field values (considering them to be 64-bit)" do expect(described_class.field_value_size(10.0)).to eq(9) expect(described_class.encode(10.0).bytesize).to eq(9) expect(described_class.field_value_size(120000.0)).to eq(9) expect(described_class.encode(120000.0).bytesize).to eq(9) end it "calculates size of float field values (boxed as 32-bit)" do expect(described_class.encode(AMQ::Protocol::Float32Bit.new(10.0)).bytesize).to eq(5) expect(described_class.encode(AMQ::Protocol::Float32Bit.new(120000.0)).bytesize).to eq(5) end it "calculates size of boolean field values" do expect(described_class.field_value_size(true)).to eq(2) expect(described_class.encode(true).bytesize).to eq(2) expect(described_class.field_value_size(false)).to eq(2) expect(described_class.encode(false).bytesize).to eq(2) end it "calculates size of void field values" do expect(described_class.field_value_size(nil)).to eq(1) expect(described_class.encode(nil).bytesize).to eq(1) end it "calculates size of time field values" do t = Time.parse("2011-07-14 01:17:46 +0400") expect(described_class.field_value_size(t)).to eq(9) expect(described_class.encode(t).bytesize).to eq(9) end it "calculates size of basic table field values" do input1 = { "key" => "value" } expect(described_class.field_value_size(input1)).to eq(19) expect(described_class.encode(input1).bytesize).to eq(19) input2 = { "intval" => 1 } expect(described_class.field_value_size(input2)).to eq(21) expect(described_class.encode(input2).bytesize).to eq(21) input3 = { "intval" => 1, "key" => "value" } expect(described_class.field_value_size(input3)).to eq(35) expect(described_class.encode(input3).bytesize).to eq(35) end it "calculates size of table field values" do input1 = { "hashval" => { "protocol" => { "name" => "AMQP", "major" => 0, "minor" => "9", "rev" => 1.0, "spec" => { "url" => "http://bit.ly/hw2ELX", "utf8" => "à bientôt".force_encoding(::Encoding::ASCII_8BIT) } }, "true" => true, "false" => false, "nil" => nil } } expect(described_class.field_value_size(input1)).to eq(166) # puts(described_class.encode(input1).inspect) expect(described_class.encode(input1).bytesize).to eq(166) input2 = { "boolval" => true, "intval" => 1, "strval" => "Test", "timestampval" => Time.parse("2011-07-14 01:17:46 +0400"), "floatval" => 3.14, "longval" => 912598613, "hashval" => { "protocol" => "AMQP091", "true" => true, "false" => false, "nil" => nil } } expect(described_class.field_value_size(input2)).to eq(158) expect(described_class.encode(input2).bytesize).to eq(158) end it "calculates size of basic array field values" do input1 = [1, 2, 3] expect(described_class.field_value_size(input1)).to eq(32) expect(described_class.encode(input1).bytesize).to eq(32) input2 = ["one", "two", "three"] expect(described_class.field_value_size(input2)).to eq(31) expect(described_class.encode(input2).bytesize).to eq(31) input3 = ["one", 2, "three"] expect(described_class.field_value_size(input3)).to eq(32) expect(described_class.encode(input3).bytesize).to eq(32) input4 = ["one", 2, "three", ["four", 5, [6.0]]] expect(described_class.field_value_size(input4)).to eq(69) expect(described_class.encode(input4).bytesize).to eq(69) end end # TableValueEncoder end # Protocol end # AMQ amq-protocol-2.3.0/spec/amq/protocol/table_spec.rb0000644000004100000410000002147313226473014022171 0ustar www-datawww-datarequire 'bigdecimal' require 'time' module AMQ module Protocol RSpec.describe Table do timestamp = Time.utc(2010, 12, 31, 23, 58, 59) bigdecimal_1 = BigDecimal.new("1.0") bigdecimal_2 = BigDecimal.new("5E-3") DATA = { {} => "\x00\x00\x00\x00", {"test" => 1} => "\x00\x00\x00\x0E\x04testl\x00\x00\x00\x00\x00\x00\x00\x01", {"float" => 1.92} => "\x00\x00\x00\x0F\x05floatd?\xFE\xB8Q\xEB\x85\x1E\xB8", {"test" => "string"} => "\x00\x00\x00\x10\x04testS\x00\x00\x00\x06string", {"test" => {}} => "\x00\x00\x00\n\x04testF\x00\x00\x00\x00", {"test" => bigdecimal_1} => "\x00\x00\x00\v\x04testD\x00\x00\x00\x00\x01", {"test" => bigdecimal_2} => "\x00\x00\x00\v\x04testD\x03\x00\x00\x00\x05", {"test" => timestamp} => "\x00\x00\x00\x0e\x04testT\x00\x00\x00\x00M\x1enC" } describe ".encode" do it "should return \"\x00\x00\x00\x00\" for nil" do encoded_value = "\x00\x00\x00\x00" expect(Table.encode(nil)).to eql(encoded_value) end it "should serialize { :test => true }" do expect(Table.encode(:test => true)). to eql("\x00\x00\x00\a\x04testt\x01".force_encoding(Encoding::ASCII_8BIT)) end it "should serialize { :test => false }" do expect(Table.encode(:test => false)). to eql("\x00\x00\x00\a\x04testt\x00".force_encoding(Encoding::ASCII_8BIT)) end it "should serialize { :coordinates => { :latitude => 59.35 } }" do expect(Table.encode(:coordinates => { :latitude => 59.35 })). to eql("\x00\x00\x00#\vcoordinatesF\x00\x00\x00\x12\blatituded@M\xAC\xCC\xCC\xCC\xCC\xCD".force_encoding(Encoding::ASCII_8BIT)) end it "should serialize { :coordinates => { :longitude => 18.066667 } }" do expect(Table.encode(:coordinates => { :longitude => 18.066667 })). to eql("\x00\x00\x00$\vcoordinatesF\x00\x00\x00\x13\tlongituded@2\x11\x11\x16\xA8\xB8\xF1".force_encoding(Encoding::ASCII_8BIT)) end DATA.each do |data, encoded| it "should return #{encoded.inspect} for #{data.inspect}" do expect(Table.encode(data)).to eql(encoded.force_encoding(Encoding::ASCII_8BIT)) end end end describe ".decode" do DATA.each do |data, encoded| it "should return #{data.inspect} for #{encoded.inspect}" do expect(Table.decode(encoded)).to eql(data) end it "is capable of decoding what it encodes" do expect(Table.decode(Table.encode(data))).to eq(data) end end # DATA.each it "is capable of decoding boolean table values" do input1 = { "boolval" => true } expect(Table.decode(Table.encode(input1))).to eq(input1) input2 = { "boolval" => false } expect(Table.decode(Table.encode(input2))).to eq(input2) end it "is capable of decoding nil table values" do input = { "nilval" => nil } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding nil table in nested hash/map values" do input = { "hash" => {"nil" => nil} } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding string table values" do input = { "stringvalue" => "string" } expect(Table.decode(Table.encode(input))).to eq(input) expect(Table.decode("\x00\x00\x00\x17\vstringvalueS\x00\x00\x00\x06string")).to eq(input) end it "is capable of decoding byte array table values (as Ruby strings)" do expect(Table.decode("\x00\x00\x00\x17\vstringvaluex\x00\x00\x00\x06string")).to eq({"stringvalue" => "string"}) end it "is capable of decoding string table values with UTF-8 characters" do input = { "строка".force_encoding(::Encoding::ASCII_8BIT) => "значение".force_encoding(::Encoding::ASCII_8BIT) } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding integer table values" do input = { "intvalue" => 10 } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding signed integer table values" do input = { "intvalue" => -10 } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding long table values" do input = { "longvalue" => 912598613 } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding float table values" do input = { "floatvalue" => 100.0 } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding time table values" do input = { "intvalue" => Time.parse("2011-07-14 01:17:46 +0400") } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding empty hash table values" do input = { "hashvalue" => Hash.new } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding empty array table values" do input = { "arrayvalue" => Array.new } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding single string value array table values" do input = { "arrayvalue" => ["amq-protocol"] } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding simple nested hash table values" do input = { "hashvalue" => { "a" => "b" } } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding nil table values" do input = { "nil" => nil } expect(Table.decode(Table.encode(input))).to eq(input) end it 'is capable of decoding 8bit signed integers' do output = TableValueDecoder.decode_byte("\xC0",0).first expect(output).to eq(192) end it 'is capable of decoding 16bit signed integers' do output = TableValueDecoder.decode_short("\x06\x8D", 0).first expect(output).to eq(1677) end it "is capable of decoding tables" do input = { "boolval" => true, "intval" => 1, "strval" => "Test", "timestampval" => Time.parse("2011-07-14 01:17:46 +0400"), "floatval" => 3.14, "longval" => 912598613, "hashval" => { "protocol" => "AMQP091", "true" => true, "false" => false, "nil" => nil } } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding deeply nested tables" do input = { "hashval" => { "protocol" => { "name" => "AMQP", "major" => 0, "minor" => "9", "rev" => 1.0, "spec" => { "url" => "http://bit.ly/hw2ELX", "utf8" => "à bientôt".force_encoding(::Encoding::ASCII_8BIT) } }, "true" => true, "false" => false, "nil" => nil } } expect(Table.decode(Table.encode(input))).to eq(input) end it "is capable of decoding array values in tables" do input1 = { "arrayval1" => [198, 3, 77, 8.0, ["inner", "array", { "oh" => "well", "it" => "should work", "3" => 6 }], "two", { "a" => "value", "is" => nil }], "arrayval2" => [198, 3, 77, "two", { "a" => "value", "is" => nil }, 8.0, ["inner", "array", { "oh" => "well", "it" => "should work", "3" => 6 }]] } expect(Table.decode(Table.encode(input1))).to eq(input1) now = Time.now input2 = { "coordinates" => { "latitude" => 59.35, "longitude" => 18.066667 }, "participants" => 11, "venue" => "Stockholm", "true_field" => true, "false_field" => false, "nil_field" => nil, "ary_field" => ["one", 2.0, 3] } expect(Table.decode(Table.encode(input2))).to eq(input2) input3 = { "timely" => { "now" => now } } expect(Table.decode(Table.encode(input3))["timely"]["now"].to_i).to eq(now.to_i) end end # describe end end end amq-protocol-2.3.0/spec/amq/protocol/tx_spec.rb0000644000004100000410000000257313226473014021535 0ustar www-datawww-data# encoding: binary module AMQ module Protocol class Tx RSpec.describe Select do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 method_frame = Select.encode(channel) expect(method_frame.payload).to eq("\000Z\000\n") expect(method_frame.channel).to eq(1) end end end # RSpec.describe SelectOk do # describe '.decode' do # end # end RSpec.describe Commit do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 method_frame = Commit.encode(channel) expect(method_frame.payload).to eq("\000Z\000\024") expect(method_frame.channel).to eq(1) end end end # RSpec.describe CommitOk do # describe '.decode' do # end # end RSpec.describe Rollback do describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 method_frame = Rollback.encode(channel) expect(method_frame.payload).to eq("\000Z\000\036") expect(method_frame.channel).to eq(1) end end end # RSpec.describe RollbackOk do # describe '.decode' do # end # end end end end amq-protocol-2.3.0/spec/amq/protocol/confirm_spec.rb0000644000004100000410000000177213226473014022537 0ustar www-datawww-data# encoding: binary module AMQ module Protocol class Confirm RSpec.describe Select do describe '.decode' do subject do Select.decode("\x01") end its(:nowait) { should be_truthy } end describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 nowait = true method_frame = Select.encode(channel, nowait) expect(method_frame.payload).to eq("\x00U\x00\n\x01") expect(method_frame.channel).to eq(1) end end end RSpec.describe SelectOk do # describe '.decode' do # end describe '.encode' do it 'encodes the parameters into a MethodFrame' do channel = 1 method_frame = SelectOk.encode(channel) expect(method_frame.payload).to eq("\000U\000\v") expect(method_frame.channel).to eq(1) end end end end end end amq-protocol-2.3.0/spec/amq/protocol/frame_spec.rb0000644000004100000410000000666513226473014022202 0ustar www-datawww-datamodule AMQ module Protocol RSpec.describe Frame do describe ".encode" do it "should raise FrameTypeError if type isn't one of: [:method, :header, :body, :heartbeat]" do expect { Frame.encode(nil, "", 0) }.to raise_error(FrameTypeError) end it "should raise FrameTypeError if type isn't valid (when type is a symbol)" do expect { Frame.encode(:xyz, "test", 12) }.to raise_error(FrameTypeError) end it "should raise FrameTypeError if type isn't valid (when type is a number)" do expect { Frame.encode(16, "test", 12) }.to raise_error(FrameTypeError) end it "should raise RuntimeError if channel isn't 0 or an integer in range 1..65535" do expect { Frame.encode(:method, "", -1) }.to raise_error(RuntimeError, /^Channel has to be 0 or an integer in range 1\.\.65535/) expect { Frame.encode(:method, "", 65536) }.to raise_error(RuntimeError, /^Channel has to be 0 or an integer in range 1\.\.65535/) expect { Frame.encode(:method, "", 65535) }.not_to raise_error expect { Frame.encode(:method, "", 0) }.not_to raise_error expect { Frame.encode(:method, "", 1) }.not_to raise_error end it "should raise RuntimeError if payload is nil" do expect { Frame.encode(:method, nil, 0) }.to raise_error(RuntimeError, "Payload can't be nil") end it "should encode type" do expect(Frame.encode(:body, "", 0).unpack("c").first).to eql(3) end it "should encode channel" do expect(Frame.encode(:body, "", 12).unpack("cn").last).to eql(12) end it "should encode size" do expect(Frame.encode(:body, "test", 12).unpack("cnN").last).to eql(4) end it "should include payload" do expect(Frame.encode(:body, "test", 12)[7..-2]).to eql("test") end it "should include final octet" do expect(Frame.encode(:body, "test", 12).each_byte.to_a.last).to eq("CE".hex) end it "should encode unicode strings" do expect { Frame.encode(:body, "à bientôt!", 12) }.to_not raise_error end end describe ".new" do it "should raise FrameTypeError if the type is not one of the accepted" do expect { Frame.new(10) }.to raise_error(FrameTypeError) end end describe '#decode_header' do it 'raises FrameTypeError if the decoded type is not one of the accepted' do expect { Frame.decode_header("\n\x00\x01\x00\x00\x00\x05") }.to raise_error(FrameTypeError) end it 'raises EmptyResponseError if the header is nil' do expect { Frame.decode_header(nil) }.to raise_error(EmptyResponseError) end end describe HeaderFrame do subject { HeaderFrame.new("\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x98\x00\x18application/octet-stream\x02\x00", nil) } it "should decode body_size from payload" do expect(subject.body_size).to eq(10) end it "should decode klass_id from payload" do expect(subject.klass_id).to eq(60) end it "should decode weight from payload" do expect(subject.weight).to eq(0) end it "should decode properties from payload" do expect(subject.properties[:delivery_mode]).to eq(2) expect(subject.properties[:priority]).to eq(0) end end end end end amq-protocol-2.3.0/spec/amq/protocol/method_spec.rb0000644000004100000410000000421013226473014022350 0ustar www-datawww-datamodule AMQ module Protocol RSpec.describe Method do describe '.split_headers' do it 'splits user defined headers into properties and headers' do input = {:delivery_mode => 2, :content_type => 'application/octet-stream', :foo => 'bar'} properties, headers = Method.split_headers(input) expect(properties).to eq({:delivery_mode => 2, :content_type => 'application/octet-stream'}) expect(headers).to eq({:foo => 'bar'}) end end describe '.encode_body' do context 'when the body fits in a single frame' do it 'encodes a body into a BodyFrame' do body_frames = Method.encode_body('Hello world', 1, 131072) expect(body_frames.first.payload).to eq('Hello world') expect(body_frames.first.channel).to eq(1) expect(body_frames.size).to eq(1) end end context 'when the body is too big to fit in a single frame' do it 'encodes a body into a list of BodyFrames that each fit within the frame size' do lipsum = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' frame_size = 100 expected_payload_size = 92 body_frames = Method.encode_body(lipsum, 1, frame_size) expect(body_frames.map(&:payload)).to eq(lipsum.split('').each_slice(expected_payload_size).map(&:join)) end end context 'when the body fits perfectly in a single frame' do it 'encodes a body into a single BodyFrame' do body_frames = Method.encode_body('*' * 131064, 1, 131072) expect(body_frames.first.payload).to eq('*' * 131064) expect(body_frames.size).to eq(1) end end end end end end amq-protocol-2.3.0/spec/amq/protocol/value_decoder_spec.rb0000644000004100000410000000455513226473014023705 0ustar www-datawww-datarequire 'time' require "amq/protocol/table_value_decoder" module AMQ module Protocol RSpec.describe TableValueDecoder do it "is capable of decoding basic arrays TableValueEncoder encodes" do input1 = [1, 2, 3] value, offset = described_class.decode_array(TableValueEncoder.encode(input1), 1) expect(value.size).to eq(3) expect(value.first).to eq(1) expect(value).to eq(input1) input2 = ["one", 2, "three"] value, offset = described_class.decode_array(TableValueEncoder.encode(input2), 1) expect(value.size).to eq(3) expect(value.first).to eq("one") expect(value).to eq(input2) input3 = ["one", 2, "three", 4.0, 5000000.0] value, offset = described_class.decode_array(TableValueEncoder.encode(input3), 1) expect(value.size).to eq(5) expect(value.last).to eq(5000000.0) expect(value).to eq(input3) end it "is capable of decoding arrays TableValueEncoder encodes" do input1 = [{ "one" => 2 }, 3] data1 = TableValueEncoder.encode(input1) # puts(TableValueEncoder.encode({ "one" => 2 }).inspect) # puts(TableValueEncoder.encode(input1).inspect) value, offset = described_class.decode_array(data1, 1) expect(value.size).to eq(2) expect(value.first).to eq(Hash["one" => 2]) expect(value).to eq(input1) input2 = ["one", 2, { "three" => { "four" => 5.0 } }] value, offset = described_class.decode_array(TableValueEncoder.encode(input2), 1) expect(value.size).to eq(3) expect(value.last["three"]["four"]).to eq(5.0) expect(value).to eq(input2) end it "is capable of decoding 32 bit float values" do input = Float32Bit.new(10.0) data = TableValueEncoder.encode(input) value = described_class.decode_32bit_float(data, 1)[0] expect(value).to eq(10.0) end context "8bit/byte decoding" do let(:examples) { { 0x00 => "\x00", 0x01 => "\x01", 0x10 => "\x10", 255 => "\xFF" # not -1 } } it "is capable of decoding byte values" do examples.each do |key, value| expect(described_class.decode_byte(value, 0).first).to eq(key) end end end end end end amq-protocol-2.3.0/spec/amq/pack_spec.rb0000644000004100000410000000372613226473014020160 0ustar www-datawww-data# encoding: binary RSpec.describe AMQ::Pack do context "16-bit big-endian packing / unpacking" do let(:examples_16bit) { { 0x068D => "\x06\x8D" # 1677 } } it "unpacks signed integers from a string to a number" do examples_16bit.each do |key, value| expect(described_class.unpack_int16_big_endian(value)[0]).to eq(key) end end end context "64-bit big-endian packing / unpacking" do let(:examples) { { 0x0000000000000000 => "\x00\x00\x00\x00\x00\x00\x00\x00", 0x000000000000000A => "\x00\x00\x00\x00\x00\x00\x00\x0A", 0x00000000000000A0 => "\x00\x00\x00\x00\x00\x00\x00\xA0", 0x000000000000B0A0 => "\x00\x00\x00\x00\x00\x00\xB0\xA0", 0x00000000000CB0AD => "\x00\x00\x00\x00\x00\x0C\xB0\xAD", 0x8BADF00DDEFEC8ED => "\x8B\xAD\xF0\x0D\xDE\xFE\xC8\xED", 0x0D15EA5EFEE1DEAD => "\x0D\x15\xEA\x5E\xFE\xE1\xDE\xAD", 0xDEADBEEFDEADBABE => "\xDE\xAD\xBE\xEF\xDE\xAD\xBA\xBE" } } it "packs integers into big-endian string" do examples.each do |key, value| expect(described_class.pack_uint64_big_endian(key)).to eq(value) end end it "should unpack string representation into integer" do examples.each do |key, value| expect(described_class.unpack_uint64_big_endian(value)[0]).to eq(key) end end if RUBY_VERSION < '1.9' describe "with utf encoding" do before do $KCODE = 'u' end after do $KCODE = 'NONE' end it "packs integers into big-endian string" do examples.each do |key, value| expect(described_class.pack_uint64_big_endian(key)).to eq(value) end end it "should unpack string representation into integer" do examples.each do |key, value| expect(described_class.unpack_uint64_big_endian(value)[0]).to eq(key) end end end end end end amq-protocol-2.3.0/spec/amq/int_allocator_spec.rb0000644000004100000410000000475713226473014022101 0ustar www-datawww-datarequire "amq/int_allocator" RSpec.describe AMQ::IntAllocator do # # Environment # subject do described_class.new(1, 5) end # ... # # Examples # describe "#number_of_bits" do it "returns number of bits available for allocation" do expect(subject.number_of_bits).to eq(4) end end describe "#hi" do it "returns upper bound of the allocation range" do expect(subject.hi).to eq(5) end end describe "#lo" do it "returns lower bound of the allocation range" do expect(subject.lo).to eq(1) end end describe "#allocate" do context "when integer in the range is available" do it "returns allocated integer" do expect(subject.allocate).to eq(1) expect(subject.allocate).to eq(2) expect(subject.allocate).to eq(3) expect(subject.allocate).to eq(4) expect(subject.allocate).to eq(-1) end end context "when integer in the range IS NOT available" do it "returns -1" do 4.times { subject.allocate } expect(subject.allocate).to eq(-1) expect(subject.allocate).to eq(-1) expect(subject.allocate).to eq(-1) expect(subject.allocate).to eq(-1) end end end describe "#free" do context "when the integer WAS allocated" do it "returns frees that integer" do 4.times { subject.allocate } expect(subject.allocate).to eq(-1) subject.free(1) expect(subject.allocate).to eq(1) expect(subject.allocate).to eq(-1) subject.free(2) expect(subject.allocate).to eq(2) expect(subject.allocate).to eq(-1) subject.free(3) expect(subject.allocate).to eq(3) expect(subject.allocate).to eq(-1) end end context "when the integer WAS NOT allocated" do it "has no effect" do 32.times { subject.free(1) } expect(subject.allocate).to eq(1) end end end describe "#allocated?" do context "when given position WAS allocated" do it "returns true" do 3.times { subject.allocate } expect(subject.allocated?(1)).to be_truthy expect(subject.allocated?(2)).to be_truthy expect(subject.allocated?(3)).to be_truthy end end context "when given position WAS NOT allocated" do it "returns false" do 2.times { subject.allocate } expect(subject.allocated?(3)).to be_falsey expect(subject.allocated?(4)).to be_falsey end end end end amq-protocol-2.3.0/spec/amq/protocol_spec.rb0000644000004100000410000006343413226473014021105 0ustar www-datawww-datamodule AMQ RSpec.describe Protocol do it "should have PROTOCOL_VERSION constant" do expect(Protocol::PROTOCOL_VERSION).to match(/^\d+\.\d+\.\d$/) end it "should have DEFAULT_PORT constant" do expect(Protocol::DEFAULT_PORT).to be_kind_of(Integer) end it "should have PREAMBLE constant" do expect(Protocol::PREAMBLE).to be_kind_of(String) end describe Protocol::Error do it "should be an exception class" do expect(Protocol::Error.ancestors).to include(Exception) end end describe Protocol::Connection do it "should be a subclass of Class" do expect(Protocol::Connection.superclass).to eq(Protocol::Class) end it "should have name equal to connection" do expect(Protocol::Connection.name).to eql("connection") end it "should have method id equal to 10" do expect(Protocol::Connection.method_id).to eq(10) end describe Protocol::Connection::Start do it "should be a subclass of Method" do expect(Protocol::Connection::Start.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.start" do expect(Protocol::Connection::Start.name).to eql("connection.start") end it "should have method id equal to 10" do expect(Protocol::Connection::Start.method_id).to eq(10) end end describe Protocol::Connection::StartOk do it "should be a subclass of Method" do expect(Protocol::Connection::StartOk.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.start-ok" do expect(Protocol::Connection::StartOk.name).to eql("connection.start-ok") end it "has method id equal to 11" do expect(Protocol::Connection::StartOk.method_id).to eq(11) end end describe Protocol::Connection::Secure do it "should be a subclass of Method" do expect(Protocol::Connection::Secure.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.secure" do expect(Protocol::Connection::Secure.name).to eql("connection.secure") end it "has method id equal to 20" do expect(Protocol::Connection::Secure.method_id).to eq(20) end end describe Protocol::Connection::SecureOk do it "should be a subclass of Method" do expect(Protocol::Connection::SecureOk.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.secure-ok" do expect(Protocol::Connection::SecureOk.name).to eql("connection.secure-ok") end it "has method id equal to 21" do expect(Protocol::Connection::SecureOk.method_id).to eq(21) end end describe Protocol::Connection::Tune do it "should be a subclass of Method" do expect(Protocol::Connection::Tune.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.tune" do expect(Protocol::Connection::Tune.name).to eql("connection.tune") end it "has method id equal to 30" do expect(Protocol::Connection::Tune.method_id).to eq(30) end end describe Protocol::Connection::TuneOk do it "should be a subclass of Method" do expect(Protocol::Connection::TuneOk.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.tune-ok" do expect(Protocol::Connection::TuneOk.name).to eql("connection.tune-ok") end it "has method id equal to 31" do expect(Protocol::Connection::TuneOk.method_id).to eq(31) end describe ".encode" do it do frame = Protocol::Connection::TuneOk.encode(0, 131072, 0) expect(frame.payload).to eql("\x00\n\x00\x1F\x00\x00\x00\x02\x00\x00\x00\x00") end end end describe Protocol::Connection::Open do it "should be a subclass of Method" do expect(Protocol::Connection::Open.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.open" do expect(Protocol::Connection::Open.name).to eql("connection.open") end it "has method id equal to 40" do expect(Protocol::Connection::Open.method_id).to eq(40) end end describe Protocol::Connection::OpenOk do it "should be a subclass of Method" do expect(Protocol::Connection::OpenOk.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.open-ok" do expect(Protocol::Connection::OpenOk.name).to eql("connection.open-ok") end it "has method id equal to 41" do expect(Protocol::Connection::OpenOk.method_id).to eq(41) end end describe Protocol::Connection::Close do it "should be a subclass of Method" do expect(Protocol::Connection::Close.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.close" do expect(Protocol::Connection::Close.name).to eql("connection.close") end it "has method id equal to 50" do expect(Protocol::Connection::Close.method_id).to eq(50) end end describe Protocol::Connection::CloseOk do it "should be a subclass of Method" do expect(Protocol::Connection::CloseOk.superclass).to eq(Protocol::Method) end it "should have method name equal to connection.close-ok" do expect(Protocol::Connection::CloseOk.name).to eql("connection.close-ok") end it "has method id equal to 51" do expect(Protocol::Connection::CloseOk.method_id).to eq(51) end end end describe Protocol::Channel do it "should be a subclass of Class" do expect(Protocol::Channel.superclass).to eq(Protocol::Class) end it "should have name equal to channel" do expect(Protocol::Channel.name).to eql("channel") end it "has method id equal to 20" do expect(Protocol::Channel.method_id).to eq(20) end describe Protocol::Channel::Open do it "should be a subclass of Method" do expect(Protocol::Channel::Open.superclass).to eq(Protocol::Method) end it "should have method name equal to channel.open" do expect(Protocol::Channel::Open.name).to eql("channel.open") end it "has method id equal to 10" do expect(Protocol::Channel::Open.method_id).to eq(10) end end describe Protocol::Channel::OpenOk do it "should be a subclass of Method" do expect(Protocol::Channel::OpenOk.superclass).to eq(Protocol::Method) end it "should have method name equal to channel.open-ok" do expect(Protocol::Channel::OpenOk.name).to eql("channel.open-ok") end it "has method id equal to 11" do expect(Protocol::Channel::OpenOk.method_id).to eq(11) end end describe Protocol::Channel::Flow do it "should be a subclass of Method" do expect(Protocol::Channel::Flow.superclass).to eq(Protocol::Method) end it "should have method name equal to channel.flow" do expect(Protocol::Channel::Flow.name).to eql("channel.flow") end it "has method id equal to 20" do expect(Protocol::Channel::Flow.method_id).to eq(20) end end describe Protocol::Channel::FlowOk do it "should be a subclass of Method" do expect(Protocol::Channel::FlowOk.superclass).to eq(Protocol::Method) end it "should have method name equal to channel.flow-ok" do expect(Protocol::Channel::FlowOk.name).to eql("channel.flow-ok") end it "has method id equal to 21" do expect(Protocol::Channel::FlowOk.method_id).to eq(21) end end describe Protocol::Channel::Close do it "should be a subclass of Method" do expect(Protocol::Channel::Close.superclass).to eq(Protocol::Method) end it "should have method name equal to channel.close" do expect(Protocol::Channel::Close.name).to eql("channel.close") end it "has method id equal to 40" do expect(Protocol::Channel::Close.method_id).to eq(40) end end describe Protocol::Channel::CloseOk do it "should be a subclass of Method" do expect(Protocol::Channel::CloseOk.superclass).to eq(Protocol::Method) end it "should have method name equal to channel.close-ok" do expect(Protocol::Channel::CloseOk.name).to eql("channel.close-ok") end it "has method id equal to 41" do expect(Protocol::Channel::CloseOk.method_id).to eq(41) end end end describe Protocol::Exchange do it "should be a subclass of Class" do expect(Protocol::Exchange.superclass).to eq(Protocol::Class) end it "should have name equal to exchange" do expect(Protocol::Exchange.name).to eql("exchange") end it "has method id equal to 40" do expect(Protocol::Exchange.method_id).to eq(40) end describe Protocol::Exchange::Declare do it "should be a subclass of Method" do expect(Protocol::Exchange::Declare.superclass).to eq(Protocol::Method) end it "should have method name equal to exchange.declare" do expect(Protocol::Exchange::Declare.name).to eql("exchange.declare") end it "has method id equal to 10" do expect(Protocol::Exchange::Declare.method_id).to eq(10) end end describe Protocol::Exchange::DeclareOk do it "should be a subclass of Method" do expect(Protocol::Exchange::DeclareOk.superclass).to eq(Protocol::Method) end it "should have method name equal to exchange.declare-ok" do expect(Protocol::Exchange::DeclareOk.name).to eql("exchange.declare-ok") end it "has method id equal to 11" do expect(Protocol::Exchange::DeclareOk.method_id).to eq(11) end end describe Protocol::Exchange::Delete do it "should be a subclass of Method" do expect(Protocol::Exchange::Delete.superclass).to eq(Protocol::Method) end it "should have method name equal to exchange.delete" do expect(Protocol::Exchange::Delete.name).to eql("exchange.delete") end it "has method id equal to 20" do expect(Protocol::Exchange::Delete.method_id).to eq(20) end end describe Protocol::Exchange::DeleteOk do it "should be a subclass of Method" do expect(Protocol::Exchange::DeleteOk.superclass).to eq(Protocol::Method) end it "should have method name equal to exchange.delete-ok" do expect(Protocol::Exchange::DeleteOk.name).to eql("exchange.delete-ok") end it "has method id equal to 21" do expect(Protocol::Exchange::DeleteOk.method_id).to eq(21) end end describe Protocol::Exchange::Bind do it "should be a subclass of Method" do expect(Protocol::Exchange::Bind.superclass).to eq(Protocol::Method) end it "should have method name equal to exchange.bind" do expect(Protocol::Exchange::Bind.name).to eql("exchange.bind") end it "has method id equal to 30" do expect(Protocol::Exchange::Bind.method_id).to eq(30) end end describe Protocol::Exchange::BindOk do it "should be a subclass of Method" do expect(Protocol::Exchange::BindOk.superclass).to eq(Protocol::Method) end it "should have method name equal to exchange.bind-ok" do expect(Protocol::Exchange::BindOk.name).to eql("exchange.bind-ok") end it "has method id equal to 31" do expect(Protocol::Exchange::BindOk.method_id).to eq(31) end end describe Protocol::Exchange::Unbind do it "should be a subclass of Method" do expect(Protocol::Exchange::Unbind.superclass).to eq(Protocol::Method) end it "should have method name equal to exchange.unbind" do expect(Protocol::Exchange::Unbind.name).to eql("exchange.unbind") end it "has method id equal to 40" do expect(Protocol::Exchange::Unbind.method_id).to eq(40) end end describe Protocol::Exchange::UnbindOk do it "should be a subclass of Method" do expect(Protocol::Exchange::UnbindOk.superclass).to eq(Protocol::Method) end it "should have method name equal to exchange.unbind-ok" do expect(Protocol::Exchange::UnbindOk.name).to eql("exchange.unbind-ok") end it "has method id equal to 51" do expect(Protocol::Exchange::UnbindOk.method_id).to eq(51) end end end describe Protocol::Queue do it "should be a subclass of Class" do expect(Protocol::Queue.superclass).to eq(Protocol::Class) end it "should have name equal to queue" do expect(Protocol::Queue.name).to eql("queue") end it "has method id equal to 50" do expect(Protocol::Queue.method_id).to eq(50) end describe Protocol::Queue::Declare do it "should be a subclass of Method" do expect(Protocol::Queue::Declare.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.declare" do expect(Protocol::Queue::Declare.name).to eql("queue.declare") end it "has method id equal to 10" do expect(Protocol::Queue::Declare.method_id).to eq(10) end end describe Protocol::Queue::DeclareOk do it "should be a subclass of Method" do expect(Protocol::Queue::DeclareOk.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.declare-ok" do expect(Protocol::Queue::DeclareOk.name).to eql("queue.declare-ok") end it "has method id equal to 11" do expect(Protocol::Queue::DeclareOk.method_id).to eq(11) end end describe Protocol::Queue::Bind do it "should be a subclass of Method" do expect(Protocol::Queue::Bind.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.bind" do expect(Protocol::Queue::Bind.name).to eql("queue.bind") end it "has method id equal to 20" do expect(Protocol::Queue::Bind.method_id).to eq(20) end end describe Protocol::Queue::BindOk do it "should be a subclass of Method" do expect(Protocol::Queue::BindOk.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.bind-ok" do expect(Protocol::Queue::BindOk.name).to eql("queue.bind-ok") end it "has method id equal to 21" do expect(Protocol::Queue::BindOk.method_id).to eq(21) end end describe Protocol::Queue::Purge do it "should be a subclass of Method" do expect(Protocol::Queue::Purge.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.purge" do expect(Protocol::Queue::Purge.name).to eql("queue.purge") end it "has method id equal to 30" do expect(Protocol::Queue::Purge.method_id).to eq(30) end end describe Protocol::Queue::PurgeOk do it "should be a subclass of Method" do expect(Protocol::Queue::PurgeOk.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.purge-ok" do expect(Protocol::Queue::PurgeOk.name).to eql("queue.purge-ok") end it "has method id equal to 31" do expect(Protocol::Queue::PurgeOk.method_id).to eq(31) end end describe Protocol::Queue::Delete do it "should be a subclass of Method" do expect(Protocol::Queue::Delete.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.delete" do expect(Protocol::Queue::Delete.name).to eql("queue.delete") end it "has method id equal to 40" do expect(Protocol::Queue::Delete.method_id).to eq(40) end end describe Protocol::Queue::DeleteOk do it "should be a subclass of Method" do expect(Protocol::Queue::DeleteOk.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.delete-ok" do expect(Protocol::Queue::DeleteOk.name).to eql("queue.delete-ok") end it "has method id equal to 41" do expect(Protocol::Queue::DeleteOk.method_id).to eq(41) end end describe Protocol::Queue::Unbind do it "should be a subclass of Method" do expect(Protocol::Queue::Unbind.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.unbind" do expect(Protocol::Queue::Unbind.name).to eql("queue.unbind") end it "has method id equal to 50" do expect(Protocol::Queue::Unbind.method_id).to eq(50) end end describe Protocol::Queue::UnbindOk do it "should be a subclass of Method" do expect(Protocol::Queue::UnbindOk.superclass).to eq(Protocol::Method) end it "should have method name equal to queue.unbind-ok" do expect(Protocol::Queue::UnbindOk.name).to eql("queue.unbind-ok") end it "has method id equal to 51" do expect(Protocol::Queue::UnbindOk.method_id).to eq(51) end end end describe Protocol::Basic do it "should be a subclass of Class" do expect(Protocol::Basic.superclass).to eq(Protocol::Class) end it "should have name equal to basic" do expect(Protocol::Basic.name).to eql("basic") end it "has method id equal to 60" do expect(Protocol::Basic.method_id).to eq(60) end describe Protocol::Basic::Qos do it "should be a subclass of Method" do expect(Protocol::Basic::Qos.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.qos" do expect(Protocol::Basic::Qos.name).to eql("basic.qos") end it "has method id equal to 10" do expect(Protocol::Basic::Qos.method_id).to eq(10) end end describe Protocol::Basic::QosOk do it "should be a subclass of Method" do expect(Protocol::Basic::QosOk.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.qos-ok" do expect(Protocol::Basic::QosOk.name).to eql("basic.qos-ok") end it "has method id equal to 11" do expect(Protocol::Basic::QosOk.method_id).to eq(11) end end describe Protocol::Basic::Consume do it "should be a subclass of Method" do expect(Protocol::Basic::Consume.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.consume" do expect(Protocol::Basic::Consume.name).to eql("basic.consume") end it "has method id equal to 20" do expect(Protocol::Basic::Consume.method_id).to eq(20) end end describe Protocol::Basic::ConsumeOk do it "should be a subclass of Method" do expect(Protocol::Basic::ConsumeOk.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.consume-ok" do expect(Protocol::Basic::ConsumeOk.name).to eql("basic.consume-ok") end it "has method id equal to 21" do expect(Protocol::Basic::ConsumeOk.method_id).to eq(21) end end describe Protocol::Basic::Cancel do it "should be a subclass of Method" do expect(Protocol::Basic::Cancel.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.cancel" do expect(Protocol::Basic::Cancel.name).to eql("basic.cancel") end it "has method id equal to 30" do expect(Protocol::Basic::Cancel.method_id).to eq(30) end end describe Protocol::Basic::CancelOk do it "should be a subclass of Method" do expect(Protocol::Basic::CancelOk.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.cancel-ok" do expect(Protocol::Basic::CancelOk.name).to eql("basic.cancel-ok") end it "has method id equal to 31" do expect(Protocol::Basic::CancelOk.method_id).to eq(31) end end describe Protocol::Basic::Publish do it "should be a subclass of Method" do expect(Protocol::Basic::Publish.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.publish" do expect(Protocol::Basic::Publish.name).to eql("basic.publish") end it "has method id equal to 40" do expect(Protocol::Basic::Publish.method_id).to eq(40) end end describe Protocol::Basic::Return do it "should be a subclass of Method" do expect(Protocol::Basic::Return.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.return" do expect(Protocol::Basic::Return.name).to eql("basic.return") end it "has method id equal to 50" do expect(Protocol::Basic::Return.method_id).to eq(50) end end describe Protocol::Basic::Deliver do it "should be a subclass of Method" do expect(Protocol::Basic::Deliver.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.deliver" do expect(Protocol::Basic::Deliver.name).to eql("basic.deliver") end it "has method id equal to 60" do expect(Protocol::Basic::Deliver.method_id).to eq(60) end end describe Protocol::Basic::Get do it "should be a subclass of Method" do expect(Protocol::Basic::Get.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.get" do expect(Protocol::Basic::Get.name).to eql("basic.get") end it "has method id equal to 70" do expect(Protocol::Basic::Get.method_id).to eq(70) end end describe Protocol::Basic::GetOk do it "should be a subclass of Method" do expect(Protocol::Basic::GetOk.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.get-ok" do expect(Protocol::Basic::GetOk.name).to eql("basic.get-ok") end it "has method id equal to 71" do expect(Protocol::Basic::GetOk.method_id).to eq(71) end end describe Protocol::Basic::GetEmpty do it "should be a subclass of Method" do expect(Protocol::Basic::GetEmpty.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.get-empty" do expect(Protocol::Basic::GetEmpty.name).to eql("basic.get-empty") end it "has method id equal to 72" do expect(Protocol::Basic::GetEmpty.method_id).to eq(72) end end describe Protocol::Basic::Ack do it "should be a subclass of Method" do expect(Protocol::Basic::Ack.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.ack" do expect(Protocol::Basic::Ack.name).to eql("basic.ack") end it "has method id equal to 80" do expect(Protocol::Basic::Ack.method_id).to eq(80) end end describe Protocol::Basic::Reject do it "should be a subclass of Method" do expect(Protocol::Basic::Reject.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.reject" do expect(Protocol::Basic::Reject.name).to eql("basic.reject") end it "has method id equal to 90" do expect(Protocol::Basic::Reject.method_id).to eq(90) end end describe Protocol::Basic::RecoverAsync do it "should be a subclass of Method" do expect(Protocol::Basic::RecoverAsync.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.recover-async" do expect(Protocol::Basic::RecoverAsync.name).to eql("basic.recover-async") end it "has method id equal to 100" do expect(Protocol::Basic::RecoverAsync.method_id).to eq(100) end end describe Protocol::Basic::Recover do it "should be a subclass of Method" do expect(Protocol::Basic::Recover.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.recover" do expect(Protocol::Basic::Recover.name).to eql("basic.recover") end it "has method id equal to 110" do expect(Protocol::Basic::Recover.method_id).to eq(110) end end describe Protocol::Basic::RecoverOk do it "should be a subclass of Method" do expect(Protocol::Basic::RecoverOk.superclass).to eq(Protocol::Method) end it "should have method name equal to basic.recover-ok" do expect(Protocol::Basic::RecoverOk.name).to eql("basic.recover-ok") end it "has method id equal to 111" do expect(Protocol::Basic::RecoverOk.method_id).to eq(111) end end end end end amq-protocol-2.3.0/spec/amq/uri_parsing_spec.rb0000644000004100000410000002075513226473014021565 0ustar www-datawww-datarequire "amq/uri" # https://www.rabbitmq.com/uri-spec.html # http://www.ietf.org/rfc/rfc3986.txt # https://tools.ietf.org/rfc/rfc5246.txt RSpec.describe AMQ::URI do describe ".parse" do subject { described_class.parse(uri) } context "schema is not amqp or amqps" do let(:uri) { "http://rabbitmq" } it "raises ArgumentError" do expect { subject }.to raise_error(ArgumentError, /amqp or amqps schema/) end end describe "host" do context "present" do let(:uri) { "amqp://rabbitmq" } it "parses host" do expect(subject[:host]).to eq("rabbitmq") end end if RUBY_VERSION >= "2.2" context "absent" do let(:uri) { "amqp://" } # Note that according to the ABNF, the host component may not be absent, but it may be zero-length. it "falls back to default nil host" do expect(subject[:host]).to be_nil end end end if RUBY_VERSION < "2.2" context "absent" do let(:uri) { "amqp://" } # Note that according to the ABNF, the host component may not be absent, but it may be zero-length. it "raises InvalidURIError" do expect { subject[:host] }.to raise_error(URI::InvalidURIError, /bad URI\(absolute but no path\)/) end end end end describe "port" do context "present" do let(:uri) { "amqp://rabbitmq:5672" } it "parses port" do expect(subject[:port]).to eq(5672) end end context "absent" do context "schema amqp" do let(:uri) { "amqp://rabbitmq" } it "falls back to 5672 port" do expect(subject[:port]).to eq(5672) end end context "schema amqps" do let(:uri) { "amqps://rabbitmq" } it "falls back to 5671 port" do expect(subject[:port]).to eq(5671) end end end end describe "username and passowrd" do context "both present" do let(:uri) { "amqp://alpha:beta@rabbitmq" } it "parses user and pass" do expect(subject[:user]).to eq("alpha") expect(subject[:pass]).to eq("beta") end end context "only username present" do let(:uri) { "amqp://alpha@rabbitmq" } it "parses user and falls back to nil pass" do expect(subject[:user]).to eq("alpha") expect(subject[:pass]).to be_nil end context "with ':'" do let(:uri) { "amqp://alpha:@rabbitmq" } it "parses user and falls back to "" (empty) pass" do expect(subject[:user]).to eq("alpha") expect(subject[:pass]).to eq("") end end end context "only password present" do let(:uri) { "amqp://:beta@rabbitmq" } it "parses pass and falls back to "" (empty) user" do expect(subject[:user]).to eq("") expect(subject[:pass]).to eq("beta") end end context "both absent" do let(:uri) { "amqp://rabbitmq" } it "falls back to nil user and pass" do expect(subject[:user]).to be_nil expect(subject[:pass]).to be_nil end context "with ':'" do let(:uri) { "amqp://:@rabbitmq" } it "falls back to "" (empty) user and "" (empty) pass" do expect(subject[:user]).to eq("") expect(subject[:pass]).to eq("") end end end context "%-encoded" do let(:uri) { "amqp://%61lpha:bet%61@rabbitmq" } it "parses user and pass" do expect(subject[:user]).to eq("alpha") expect(subject[:pass]).to eq("beta") end end end describe "path" do context "present" do let(:uri) { "amqp://rabbitmq/staging" } it "parses vhost" do expect(subject[:vhost]).to eq("staging") end context "with dots" do let(:uri) { "amqp://rabbitmq/staging.critical.subsystem-a" } it "parses vhost" do expect(subject[:vhost]).to eq("staging.critical.subsystem-a") end end context "with slashes" do let(:uri) { "amqp://rabbitmq/staging/critical/subsystem-a" } it "raises ArgumentError" do expect { subject[:vhost] }.to raise_error(ArgumentError) end end context "with trailing slash" do let(:uri) { "amqp://rabbitmq/" } it "parses vhost as an empty string" do expect(subject[:vhost]).to eq("") end end context "with trailing %-encoded slashes" do let(:uri) { "amqp://rabbitmq/%2Fstaging" } it "parses vhost as string with leading slash" do expect(subject[:vhost]).to eq("/staging") end end context "%-encoded" do let(:uri) { "amqp://rabbitmq/%2Fstaging%2Fcritical%2Fsubsystem-a" } it "parses vhost as string with leading slash" do expect(subject[:vhost]).to eq("/staging/critical/subsystem-a") end end end context "absent" do let(:uri) { "amqp://rabbitmq" } it "falls back to default nil vhost" do expect(subject[:vhost]).to be_nil end end end describe "query parameters" do context "present" do let(:uri) { "amqp://rabbitmq?heartbeat=10&connection_timeout=100&channel_max=1000&auth_mechanism=plain&auth_mechanism=amqplain" } it "parses client connection parameters" do expect(subject[:heartbeat]).to eq(10) expect(subject[:connection_timeout]).to eq(100) expect(subject[:channel_max]).to eq(1000) expect(subject[:auth_mechanism]).to eq(["plain", "amqplain"]) end end context "absent" do let(:uri) { "amqp://rabbitmq" } it "falls back to default client connection parameters" do expect(subject[:heartbeat]).to be_nil expect(subject[:connection_timeout]).to be_nil expect(subject[:channel_max]).to be_nil expect(subject[:auth_mechanism]).to be_empty end end context "schema amqp" do context "tls parameters" do %w(verify fail_if_no_peer_cert cacertfile certfile keyfile).each do |tls_param| describe "#{tls_param}" do let(:uri) { "amqp://rabbitmq?#{tls_param}=value" } it "raises ArgumentError" do expect { subject }.to raise_error(ArgumentError, /The option '#{tls_param}' can only be used in URIs that use amqps schema/) end end end end end context "amqpa schema" do context "TLS parameters in query string" do context "case 1" do let(:uri) { "amqps://rabbitmq?verify=true&fail_if_no_peer_cert=true&cacertfile=/examples/tls/cacert.pem&certfile=/examples/tls/client_cert.pem&keyfile=/examples/tls/client_key.pem" } it "parses tls options" do expect(subject[:verify]).to be_truthy expect(subject[:fail_if_no_peer_cert]).to be_truthy expect(subject[:cacertfile]).to eq("/examples/tls/cacert.pem") expect(subject[:certfile]).to eq("/examples/tls/client_cert.pem") expect(subject[:keyfile]).to eq("/examples/tls/client_key.pem") end end context "case 2" do let(:uri) { "amqps://bunny_gem:bunny_password@/bunny_testbed?heartbeat=10&connection_timeout=100&channel_max=1000&verify=false&cacertfile=spec/tls/ca_certificate.pem&certfile=spec/tls/client_certificate.pem&keyfile=spec/tls/client_key.pem" } it "parses tls options" do expect(subject[:verify]).to be_falsey expect(subject[:fail_if_no_peer_cert]).to be_falsey expect(subject[:cacertfile]).to eq("spec/tls/ca_certificate.pem") expect(subject[:certfile]).to eq("spec/tls/client_certificate.pem") expect(subject[:keyfile]).to eq("spec/tls/client_key.pem") end end context "absent" do let(:uri) { "amqps://rabbitmq" } it "falls back to default tls options" do expect(subject[:verify]).to be_falsey expect(subject[:fail_if_no_peer_cert]).to be_falsey expect(subject[:cacertfile]).to be_nil expect(subject[:certfile]).to be_nil expect(subject[:keyfile]).to be_nil end end end end end end end amq-protocol-2.3.0/spec/amq/settings_spec.rb0000644000004100000410000000123413226473014021072 0ustar www-datawww-datarequire "amq/settings" RSpec.describe AMQ::Settings do describe ".default" do it "should provide some default values" do expect(AMQ::Settings.default).to_not be_nil expect(AMQ::Settings.default[:host]).to_not be_nil end end describe ".configure(&block)" do it "should merge custom settings with default settings" do settings = AMQ::Settings.configure(:host => "tagadab") expect(settings[:host]).to eql("tagadab") end it "should merge custom settings from AMQP URL with default settings" do settings = AMQ::Settings.configure("amqp://tagadab") expect(settings[:host]).to eql("tagadab") end end end amq-protocol-2.3.0/spec/spec_helper.rb0000644000004100000410000000067213226473014017740 0ustar www-datawww-data# encoding: binary require 'bundler/setup' Bundler.require(:test) begin require 'simplecov' SimpleCov.start do add_filter '/spec/' end rescue LoadError end $: << File.expand_path('../../lib', __FILE__) require "amq/protocol" puts "Running on #{RUBY_VERSION}" RSpec.configure do |config| config.include AMQ::Protocol config.filter_run_when_matching :focus config.disable_monkey_patching! config.warnings = true end amq-protocol-2.3.0/.travis.yml0000644000004100000410000000040113226473014016267 0ustar www-datawww-datalanguage: ruby bundler_args: --without development cache: bundler script: "bundle exec rspec spec" rvm: - ruby-head - "2.4.1" - "2.3.4" - "2.2.7" notifications: recipients: - michael@rabbitmq.com matrix: allow_failures: - rvm: ruby-head amq-protocol-2.3.0/lib/0000755000004100000410000000000013226473014014731 5ustar www-datawww-dataamq-protocol-2.3.0/lib/amq/0000755000004100000410000000000013226473014015507 5ustar www-datawww-dataamq-protocol-2.3.0/lib/amq/protocol.rb0000644000004100000410000000012613226473014017674 0ustar www-datawww-data# -*- coding: utf-8 -*- require "amq/protocol/version" require "amq/protocol/client" amq-protocol-2.3.0/lib/amq/bit_set.rb0000644000004100000410000000540313226473014017467 0ustar www-datawww-data# encoding: utf-8 module AMQ # Very minimalistic, pure Ruby implementation of bit set. Inspired by java.util.BitSet, # although significantly smaller in scope. # # Originally part of amqp gem. Extracted to make it possible for Bunny to use it. class BitSet attr_reader :words_in_use # # API # ADDRESS_BITS_PER_WORD = 6 BITS_PER_WORD = (1 << ADDRESS_BITS_PER_WORD) WORD_MASK = 0xffffffffffffffff # @param [Integer] Number of bits in the set # @api public def initialize(nbits) @nbits = nbits self.init_words(nbits) end # initialize(nbits) # Sets (flags) given bit. This method allows bits to be set more than once in a row, no exception will be raised. # # @param [Integer] A bit to set # @api public def set(i) check_range(i) w = self.word_index(i) result = @words[w] |= (1 << (i % BITS_PER_WORD)) result end # set(i) # Fetches flag value for given bit. # # @param [Integer] A bit to fetch # @return [Boolean] true if given bit is set, false otherwise # @api public def get(i) check_range(i) w = self.word_index(i) (@words[w] & (1 << i % BITS_PER_WORD)) != 0 end # get(i) alias [] get # Unsets (unflags) given bit. This method allows bits to be unset more than once in a row, no exception will be raised. # # @param [Integer] A bit to unset # @api public def unset(i) check_range(i) w = self.word_index(i) return if w.nil? result = @words[w] &= ~(1 << i % BITS_PER_WORD) result end # unset(i) # Clears all bits in the set # @api public def clear self.init_words(@nbits) end # clear def next_clear_bit() @words.each_with_index do |word, i| if word == WORD_MASK next end return i * BITS_PER_WORD + BitSet.number_of_trailing_ones(word) end -1 end # next_clear_bit def to_s result = "" @words.each do |w| result += w.to_s(2).rjust(BITS_PER_WORD,'0') + ":" end result end # to_s # # Implementation # # @private def self.number_of_trailing_ones(num) 0.upto(BITS_PER_WORD) do |bit| return bit if num[bit] == 0 end BITS_PER_WORD end # number_of_trailing_ones # @private def word_index(i) i >> ADDRESS_BITS_PER_WORD end # word_index protected # @private def init_words(nbits) n = word_index(nbits-1) + 1 @words = Array.new(n) { 0 } end # init_words def check_range(i) if i < 0 || i >= @nbits raise IndexError.new("Cannot access bit #{i} from a BitSet with #{@nbits} bits") end end # check_range end # BitSet end # AMQ amq-protocol-2.3.0/lib/amq/protocol/0000755000004100000410000000000013226473014017350 5ustar www-datawww-dataamq-protocol-2.3.0/lib/amq/protocol/table_value_decoder.rb0000644000004100000410000001302113226473014023642 0ustar www-datawww-data# encoding: binary require "amq/endianness" require "amq/protocol/type_constants" require "amq/protocol/float_32bit" module AMQ module Protocol class TableValueDecoder # # Behaviors # include TypeConstants # # API # def self.decode_array(data, initial_offset) array_length = data.slice(initial_offset, 4).unpack(PACK_UINT32).first ary = Array.new offset = initial_offset + 4 while offset <= (initial_offset + array_length) type, offset = decode_value_type(data, offset) i = case type when TYPE_STRING v, offset = decode_string(data, offset) v when TYPE_BYTE_ARRAY # Ruby doesn't have a direct counterpart to # ByteBuffer or byte[], so using a string feels # more appropriate than an array of fixnums v, offset = decode_string(data, offset) v when TYPE_INTEGER v, offset = decode_integer(data, offset) v when TYPE_DECIMAL v, offset = decode_big_decimal(data, offset) v when TYPE_TIME v, offset = decode_time(data, offset) v when TYPE_HASH v, offset = decode_hash(data, offset) v when TYPE_BOOLEAN v, offset = decode_boolean(data, offset) v when TYPE_BYTE then v, offset = decode_byte(data, offset) v when TYPE_SIGNED_16BIT then v, offset = decode_short(data, offset) v when TYPE_SIGNED_64BIT then v, offset = decode_long(data, offset) v when TYPE_32BIT_FLOAT then v, offset = decode_32bit_float(data, offset) v when TYPE_64BIT_FLOAT then v, offset = decode_64bit_float(data, offset) v when TYPE_VOID nil when TYPE_ARRAY v, offset = TableValueDecoder.decode_array(data, offset) v else raise ArgumentError.new("unsupported type in a table value: #{type.inspect}, do not know how to decode!") end ary << i end [ary, initial_offset + array_length + 4] end # self.decode_array(data, initial_offset) def self.decode_string(data, offset) length = data.slice(offset, 4).unpack(PACK_UINT32).first offset += 4 v = data.slice(offset, length) offset += length [v, offset] end # self.decode_string(data, offset) def self.decode_integer(data, offset) v = data.slice(offset, 4).unpack(PACK_UINT32).first offset += 4 [v, offset] end # self.decode_integer(data, offset) if AMQ::Endianness.big_endian? def self.decode_long(data, offset) v = data.slice(offset, 8).unpack(PACK_INT64) offset += 8 [v, offset] end else def self.decode_long(data, offset) slice = data.slice(offset, 8).bytes.to_a.reverse.map(&:chr).join v = slice.unpack(PACK_INT64).first offset += 8 [v, offset] end end def self.decode_big_decimal(data, offset) decimals, raw = data.slice(offset, 5).unpack(PACK_UCHAR_UINT32) offset += 5 v = BigDecimal.new(raw.to_s) * (BigDecimal.new(TEN) ** -decimals) [v, offset] end # self.decode_big_decimal(data, offset) def self.decode_time(data, offset) timestamp = data.slice(offset, 8).unpack(PACK_UINT64_BE).last v = Time.at(timestamp) offset += 8 [v, offset] end # self.decode_time(data, offset) def self.decode_boolean(data, offset) integer = data.slice(offset, 2).unpack(PACK_CHAR).first # 0 or 1 offset += 1 [(integer == 1), offset] end # self.decode_boolean(data, offset) def self.decode_32bit_float(data, offset) v = data.slice(offset, 4).unpack(PACK_32BIT_FLOAT).first offset += 4 [v, offset] end # self.decode_32bit_float(data, offset) def self.decode_64bit_float(data, offset) v = data.slice(offset, 8).unpack(PACK_64BIT_FLOAT).first offset += 8 [v, offset] end # self.decode_64bit_float(data, offset) def self.decode_value_type(data, offset) [data.slice(offset, 1), offset + 1] end # self.decode_value_type(data, offset) def self.decode_hash(data, offset) length = data.slice(offset, 4).unpack(PACK_UINT32).first v = Table.decode(data.slice(offset, length + 4)) offset += 4 + length [v, offset] end # self.decode_hash(data, offset) # Decodes/Converts a byte value from the data at the provided offset. # # @param [Array] data - A big-endian ordered array of bytes. # @param [Fixnum] offset - The offset which bytes the byte is consumed. # @return [Array] - The Fixnum value and new offset pair. def self.decode_byte(data, offset) [data.slice(offset, 1).unpack(PACK_CHAR).first, offset += 1] end def self.decode_short(data, offset) v = AMQ::Hacks.unpack_int16_big_endian(data.slice(offset, 2)).first offset += 2 [v, offset] end end # TableValueDecoder end # Protocol end # AMQ amq-protocol-2.3.0/lib/amq/protocol/float_32bit.rb0000644000004100000410000000046413226473014022011 0ustar www-datawww-datamodule AMQ module Protocol # Allows distinguishing between 32-bit and 64-bit floats in Ruby. # Useful in cases when RabbitMQ plugins encode # values as 32 bit numbers. class Float32Bit attr_reader :value def initialize(value) @value = value end end end end amq-protocol-2.3.0/lib/amq/protocol/table.rb0000644000004100000410000001111213226473014020760 0ustar www-datawww-data# encoding: binary require "amq/protocol/type_constants" require "amq/protocol/table_value_encoder" require "amq/protocol/table_value_decoder" module AMQ module Protocol class Table # # Behaviors # include TypeConstants # # API # class InvalidTableError < StandardError def initialize(key, value) super("Invalid table value on key #{key}: #{value.inspect} (#{value.class})") end end def self.encode(table) buffer = String.new table ||= {} table.each do |key, value| key = key.to_s # it can be a symbol as well buffer << key.bytesize.chr + key case value when Hash then buffer << TYPE_HASH buffer << self.encode(value) else buffer << TableValueEncoder.encode(value).force_encoding(buffer.encoding) end end [buffer.bytesize].pack(PACK_UINT32).force_encoding(buffer.encoding) + buffer end def self.decode(data) table = Hash.new table_length = data.unpack(PACK_UINT32).first return table if table_length.zero? offset = 4 while offset <= table_length key, offset = decode_table_key(data, offset) type, offset = TableValueDecoder.decode_value_type(data, offset) table[key] = case type when TYPE_STRING v, offset = TableValueDecoder.decode_string(data, offset) v when TYPE_BYTE_ARRAY # Ruby doesn't have a direct counterpart to # ByteBuffer or byte[], so using a string feels # more appropriate than an array of fixnums v, offset = TableValueDecoder.decode_string(data, offset) v when TYPE_INTEGER v, offset = TableValueDecoder.decode_integer(data, offset) v when TYPE_DECIMAL v, offset = TableValueDecoder.decode_big_decimal(data, offset) v when TYPE_TIME v, offset = TableValueDecoder.decode_time(data, offset) v when TYPE_HASH v, offset = TableValueDecoder.decode_hash(data, offset) v when TYPE_BOOLEAN v, offset = TableValueDecoder.decode_boolean(data, offset) v when TYPE_BYTE then v, offset = TableValueDecoder.decode_byte(data, offset) v when TYPE_SIGNED_16BIT then v, offset = TableValueDecoder.decode_short(data, offset) v when TYPE_SIGNED_64BIT then v, offset = TableValueDecoder.decode_long(data, offset) v when TYPE_32BIT_FLOAT then v, offset = TableValueDecoder.decode_32bit_float(data, offset) v when TYPE_64BIT_FLOAT then v, offset = TableValueDecoder.decode_64bit_float(data, offset) v when TYPE_VOID nil when TYPE_ARRAY v, offset = TableValueDecoder.decode_array(data, offset) v else raise ArgumentError, "Not a valid type: #{type.inspect}\nData: #{data.inspect}\nUnprocessed data: #{data[offset..-1].inspect}\nOffset: #{offset}\nTotal size: #{table_length}\nProcessed data: #{table.inspect}" end end table end # self.decode def self.length(data) data.unpack(PACK_UINT32).first end def self.hash_size(value) acc = 0 value.each do |k, v| acc += (1 + k.to_s.bytesize) acc += TableValueEncoder.field_value_size(v) end acc end # self.hash_size(value) def self.decode_table_key(data, offset) key_length = data.slice(offset, 1).unpack(PACK_CHAR).first offset += 1 key = data.slice(offset, key_length) offset += key_length [key, offset] end # self.decode_table_key(data, offset) end # Table end # Protocol end # AMQ amq-protocol-2.3.0/lib/amq/protocol/client.rb0000644000004100000410000020235713226473014021164 0ustar www-datawww-data# encoding: binary # THIS IS AN AUTOGENERATED FILE, DO NOT MODIFY # IT DIRECTLY ! FOR CHANGES, PLEASE UPDATE FILES # IN THE ./codegen DIRECTORY OF THE AMQ-PROTOCOL REPOSITORY. require "amq/pack" require "amq/protocol/table" require "amq/protocol/frame" require "amq/protocol/constants" require "amq/protocol/exceptions" module AMQ module Protocol PROTOCOL_VERSION = "0.9.1".freeze PREAMBLE = "AMQP\x00\x00\x09\x01".freeze DEFAULT_PORT = 5672 # @return [Array] Collection of subclasses of AMQ::Protocol::Class. def self.classes Protocol::Class.classes end # @return [Array] Collection of subclasses of AMQ::Protocol::Method. def self.methods Protocol::Method.methods end class ContentTooLarge < SoftError VALUE = 311 end class NoRoute < SoftError VALUE = 312 end class NoConsumers < SoftError VALUE = 313 end class AccessRefused < SoftError VALUE = 403 end class NotFound < SoftError VALUE = 404 end class ResourceLocked < SoftError VALUE = 405 end class PreconditionFailed < SoftError VALUE = 406 end class ConnectionForced < HardError VALUE = 320 end class InvalidPath < HardError VALUE = 402 end class FrameError < HardError VALUE = 501 end class SyntaxError < HardError VALUE = 502 end class CommandInvalid < HardError VALUE = 503 end class ChannelError < HardError VALUE = 504 end class UnexpectedFrame < HardError VALUE = 505 end class ResourceError < HardError VALUE = 506 end class NotAllowed < HardError VALUE = 530 end class NotImplemented < HardError VALUE = 540 end class InternalError < HardError VALUE = 541 end class Class @classes = Array.new def self.method_id @method_id end def self.name @name end def self.inherited(base) if self == Protocol::Class @classes << base end end def self.classes @classes end end class Method @methods = Array.new def self.method_id @method_id end def self.name @name end def self.index @index end def self.inherited(base) if self == Protocol::Method @methods << base end end def self.methods @methods end def self.split_headers(user_headers) properties, headers = {}, {} user_headers.each do |key, value| # key MUST be a symbol since symbols are not garbage-collected if Basic::PROPERTIES.include?(key) properties[key] = value else headers[key] = value end end return [properties, headers] end def self.encode_body(body, channel, frame_size) return [] if body.empty? # 8 = 1 + 2 + 4 + 1 # 1 byte of frame type # 2 bytes of channel number # 4 bytes of frame payload length # 1 byte of payload trailer FRAME_END byte limit = frame_size - 8 return [BodyFrame.new(body, channel)] if body.bytesize < limit # Otherwise String#slice on 1.9 will operate with code points, # and we need bytes. MK. body.force_encoding("ASCII-8BIT") if RUBY_VERSION.to_f >= 1.9 array = Array.new while body && !body.empty? payload, body = body[0, limit], body[limit, body.length - limit] array << BodyFrame.new(payload, channel) end array end def self.instantiate(*args, &block) self.new(*args, &block) end end class Connection < Protocol::Class @name = "connection" @method_id = 10 class Start < Protocol::Method @name = "connection.start" @method_id = 10 @index = 0x000A000A # 10, 10, 655370 @packed_indexes = [10, 10].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method version_major = data[offset, 1].unpack(PACK_CHAR).first offset += 1 version_minor = data[offset, 1].unpack(PACK_CHAR).first offset += 1 table_length = Table.length(data[offset, 4]) server_properties = Table.decode(data[offset, table_length + 4]) offset += table_length + 4 length = data[offset, 4].unpack(PACK_UINT32).first offset += 4 mechanisms = data[offset, length] offset += length length = data[offset, 4].unpack(PACK_UINT32).first offset += 4 locales = data[offset, length] offset += length self.new(version_major, version_minor, server_properties, mechanisms, locales) end attr_reader :version_major, :version_minor, :server_properties, :mechanisms, :locales def initialize(version_major, version_minor, server_properties, mechanisms, locales) @version_major = version_major @version_minor = version_minor @server_properties = server_properties @mechanisms = mechanisms @locales = locales end def self.has_content? false end end class StartOk < Protocol::Method @name = "connection.start-ok" @method_id = 11 @index = 0x000A000B # 10, 11, 655371 @packed_indexes = [10, 11].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['client_properties = nil', "mechanism = 'PLAIN'", 'response = nil', "locale = 'en_US'"] def self.encode(client_properties, mechanism, response, locale) channel = 0 buffer = @packed_indexes.dup buffer << AMQ::Protocol::Table.encode(client_properties) buffer << mechanism.to_s.bytesize.chr buffer << mechanism.to_s buffer << [response.to_s.bytesize].pack(PACK_UINT32) buffer << response.to_s buffer << locale.to_s.bytesize.chr buffer << locale.to_s MethodFrame.new(buffer, channel) end end class Secure < Protocol::Method @name = "connection.secure" @method_id = 20 @index = 0x000A0014 # 10, 20, 655380 @packed_indexes = [10, 20].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 4].unpack(PACK_UINT32).first offset += 4 challenge = data[offset, length] offset += length self.new(challenge) end attr_reader :challenge def initialize(challenge) @challenge = challenge end def self.has_content? false end end class SecureOk < Protocol::Method @name = "connection.secure-ok" @method_id = 21 @index = 0x000A0015 # 10, 21, 655381 @packed_indexes = [10, 21].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['response = nil'] def self.encode(response) channel = 0 buffer = @packed_indexes.dup buffer << [response.to_s.bytesize].pack(PACK_UINT32) buffer << response.to_s MethodFrame.new(buffer, channel) end end class Tune < Protocol::Method @name = "connection.tune" @method_id = 30 @index = 0x000A001E # 10, 30, 655390 @packed_indexes = [10, 30].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method channel_max = data[offset, 2].unpack(PACK_UINT16).first offset += 2 frame_max = data[offset, 4].unpack(PACK_UINT32).first offset += 4 heartbeat = data[offset, 2].unpack(PACK_UINT16).first offset += 2 self.new(channel_max, frame_max, heartbeat) end attr_reader :channel_max, :frame_max, :heartbeat def initialize(channel_max, frame_max, heartbeat) @channel_max = channel_max @frame_max = frame_max @heartbeat = heartbeat end def self.has_content? false end end class TuneOk < Protocol::Method @name = "connection.tune-ok" @method_id = 31 @index = 0x000A001F # 10, 31, 655391 @packed_indexes = [10, 31].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['channel_max = false', 'frame_max = false', 'heartbeat = false'] def self.encode(channel_max, frame_max, heartbeat) channel = 0 buffer = @packed_indexes.dup buffer << [channel_max].pack(PACK_UINT16) buffer << [frame_max].pack(PACK_UINT32) buffer << [heartbeat].pack(PACK_UINT16) MethodFrame.new(buffer, channel) end end class Open < Protocol::Method @name = "connection.open" @method_id = 40 @index = 0x000A0028 # 10, 40, 655400 @packed_indexes = [10, 40].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ["virtual_host = '/'", 'capabilities = EMPTY_STRING', 'insist = false'] def self.encode(virtual_host) capabilities = EMPTY_STRING insist = false channel = 0 buffer = @packed_indexes.dup buffer << virtual_host.to_s.bytesize.chr buffer << virtual_host.to_s buffer << capabilities.to_s.bytesize.chr buffer << capabilities.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if insist buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class OpenOk < Protocol::Method @name = "connection.open-ok" @method_id = 41 @index = 0x000A0029 # 10, 41, 655401 @packed_indexes = [10, 41].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 known_hosts = data[offset, length] offset += length self.new(known_hosts) end attr_reader :known_hosts def initialize(known_hosts) @known_hosts = known_hosts end def self.has_content? false end end class Close < Protocol::Method @name = "connection.close" @method_id = 50 @index = 0x000A0032 # 10, 50, 655410 @packed_indexes = [10, 50].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method reply_code = data[offset, 2].unpack(PACK_UINT16).first offset += 2 length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 reply_text = data[offset, length] offset += length class_id = data[offset, 2].unpack(PACK_UINT16).first offset += 2 method_id = data[offset, 2].unpack(PACK_UINT16).first offset += 2 self.new(reply_code, reply_text, class_id, method_id) end attr_reader :reply_code, :reply_text, :class_id, :method_id def initialize(reply_code, reply_text, class_id, method_id) @reply_code = reply_code @reply_text = reply_text @class_id = class_id @method_id = method_id end def self.has_content? false end # @return # ['reply_code = nil', 'reply_text = EMPTY_STRING', 'class_id = nil', 'method_id = nil'] def self.encode(reply_code, reply_text, class_id, method_id) channel = 0 buffer = @packed_indexes.dup buffer << [reply_code].pack(PACK_UINT16) buffer << reply_text.to_s.bytesize.chr buffer << reply_text.to_s buffer << [class_id].pack(PACK_UINT16) buffer << [method_id].pack(PACK_UINT16) MethodFrame.new(buffer, channel) end end class CloseOk < Protocol::Method @name = "connection.close-ok" @method_id = 51 @index = 0x000A0033 # 10, 51, 655411 @packed_indexes = [10, 51].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end # @return # [] def self.encode() channel = 0 buffer = @packed_indexes.dup MethodFrame.new(buffer, channel) end end class Blocked < Protocol::Method @name = "connection.blocked" @method_id = 60 @index = 0x000A003C # 10, 60, 655420 @packed_indexes = [10, 60].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 reason = data[offset, length] offset += length self.new(reason) end attr_reader :reason def initialize(reason) @reason = reason end def self.has_content? false end # @return # ['reason = EMPTY_STRING'] def self.encode(reason) channel = 0 buffer = @packed_indexes.dup buffer << reason.to_s.bytesize.chr buffer << reason.to_s MethodFrame.new(buffer, channel) end end class Unblocked < Protocol::Method @name = "connection.unblocked" @method_id = 61 @index = 0x000A003D # 10, 61, 655421 @packed_indexes = [10, 61].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end # @return # [] def self.encode() channel = 0 buffer = @packed_indexes.dup MethodFrame.new(buffer, channel) end end end class Channel < Protocol::Class @name = "channel" @method_id = 20 class Open < Protocol::Method @name = "channel.open" @method_id = 10 @index = 0x0014000A # 20, 10, 1310730 @packed_indexes = [20, 10].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['out_of_band = EMPTY_STRING'] def self.encode(channel, out_of_band) buffer = @packed_indexes.dup buffer << out_of_band.to_s.bytesize.chr buffer << out_of_band.to_s MethodFrame.new(buffer, channel) end end class OpenOk < Protocol::Method @name = "channel.open-ok" @method_id = 11 @index = 0x0014000B # 20, 11, 1310731 @packed_indexes = [20, 11].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 4].unpack(PACK_UINT32).first offset += 4 channel_id = data[offset, length] offset += length self.new(channel_id) end attr_reader :channel_id def initialize(channel_id) @channel_id = channel_id end def self.has_content? false end end class Flow < Protocol::Method @name = "channel.flow" @method_id = 20 @index = 0x00140014 # 20, 20, 1310740 @packed_indexes = [20, 20].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method bit_buffer = data[offset, 1].unpack(PACK_CHAR).first offset += 1 active = (bit_buffer & (1 << 0)) != 0 self.new(active) end attr_reader :active def initialize(active) @active = active end def self.has_content? false end # @return # ['active = nil'] def self.encode(channel, active) buffer = @packed_indexes.dup bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if active buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class FlowOk < Protocol::Method @name = "channel.flow-ok" @method_id = 21 @index = 0x00140015 # 20, 21, 1310741 @packed_indexes = [20, 21].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method bit_buffer = data[offset, 1].unpack(PACK_CHAR).first offset += 1 active = (bit_buffer & (1 << 0)) != 0 self.new(active) end attr_reader :active def initialize(active) @active = active end def self.has_content? false end # @return # ['active = nil'] def self.encode(channel, active) buffer = @packed_indexes.dup bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if active buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class Close < Protocol::Method @name = "channel.close" @method_id = 40 @index = 0x00140028 # 20, 40, 1310760 @packed_indexes = [20, 40].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method reply_code = data[offset, 2].unpack(PACK_UINT16).first offset += 2 length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 reply_text = data[offset, length] offset += length class_id = data[offset, 2].unpack(PACK_UINT16).first offset += 2 method_id = data[offset, 2].unpack(PACK_UINT16).first offset += 2 self.new(reply_code, reply_text, class_id, method_id) end attr_reader :reply_code, :reply_text, :class_id, :method_id def initialize(reply_code, reply_text, class_id, method_id) @reply_code = reply_code @reply_text = reply_text @class_id = class_id @method_id = method_id end def self.has_content? false end # @return # ['reply_code = nil', 'reply_text = EMPTY_STRING', 'class_id = nil', 'method_id = nil'] def self.encode(channel, reply_code, reply_text, class_id, method_id) buffer = @packed_indexes.dup buffer << [reply_code].pack(PACK_UINT16) buffer << reply_text.to_s.bytesize.chr buffer << reply_text.to_s buffer << [class_id].pack(PACK_UINT16) buffer << [method_id].pack(PACK_UINT16) MethodFrame.new(buffer, channel) end end class CloseOk < Protocol::Method @name = "channel.close-ok" @method_id = 41 @index = 0x00140029 # 20, 41, 1310761 @packed_indexes = [20, 41].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end # @return # [] def self.encode(channel) buffer = @packed_indexes.dup MethodFrame.new(buffer, channel) end end end class Exchange < Protocol::Class @name = "exchange" @method_id = 40 class Declare < Protocol::Method @name = "exchange.declare" @method_id = 10 @index = 0x0028000A # 40, 10, 2621450 @packed_indexes = [40, 10].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'exchange = nil', "type = 'direct'", 'passive = false', 'durable = false', 'auto_delete = false', 'internal = false', 'nowait = false', 'arguments = {}'] def self.encode(channel, exchange, type, passive, durable, auto_delete, internal, nowait, arguments) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << exchange.to_s.bytesize.chr buffer << exchange.to_s buffer << type.to_s.bytesize.chr buffer << type.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if passive bit_buffer = bit_buffer | (1 << 1) if durable bit_buffer = bit_buffer | (1 << 2) if auto_delete bit_buffer = bit_buffer | (1 << 3) if internal bit_buffer = bit_buffer | (1 << 4) if nowait buffer << [bit_buffer].pack(PACK_CHAR) buffer << AMQ::Protocol::Table.encode(arguments) MethodFrame.new(buffer, channel) end end class DeclareOk < Protocol::Method @name = "exchange.declare-ok" @method_id = 11 @index = 0x0028000B # 40, 11, 2621451 @packed_indexes = [40, 11].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end class Delete < Protocol::Method @name = "exchange.delete" @method_id = 20 @index = 0x00280014 # 40, 20, 2621460 @packed_indexes = [40, 20].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'exchange = nil', 'if_unused = false', 'nowait = false'] def self.encode(channel, exchange, if_unused, nowait) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << exchange.to_s.bytesize.chr buffer << exchange.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if if_unused bit_buffer = bit_buffer | (1 << 1) if nowait buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class DeleteOk < Protocol::Method @name = "exchange.delete-ok" @method_id = 21 @index = 0x00280015 # 40, 21, 2621461 @packed_indexes = [40, 21].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end class Bind < Protocol::Method @name = "exchange.bind" @method_id = 30 @index = 0x0028001E # 40, 30, 2621470 @packed_indexes = [40, 30].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'destination = nil', 'source = nil', 'routing_key = EMPTY_STRING', 'nowait = false', 'arguments = {}'] def self.encode(channel, destination, source, routing_key, nowait, arguments) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << destination.to_s.bytesize.chr buffer << destination.to_s buffer << source.to_s.bytesize.chr buffer << source.to_s buffer << routing_key.to_s.bytesize.chr buffer << routing_key.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if nowait buffer << [bit_buffer].pack(PACK_CHAR) buffer << AMQ::Protocol::Table.encode(arguments) MethodFrame.new(buffer, channel) end end class BindOk < Protocol::Method @name = "exchange.bind-ok" @method_id = 31 @index = 0x0028001F # 40, 31, 2621471 @packed_indexes = [40, 31].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end class Unbind < Protocol::Method @name = "exchange.unbind" @method_id = 40 @index = 0x00280028 # 40, 40, 2621480 @packed_indexes = [40, 40].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'destination = nil', 'source = nil', 'routing_key = EMPTY_STRING', 'nowait = false', 'arguments = {}'] def self.encode(channel, destination, source, routing_key, nowait, arguments) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << destination.to_s.bytesize.chr buffer << destination.to_s buffer << source.to_s.bytesize.chr buffer << source.to_s buffer << routing_key.to_s.bytesize.chr buffer << routing_key.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if nowait buffer << [bit_buffer].pack(PACK_CHAR) buffer << AMQ::Protocol::Table.encode(arguments) MethodFrame.new(buffer, channel) end end class UnbindOk < Protocol::Method @name = "exchange.unbind-ok" @method_id = 51 @index = 0x00280033 # 40, 51, 2621491 @packed_indexes = [40, 51].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end end class Queue < Protocol::Class @name = "queue" @method_id = 50 class Declare < Protocol::Method @name = "queue.declare" @method_id = 10 @index = 0x0032000A # 50, 10, 3276810 @packed_indexes = [50, 10].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'queue = EMPTY_STRING', 'passive = false', 'durable = false', 'exclusive = false', 'auto_delete = false', 'nowait = false', 'arguments = {}'] def self.encode(channel, queue, passive, durable, exclusive, auto_delete, nowait, arguments) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << queue.to_s.bytesize.chr buffer << queue.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if passive bit_buffer = bit_buffer | (1 << 1) if durable bit_buffer = bit_buffer | (1 << 2) if exclusive bit_buffer = bit_buffer | (1 << 3) if auto_delete bit_buffer = bit_buffer | (1 << 4) if nowait buffer << [bit_buffer].pack(PACK_CHAR) buffer << AMQ::Protocol::Table.encode(arguments) MethodFrame.new(buffer, channel) end end class DeclareOk < Protocol::Method @name = "queue.declare-ok" @method_id = 11 @index = 0x0032000B # 50, 11, 3276811 @packed_indexes = [50, 11].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 queue = data[offset, length] offset += length message_count = data[offset, 4].unpack(PACK_UINT32).first offset += 4 consumer_count = data[offset, 4].unpack(PACK_UINT32).first offset += 4 self.new(queue, message_count, consumer_count) end attr_reader :queue, :message_count, :consumer_count def initialize(queue, message_count, consumer_count) @queue = queue @message_count = message_count @consumer_count = consumer_count end def self.has_content? false end end class Bind < Protocol::Method @name = "queue.bind" @method_id = 20 @index = 0x00320014 # 50, 20, 3276820 @packed_indexes = [50, 20].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'queue = EMPTY_STRING', 'exchange = nil', 'routing_key = EMPTY_STRING', 'nowait = false', 'arguments = {}'] def self.encode(channel, queue, exchange, routing_key, nowait, arguments) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << queue.to_s.bytesize.chr buffer << queue.to_s buffer << exchange.to_s.bytesize.chr buffer << exchange.to_s buffer << routing_key.to_s.bytesize.chr buffer << routing_key.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if nowait buffer << [bit_buffer].pack(PACK_CHAR) buffer << AMQ::Protocol::Table.encode(arguments) MethodFrame.new(buffer, channel) end end class BindOk < Protocol::Method @name = "queue.bind-ok" @method_id = 21 @index = 0x00320015 # 50, 21, 3276821 @packed_indexes = [50, 21].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end class Purge < Protocol::Method @name = "queue.purge" @method_id = 30 @index = 0x0032001E # 50, 30, 3276830 @packed_indexes = [50, 30].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'queue = EMPTY_STRING', 'nowait = false'] def self.encode(channel, queue, nowait) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << queue.to_s.bytesize.chr buffer << queue.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if nowait buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class PurgeOk < Protocol::Method @name = "queue.purge-ok" @method_id = 31 @index = 0x0032001F # 50, 31, 3276831 @packed_indexes = [50, 31].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method message_count = data[offset, 4].unpack(PACK_UINT32).first offset += 4 self.new(message_count) end attr_reader :message_count def initialize(message_count) @message_count = message_count end def self.has_content? false end end class Delete < Protocol::Method @name = "queue.delete" @method_id = 40 @index = 0x00320028 # 50, 40, 3276840 @packed_indexes = [50, 40].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'queue = EMPTY_STRING', 'if_unused = false', 'if_empty = false', 'nowait = false'] def self.encode(channel, queue, if_unused, if_empty, nowait) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << queue.to_s.bytesize.chr buffer << queue.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if if_unused bit_buffer = bit_buffer | (1 << 1) if if_empty bit_buffer = bit_buffer | (1 << 2) if nowait buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class DeleteOk < Protocol::Method @name = "queue.delete-ok" @method_id = 41 @index = 0x00320029 # 50, 41, 3276841 @packed_indexes = [50, 41].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method message_count = data[offset, 4].unpack(PACK_UINT32).first offset += 4 self.new(message_count) end attr_reader :message_count def initialize(message_count) @message_count = message_count end def self.has_content? false end end class Unbind < Protocol::Method @name = "queue.unbind" @method_id = 50 @index = 0x00320032 # 50, 50, 3276850 @packed_indexes = [50, 50].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'queue = EMPTY_STRING', 'exchange = nil', 'routing_key = EMPTY_STRING', 'arguments = {}'] def self.encode(channel, queue, exchange, routing_key, arguments) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << queue.to_s.bytesize.chr buffer << queue.to_s buffer << exchange.to_s.bytesize.chr buffer << exchange.to_s buffer << routing_key.to_s.bytesize.chr buffer << routing_key.to_s buffer << AMQ::Protocol::Table.encode(arguments) MethodFrame.new(buffer, channel) end end class UnbindOk < Protocol::Method @name = "queue.unbind-ok" @method_id = 51 @index = 0x00320033 # 50, 51, 3276851 @packed_indexes = [50, 51].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end end class Basic < Protocol::Class @name = "basic" @method_id = 60 PROPERTIES = [ :content_type, # shortstr :content_encoding, # shortstr :headers, # table :delivery_mode, # octet :priority, # octet :correlation_id, # shortstr :reply_to, # shortstr :expiration, # shortstr :message_id, # shortstr :timestamp, # timestamp :type, # shortstr :user_id, # shortstr :app_id, # shortstr :cluster_id, # shortstr ] # 1 << 15 def self.encode_content_type(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [0, 0x8000, buffer] end # 1 << 14 def self.encode_content_encoding(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [1, 0x4000, buffer] end # 1 << 13 def self.encode_headers(value) buffer = '' buffer << AMQ::Protocol::Table.encode(value) [2, 0x2000, buffer] end # 1 << 12 def self.encode_delivery_mode(value) buffer = '' buffer << [value].pack(PACK_CHAR) [3, 0x1000, buffer] end # 1 << 11 def self.encode_priority(value) buffer = '' buffer << [value].pack(PACK_CHAR) [4, 0x0800, buffer] end # 1 << 10 def self.encode_correlation_id(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [5, 0x0400, buffer] end # 1 << 9 def self.encode_reply_to(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [6, 0x0200, buffer] end # 1 << 8 def self.encode_expiration(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [7, 0x0100, buffer] end # 1 << 7 def self.encode_message_id(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [8, 0x0080, buffer] end # 1 << 6 def self.encode_timestamp(value) buffer = '' buffer << AMQ::Pack.pack_uint64_big_endian(value) [9, 0x0040, buffer] end # 1 << 5 def self.encode_type(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [10, 0x0020, buffer] end # 1 << 4 def self.encode_user_id(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [11, 0x0010, buffer] end # 1 << 3 def self.encode_app_id(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [12, 0x0008, buffer] end # 1 << 2 def self.encode_cluster_id(value) buffer = '' buffer << value.to_s.bytesize.chr buffer << value.to_s [13, 0x0004, buffer] end def self.encode_properties(body_size, properties) pieces, flags = [], 0 properties.reject {|key, value| value.nil?}.each do |key, value| i, f, result = self.__send__(:"encode_#{key}", value) flags |= f pieces[i] = result end # result = [60, 0, body_size, flags].pack('n2Qn') result = [60, 0].pack(PACK_UINT16_X2) result += AMQ::Pack.pack_uint64_big_endian(body_size) result += [flags].pack(PACK_UINT16) pieces_joined = pieces.join(EMPTY_STRING) result.force_encoding(pieces_joined.encoding) + pieces_joined end # THIS DECODES ONLY FLAGS DECODE_PROPERTIES = { 0x8000 => :content_type, 0x4000 => :content_encoding, 0x2000 => :headers, 0x1000 => :delivery_mode, 0x0800 => :priority, 0x0400 => :correlation_id, 0x0200 => :reply_to, 0x0100 => :expiration, 0x0080 => :message_id, 0x0040 => :timestamp, 0x0020 => :type, 0x0010 => :user_id, 0x0008 => :app_id, 0x0004 => :cluster_id, } DECODE_PROPERTIES_TYPE = { 0x8000 => :shortstr, 0x4000 => :shortstr, 0x2000 => :table, 0x1000 => :octet, 0x0800 => :octet, 0x0400 => :shortstr, 0x0200 => :shortstr, 0x0100 => :shortstr, 0x0080 => :shortstr, 0x0040 => :timestamp, 0x0020 => :shortstr, 0x0010 => :shortstr, 0x0008 => :shortstr, 0x0004 => :shortstr, } # Hash doesn't give any guarantees on keys order, we will do it in a # straightforward way DECODE_PROPERTIES_KEYS = [ 0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100, 0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, ] def self.decode_properties(data) offset, data_length, properties = 0, data.bytesize, {} compressed_index = data[offset, 2].unpack(PACK_UINT16)[0] offset += 2 while data_length > offset DECODE_PROPERTIES_KEYS.each do |key| next unless compressed_index >= key compressed_index -= key name = DECODE_PROPERTIES[key] || raise(RuntimeError.new("No property found for index #{index.inspect}!")) case DECODE_PROPERTIES_TYPE[key] when :shortstr size = data[offset, 1].unpack(PACK_CHAR)[0] offset += 1 result = data[offset, size] when :octet size = 1 result = data[offset, size].unpack(PACK_CHAR).first when :timestamp size = 8 result = Time.at(data[offset, size].unpack(PACK_UINT64_BE).last) when :table size = 4 + data[offset, 4].unpack(PACK_UINT32)[0] result = Table.decode(data[offset, size]) end properties[name] = result offset += size end end properties end class Qos < Protocol::Method @name = "basic.qos" @method_id = 10 @index = 0x003C000A # 60, 10, 3932170 @packed_indexes = [60, 10].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['prefetch_size = false', 'prefetch_count = false', 'global = false'] def self.encode(channel, prefetch_size, prefetch_count, global) buffer = @packed_indexes.dup buffer << [prefetch_size].pack(PACK_UINT32) buffer << [prefetch_count].pack(PACK_UINT16) bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if global buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class QosOk < Protocol::Method @name = "basic.qos-ok" @method_id = 11 @index = 0x003C000B # 60, 11, 3932171 @packed_indexes = [60, 11].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end class Consume < Protocol::Method @name = "basic.consume" @method_id = 20 @index = 0x003C0014 # 60, 20, 3932180 @packed_indexes = [60, 20].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'queue = EMPTY_STRING', 'consumer_tag = EMPTY_STRING', 'no_local = false', 'no_ack = false', 'exclusive = false', 'nowait = false', 'arguments = {}'] def self.encode(channel, queue, consumer_tag, no_local, no_ack, exclusive, nowait, arguments) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << queue.to_s.bytesize.chr buffer << queue.to_s buffer << consumer_tag.to_s.bytesize.chr buffer << consumer_tag.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if no_local bit_buffer = bit_buffer | (1 << 1) if no_ack bit_buffer = bit_buffer | (1 << 2) if exclusive bit_buffer = bit_buffer | (1 << 3) if nowait buffer << [bit_buffer].pack(PACK_CHAR) buffer << AMQ::Protocol::Table.encode(arguments) MethodFrame.new(buffer, channel) end end class ConsumeOk < Protocol::Method @name = "basic.consume-ok" @method_id = 21 @index = 0x003C0015 # 60, 21, 3932181 @packed_indexes = [60, 21].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 consumer_tag = data[offset, length] offset += length self.new(consumer_tag) end attr_reader :consumer_tag def initialize(consumer_tag) @consumer_tag = consumer_tag end def self.has_content? false end end class Cancel < Protocol::Method @name = "basic.cancel" @method_id = 30 @index = 0x003C001E # 60, 30, 3932190 @packed_indexes = [60, 30].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 consumer_tag = data[offset, length] offset += length bit_buffer = data[offset, 1].unpack(PACK_CHAR).first offset += 1 nowait = (bit_buffer & (1 << 0)) != 0 self.new(consumer_tag, nowait) end attr_reader :consumer_tag, :nowait def initialize(consumer_tag, nowait) @consumer_tag = consumer_tag @nowait = nowait end def self.has_content? false end # @return # ['consumer_tag = nil', 'nowait = false'] def self.encode(channel, consumer_tag, nowait) buffer = @packed_indexes.dup buffer << consumer_tag.to_s.bytesize.chr buffer << consumer_tag.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if nowait buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class CancelOk < Protocol::Method @name = "basic.cancel-ok" @method_id = 31 @index = 0x003C001F # 60, 31, 3932191 @packed_indexes = [60, 31].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 consumer_tag = data[offset, length] offset += length self.new(consumer_tag) end attr_reader :consumer_tag def initialize(consumer_tag) @consumer_tag = consumer_tag end def self.has_content? false end end class Publish < Protocol::Method @name = "basic.publish" @method_id = 40 @index = 0x003C0028 # 60, 40, 3932200 @packed_indexes = [60, 40].pack(PACK_UINT16_X2).freeze def self.has_content? true end # @return # ['ticket = 0', 'exchange = EMPTY_STRING', 'routing_key = EMPTY_STRING', 'mandatory = false', 'immediate = false', 'user_headers = nil', 'payload = ""', 'frame_size = nil'] def self.encode(channel, payload, user_headers, exchange, routing_key, mandatory, immediate, frame_size) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << exchange.to_s.bytesize.chr buffer << exchange.to_s buffer << routing_key.to_s.bytesize.chr buffer << routing_key.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if mandatory bit_buffer = bit_buffer | (1 << 1) if immediate buffer << [bit_buffer].pack(PACK_CHAR) frames = [MethodFrame.new(buffer, channel)] properties, _headers = self.split_headers(user_headers) if properties.nil? or properties.empty? raise RuntimeError.new("Properties can not be empty!") end properties_payload = Basic.encode_properties(payload.bytesize, properties) frames << HeaderFrame.new(properties_payload, channel) frames += self.encode_body(payload, channel, frame_size) frames end end class Return < Protocol::Method @name = "basic.return" @method_id = 50 @index = 0x003C0032 # 60, 50, 3932210 @packed_indexes = [60, 50].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method reply_code = data[offset, 2].unpack(PACK_UINT16).first offset += 2 length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 reply_text = data[offset, length] offset += length length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 exchange = data[offset, length] offset += length length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 routing_key = data[offset, length] offset += length self.new(reply_code, reply_text, exchange, routing_key) end attr_reader :reply_code, :reply_text, :exchange, :routing_key def initialize(reply_code, reply_text, exchange, routing_key) @reply_code = reply_code @reply_text = reply_text @exchange = exchange @routing_key = routing_key end def self.has_content? true end end class Deliver < Protocol::Method @name = "basic.deliver" @method_id = 60 @index = 0x003C003C # 60, 60, 3932220 @packed_indexes = [60, 60].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 consumer_tag = data[offset, length] offset += length delivery_tag = AMQ::Pack.unpack_uint64_big_endian(data[offset, 8]).first offset += 8 bit_buffer = data[offset, 1].unpack(PACK_CHAR).first offset += 1 redelivered = (bit_buffer & (1 << 0)) != 0 length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 exchange = data[offset, length] offset += length length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 routing_key = data[offset, length] offset += length self.new(consumer_tag, delivery_tag, redelivered, exchange, routing_key) end attr_reader :consumer_tag, :delivery_tag, :redelivered, :exchange, :routing_key def initialize(consumer_tag, delivery_tag, redelivered, exchange, routing_key) @consumer_tag = consumer_tag @delivery_tag = delivery_tag @redelivered = redelivered @exchange = exchange @routing_key = routing_key end def self.has_content? true end end class Get < Protocol::Method @name = "basic.get" @method_id = 70 @index = 0x003C0046 # 60, 70, 3932230 @packed_indexes = [60, 70].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['ticket = 0', 'queue = EMPTY_STRING', 'no_ack = false'] def self.encode(channel, queue, no_ack) ticket = 0 buffer = @packed_indexes.dup buffer << [ticket].pack(PACK_UINT16) buffer << queue.to_s.bytesize.chr buffer << queue.to_s bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if no_ack buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class GetOk < Protocol::Method @name = "basic.get-ok" @method_id = 71 @index = 0x003C0047 # 60, 71, 3932231 @packed_indexes = [60, 71].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method delivery_tag = AMQ::Pack.unpack_uint64_big_endian(data[offset, 8]).first offset += 8 bit_buffer = data[offset, 1].unpack(PACK_CHAR).first offset += 1 redelivered = (bit_buffer & (1 << 0)) != 0 length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 exchange = data[offset, length] offset += length length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 routing_key = data[offset, length] offset += length message_count = data[offset, 4].unpack(PACK_UINT32).first offset += 4 self.new(delivery_tag, redelivered, exchange, routing_key, message_count) end attr_reader :delivery_tag, :redelivered, :exchange, :routing_key, :message_count def initialize(delivery_tag, redelivered, exchange, routing_key, message_count) @delivery_tag = delivery_tag @redelivered = redelivered @exchange = exchange @routing_key = routing_key @message_count = message_count end def self.has_content? true end end class GetEmpty < Protocol::Method @name = "basic.get-empty" @method_id = 72 @index = 0x003C0048 # 60, 72, 3932232 @packed_indexes = [60, 72].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method length = data[offset, 1].unpack(PACK_CHAR).first offset += 1 cluster_id = data[offset, length] offset += length self.new(cluster_id) end attr_reader :cluster_id def initialize(cluster_id) @cluster_id = cluster_id end def self.has_content? false end end class Ack < Protocol::Method @name = "basic.ack" @method_id = 80 @index = 0x003C0050 # 60, 80, 3932240 @packed_indexes = [60, 80].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method delivery_tag = AMQ::Pack.unpack_uint64_big_endian(data[offset, 8]).first offset += 8 bit_buffer = data[offset, 1].unpack(PACK_CHAR).first offset += 1 multiple = (bit_buffer & (1 << 0)) != 0 self.new(delivery_tag, multiple) end attr_reader :delivery_tag, :multiple def initialize(delivery_tag, multiple) @delivery_tag = delivery_tag @multiple = multiple end def self.has_content? false end # @return # ['delivery_tag = false', 'multiple = false'] def self.encode(channel, delivery_tag, multiple) buffer = @packed_indexes.dup buffer << AMQ::Pack.pack_uint64_big_endian(delivery_tag) bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if multiple buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class Reject < Protocol::Method @name = "basic.reject" @method_id = 90 @index = 0x003C005A # 60, 90, 3932250 @packed_indexes = [60, 90].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['delivery_tag = nil', 'requeue = true'] def self.encode(channel, delivery_tag, requeue) buffer = @packed_indexes.dup buffer << AMQ::Pack.pack_uint64_big_endian(delivery_tag) bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if requeue buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class RecoverAsync < Protocol::Method @name = "basic.recover-async" @method_id = 100 @index = 0x003C0064 # 60, 100, 3932260 @packed_indexes = [60, 100].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['requeue = false'] def self.encode(channel, requeue) buffer = @packed_indexes.dup bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if requeue buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class Recover < Protocol::Method @name = "basic.recover" @method_id = 110 @index = 0x003C006E # 60, 110, 3932270 @packed_indexes = [60, 110].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # ['requeue = false'] def self.encode(channel, requeue) buffer = @packed_indexes.dup bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if requeue buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class RecoverOk < Protocol::Method @name = "basic.recover-ok" @method_id = 111 @index = 0x003C006F # 60, 111, 3932271 @packed_indexes = [60, 111].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end class Nack < Protocol::Method @name = "basic.nack" @method_id = 120 @index = 0x003C0078 # 60, 120, 3932280 @packed_indexes = [60, 120].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method delivery_tag = AMQ::Pack.unpack_uint64_big_endian(data[offset, 8]).first offset += 8 bit_buffer = data[offset, 1].unpack(PACK_CHAR).first offset += 1 multiple = (bit_buffer & (1 << 0)) != 0 requeue = (bit_buffer & (1 << 1)) != 0 self.new(delivery_tag, multiple, requeue) end attr_reader :delivery_tag, :multiple, :requeue def initialize(delivery_tag, multiple, requeue) @delivery_tag = delivery_tag @multiple = multiple @requeue = requeue end def self.has_content? false end # @return # ['delivery_tag = false', 'multiple = false', 'requeue = true'] def self.encode(channel, delivery_tag, multiple, requeue) buffer = @packed_indexes.dup buffer << AMQ::Pack.pack_uint64_big_endian(delivery_tag) bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if multiple bit_buffer = bit_buffer | (1 << 1) if requeue buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end end class Tx < Protocol::Class @name = "tx" @method_id = 90 class Select < Protocol::Method @name = "tx.select" @method_id = 10 @index = 0x005A000A # 90, 10, 5898250 @packed_indexes = [90, 10].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # [] def self.encode(channel) buffer = @packed_indexes.dup MethodFrame.new(buffer, channel) end end class SelectOk < Protocol::Method @name = "tx.select-ok" @method_id = 11 @index = 0x005A000B # 90, 11, 5898251 @packed_indexes = [90, 11].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end class Commit < Protocol::Method @name = "tx.commit" @method_id = 20 @index = 0x005A0014 # 90, 20, 5898260 @packed_indexes = [90, 20].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # [] def self.encode(channel) buffer = @packed_indexes.dup MethodFrame.new(buffer, channel) end end class CommitOk < Protocol::Method @name = "tx.commit-ok" @method_id = 21 @index = 0x005A0015 # 90, 21, 5898261 @packed_indexes = [90, 21].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end class Rollback < Protocol::Method @name = "tx.rollback" @method_id = 30 @index = 0x005A001E # 90, 30, 5898270 @packed_indexes = [90, 30].pack(PACK_UINT16_X2).freeze def self.has_content? false end # @return # [] def self.encode(channel) buffer = @packed_indexes.dup MethodFrame.new(buffer, channel) end end class RollbackOk < Protocol::Method @name = "tx.rollback-ok" @method_id = 31 @index = 0x005A001F # 90, 31, 5898271 @packed_indexes = [90, 31].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end end end class Confirm < Protocol::Class @name = "confirm" @method_id = 85 class Select < Protocol::Method @name = "confirm.select" @method_id = 10 @index = 0x0055000A # 85, 10, 5570570 @packed_indexes = [85, 10].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method bit_buffer = data[offset, 1].unpack(PACK_CHAR).first offset += 1 nowait = (bit_buffer & (1 << 0)) != 0 self.new(nowait) end attr_reader :nowait def initialize(nowait) @nowait = nowait end def self.has_content? false end # @return # ['nowait = false'] def self.encode(channel, nowait) buffer = @packed_indexes.dup bit_buffer = 0 bit_buffer = bit_buffer | (1 << 0) if nowait buffer << [bit_buffer].pack(PACK_CHAR) MethodFrame.new(buffer, channel) end end class SelectOk < Protocol::Method @name = "confirm.select-ok" @method_id = 11 @index = 0x0055000B # 85, 11, 5570571 @packed_indexes = [85, 11].pack(PACK_UINT16_X2).freeze # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method self.new() end def initialize() end def self.has_content? false end # @return # [] def self.encode(channel) buffer = @packed_indexes.dup MethodFrame.new(buffer, channel) end end end METHODS = begin Method.methods.inject(Hash.new) do |hash, klass| hash.merge!(klass.index => klass) end end end end amq-protocol-2.3.0/lib/amq/protocol/version.rb0000644000004100000410000000011613226473014021360 0ustar www-datawww-datamodule AMQ module Protocol VERSION = "2.3.0" end # Protocol end # AMQ amq-protocol-2.3.0/lib/amq/protocol/type_constants.rb0000644000004100000410000000136213226473014022754 0ustar www-datawww-data# encoding: binary module AMQ module Protocol module TypeConstants TYPE_STRING = 'S'.freeze TYPE_INTEGER = 'I'.freeze TYPE_TIME = 'T'.freeze TYPE_DECIMAL = 'D'.freeze TYPE_HASH = 'F'.freeze TYPE_ARRAY = 'A'.freeze TYPE_BYTE = 'b'.freeze TYPE_64BIT_FLOAT = 'd'.freeze TYPE_32BIT_FLOAT = 'f'.freeze TYPE_SIGNED_64BIT = 'l'.freeze TYPE_SIGNED_16BIT = 's'.freeze TYPE_BOOLEAN = 't'.freeze TYPE_BYTE_ARRAY = 'x'.freeze TYPE_VOID = 'V'.freeze TEN = '10'.freeze BOOLEAN_TRUE = "\x01".freeze BOOLEAN_FALSE = "\x00".freeze end # TypeConstants end # Protocol end # AMQ amq-protocol-2.3.0/lib/amq/protocol/table_value_encoder.rb0000644000004100000410000000634613226473014023670 0ustar www-datawww-data# encoding: binary require "amq/protocol/type_constants" require "date" require "amq/protocol/float_32bit" module AMQ module Protocol class TableValueEncoder # # Behaviors # include TypeConstants # # API # def self.encode(value) accumulator = String.new case value when String then accumulator << TYPE_STRING accumulator << [value.bytesize].pack(PACK_UINT32) accumulator << value when Symbol then str = value.to_s accumulator << TYPE_STRING accumulator << [str.bytesize].pack(PACK_UINT32) accumulator << str when Integer then accumulator << TYPE_SIGNED_64BIT accumulator << [value].pack(PACK_INT64_BE) when AMQ::Protocol::Float32Bit then accumulator << TYPE_32BIT_FLOAT accumulator << [value.value].pack(PACK_32BIT_FLOAT) when Float then accumulator << TYPE_64BIT_FLOAT accumulator << [value].pack(PACK_64BIT_FLOAT) when true, false then accumulator << TYPE_BOOLEAN accumulator << (value ? BOOLEAN_TRUE : BOOLEAN_FALSE) when Time then accumulator << TYPE_TIME accumulator << [value.to_i].pack(PACK_INT64_BE) when nil then accumulator << TYPE_VOID when Array then accumulator << TYPE_ARRAY accumulator << [self.array_size(value)].pack(PACK_UINT32) value.each { |v| accumulator << self.encode(v) } when Hash then accumulator << TYPE_HASH accumulator << AMQ::Protocol::Table.encode(value) else # We don't want to require these libraries. if defined?(BigDecimal) && value.is_a?(BigDecimal) accumulator << TYPE_DECIMAL if value.exponent < 0 decimals = -value.exponent raw = (value * (decimals ** 10)).to_i accumulator << [decimals + 1, raw].pack(PACK_UCHAR_UINT32) # somewhat like floating point else # per spec, the "decimals" octet is unsigned (!) accumulator << [0, value.to_i].pack(PACK_UCHAR_UINT32) end else raise ArgumentError.new("Unsupported value #{value.inspect} of type #{value.class.name}") end # if end # case accumulator end # self.encode(value) def self.field_value_size(value) # the type tag takes 1 byte acc = 1 case value when String then acc += (value.bytesize + 4) when Integer then acc += 8 when Float then acc += 8 when Time, DateTime then acc += 8 when true, false then acc += 1 when nil then # nothing, type tag alone is enough when Hash then acc += (4 + Table.hash_size(value)) when Array then acc += (4 + self.array_size(value)) end acc end # self.field_value_size(value) def self.array_size(value) acc = 0 value.each { |v| acc += self.field_value_size(v) } acc end # self.array_size(value) end # TableValueEncoder end # Protocol end # AMQ amq-protocol-2.3.0/lib/amq/protocol/constants.rb0000644000004100000410000000125113226473014021710 0ustar www-datawww-datamodule AMQ module Protocol TLS_PORT = 5671 SSL_PORT = 5671 # caching EMPTY_STRING = "".freeze PACK_INT8 = 'c'.freeze PACK_CHAR = 'C'.freeze PACK_UINT16 = 'n'.freeze PACK_UINT16_X2 = 'n2'.freeze PACK_UINT32 = 'N'.freeze PACK_UINT32_X2 = 'N2'.freeze PACK_UINT64_BE = 'Q>'.freeze PACK_INT64 = 'q'.freeze PACK_INT64_BE = 'q>'.freeze PACK_UCHAR_UINT32 = 'CN'.freeze PACK_CHAR_UINT16_UINT32 = 'cnN'.freeze PACK_32BIT_FLOAT = 'f'.freeze PACK_64BIT_FLOAT = 'G'.freeze end end amq-protocol-2.3.0/lib/amq/protocol/frame.rb0000644000004100000410000001350413226473014020772 0ustar www-datawww-data# encoding: binary module AMQ module Protocol class Frame TYPES = {:method => 1, :headers => 2, :body => 3, :heartbeat => 8}.freeze TYPES_REVERSE = TYPES.invert.freeze TYPES_OPTIONS = TYPES.keys.freeze CHANNEL_RANGE = (0..65535).freeze FINAL_OCTET = "\xCE".freeze # 206 def self.encoded_payload(payload) if payload.respond_to?(:force_encoding) && payload.encoding.name != 'BINARY' # Only copy if we have to. payload = payload.dup.force_encoding('BINARY') end payload end # The channel number is 0 for all frames which are global to the connection and 1-65535 for frames that refer to specific channels. def self.encode_to_array(type, payload, channel) raise RuntimeError.new("Channel has to be 0 or an integer in range 1..65535 but was #{channel.inspect}") unless CHANNEL_RANGE.include?(channel) raise RuntimeError.new("Payload can't be nil") if payload.nil? components = [] components << [find_type(type), channel, payload.bytesize].pack(PACK_CHAR_UINT16_UINT32) components << encoded_payload(payload) components << FINAL_OCTET components end def self.encode(type, payload, channel) encode_to_array(type, payload, channel).join end class << self alias_method :__new__, :new unless method_defined?(:__new__) # because of reloading end def self.new(original_type, *args) type_id = find_type(original_type) klass = CLASSES[type_id] klass.new(*args) end def self.find_type(type) type_id = if Symbol === type then TYPES[type] else type end raise FrameTypeError.new(TYPES_OPTIONS) if type == nil || !TYPES_REVERSE.has_key?(type_id) type_id end def self.decode(*) raise NotImplementedError.new <<-EOF You are supposed to redefine this method, because it's dependent on used IO adapter. This functionality is part of the https://github.com/ruby-amqp/amq-client library. EOF end def self.decode_header(header) raise EmptyResponseError if header == nil || header.empty? type_id, channel, size = header.unpack(PACK_CHAR_UINT16_UINT32) type = TYPES_REVERSE[type_id] raise FrameTypeError.new(TYPES_OPTIONS) unless type [type, channel, size] end def final? true end end class FrameSubclass < Frame # Restore original new class << self alias_method :new, :__new__ undef_method :decode if method_defined?(:decode) end def self.id @id end def self.encode(payload, channel) super(@id, payload, channel) end attr_accessor :channel attr_reader :payload def initialize(payload, channel) @payload, @channel = payload, channel end def size @payload.bytesize end # TODO: remove once we are sure none of the clients # uses this method directly # @api private def encode_to_array components = [] components << [self.class.id, @channel, @payload.bytesize].pack(PACK_CHAR_UINT16_UINT32) components << self.class.encoded_payload(@payload) components << FINAL_OCTET components end def encode s = [self.class.id, @channel, @payload.bytesize].pack(PACK_CHAR_UINT16_UINT32) s << self.class.encoded_payload(@payload) s << FINAL_OCTET s end end class MethodFrame < FrameSubclass @id = 1 def method_class @method_class ||= begin klass_id, method_id = self.payload.unpack(PACK_UINT16_X2) index = klass_id << 16 | method_id AMQ::Protocol::METHODS[index] end end def final? !self.method_class.has_content? end # final? def decode_payload self.method_class.decode(@payload[4..-1]) end end class HeaderFrame < FrameSubclass @id = 2 def final? false end def body_size decode_payload @body_size end def weight decode_payload @weight end def klass_id decode_payload @klass_id end def properties decode_payload @properties end def decode_payload @decoded_payload ||= begin @klass_id, @weight = @payload.unpack(PACK_UINT16_X2) # the total size of the content body, that is, the sum of the body sizes for the # following content body frames. Zero indicates that there are no content body frames. # So this is NOT related to this very header frame! @body_size = AMQ::Hacks.unpack_uint64_big_endian(@payload[4..11]).first @data = @payload[12..-1] @properties = Basic.decode_properties(@data) end end end class BodyFrame < FrameSubclass @id = 3 def decode_payload @payload end def final? # we cannot know whether it is final or not so framing code in amq-client # checks this over the entire frameset. MK. false end end class HeartbeatFrame < FrameSubclass @id = 8 def final? true end # final? def self.encode super(Protocol::EMPTY_STRING, 0) end end Frame::CLASSES = { Frame::TYPES[:method] => MethodFrame, Frame::TYPES[:headers] => HeaderFrame, Frame::TYPES[:body] => BodyFrame, Frame::TYPES[:heartbeat] => HeartbeatFrame } end end amq-protocol-2.3.0/lib/amq/protocol/exceptions.rb0000644000004100000410000000307413226473014022062 0ustar www-datawww-datamodule AMQ module Protocol class Error < StandardError DEFAULT_MESSAGE = "AMQP error".freeze def self.inherited(subclass) @_subclasses ||= [] @_subclasses << subclass end # self.inherited(subclazz) def self.subclasses_with_values @_subclasses.select{ |k| defined?(k::VALUE) } end # self.subclasses_with_values def self.[](code) if result = subclasses_with_values.detect { |klass| klass::VALUE == code } result else raise "No such exception class for code #{code}" unless result end # if end # self.[] def initialize(message = self.class::DEFAULT_MESSAGE) super(message) end end class FrameTypeError < Protocol::Error def initialize(types) super("Must be one of #{types.inspect}") end end class EmptyResponseError < Protocol::Error DEFAULT_MESSAGE = "Empty response received from the server." def initialize(message = self.class::DEFAULT_MESSAGE) super(message) end end class BadResponseError < Protocol::Error def initialize(argument, expected, actual) super("Argument #{argument} has to be #{expected.inspect}, was #{data.inspect}") end end class SoftError < Protocol::Error def self.inherited(subclass) Error.inherited(subclass) end # self.inherited(subclass) end class HardError < Protocol::Error def self.inherited(subclass) Error.inherited(subclass) end # self.inherited(subclass) end end end amq-protocol-2.3.0/lib/amq/uri.rb0000644000004100000410000000613413226473014016637 0ustar www-datawww-data# encoding: utf-8 require "cgi" require "uri" module AMQ class URI # @private AMQP_DEFAULT_PORTS = { "amqp" => 5672, "amqps" => 5671 }.freeze private_constant :AMQP_DEFAULT_PORTS DEFAULTS = { heartbeat: nil, connection_timeout: nil, channel_max: nil, auth_mechanism: [], verify: false, fail_if_no_peer_cert: false, cacertfile: nil, certfile: nil, keyfile: nil }.freeze def self.parse(connection_string) uri = ::URI.parse(connection_string) raise ArgumentError.new("Connection URI must use amqp or amqps schema (example: amqp://bus.megacorp.internal:5766), learn more at http://bit.ly/ks8MXK") unless %w{amqp amqps}.include?(uri.scheme) opts = DEFAULTS.dup opts[:scheme] = uri.scheme opts[:user] = ::CGI::unescape(uri.user) if uri.user opts[:pass] = ::CGI::unescape(uri.password) if uri.password opts[:host] = uri.host if uri.host opts[:port] = uri.port || AMQP_DEFAULT_PORTS[uri.scheme] opts[:ssl] = uri.scheme.to_s.downcase =~ /amqps/i # TODO: rename to tls if uri.path =~ %r{^/(.*)} raise ArgumentError.new("#{uri} has multiple-segment path; please percent-encode any slashes in the vhost name (e.g. /production => %2Fproduction). Learn more at http://bit.ly/amqp-gem-and-connection-uris") if $1.index('/') opts[:vhost] = ::CGI::unescape($1) end if uri.query query_params = CGI::parse(uri.query) normalized_query_params = Hash[query_params.map { |param, value| [param, value.one? ? value.first : value] }] opts[:heartbeat] = normalized_query_params["heartbeat"].to_i opts[:connection_timeout] = normalized_query_params["connection_timeout"].to_i opts[:channel_max] = normalized_query_params["channel_max"].to_i opts[:auth_mechanism] = normalized_query_params["auth_mechanism"] %w(cacertfile certfile keyfile).each do |tls_option| if normalized_query_params[tls_option] && uri.scheme == "amqp" raise ArgumentError.new("The option '#{tls_option}' can only be used in URIs that use amqps schema") else opts[tls_option.to_sym] = normalized_query_params[tls_option] end end %w(verify fail_if_no_peer_cert).each do |tls_option| if normalized_query_params[tls_option] && uri.scheme == "amqp" raise ArgumentError.new("The option '#{tls_option}' can only be used in URIs that use amqps schema") else opts[tls_option.to_sym] = as_boolean(normalized_query_params[tls_option]) end end end opts end def self.parse_amqp_url(s) parse(s) end # # Implementation # # Normalizes values returned by CGI.parse. # @private def self.as_boolean(val) case val when true then true when false then false when 1 then true when 0 then false when "true" then true when "false" then false else !!val end end private_class_method :as_boolean end end amq-protocol-2.3.0/lib/amq/int_allocator.rb0000644000004100000410000000473613226473014020700 0ustar www-datawww-data# encoding: utf-8 require "amq/bit_set" module AMQ # Simple bitset-based integer allocator, heavily inspired by com.rabbitmq.utility.IntAllocator class # in the RabbitMQ Java client. # # Unlike monotonically incrementing identifier, this allocator is suitable for very long running programs # that aggressively allocate and release channels. class IntAllocator # # API # # @return [Integer] Number of integers in the allocation range attr_reader :number_of_bits # @return [Integer] Upper boundary of the integer range available for allocation attr_reader :hi # @return [Integer] Lower boundary of the integer range available for allocation attr_reader :lo # @param [Integer] lo Lower boundary of the integer range available for allocation # @param [Integer] hi Upper boundary of the integer range available for allocation # @raise [ArgumentError] if upper boundary is not greater than the lower one def initialize(lo, hi) raise ArgumentError.new "upper boundary must be greater than the lower one (given: hi = #{hi}, lo = #{lo})" unless hi > lo @hi = hi @lo = lo @number_of_bits = hi - lo @range = Range.new(1, @number_of_bits) @free_set = BitSet.new(@number_of_bits) end # initialize(hi, lo) # Attempts to allocate next available integer. If allocation succeeds, allocated value is returned. # Otherwise, -1 is returned. # # Current implementation of this method is O(n), where n is number of bits in the range available for # allocation. # # @return [Integer] Allocated integer if allocation succeeded. -1 otherwise. def allocate if n = @free_set.next_clear_bit if n < @hi - 1 then @free_set.set(n) n + 1 else -1 end else -1 end end # allocate # Releases previously allocated integer. If integer provided as argument was not previously allocated, # this method has no effect. # # @return [NilClass] nil def free(reservation) @free_set.unset(reservation-1) end # free(reservation) alias release free # @return [Boolean] true if provided argument was previously allocated, false otherwise def allocated?(reservation) @free_set.get(reservation-1) end # allocated?(reservation) # Releases the whole allocation range def reset @free_set.clear end # reset protected end # IntAllocator end # AMQ amq-protocol-2.3.0/lib/amq/pack.rb0000644000004100000410000000243713226473014016760 0ustar www-datawww-data# encoding: binary require 'amq/endianness' module AMQ # Implements pack to/unpack from 64bit string in network byte order # compatible with Ruby 1.8+. module Pack UINT64 = "Q".freeze UINT16_BE = "n".freeze INT16 = "c".freeze if Endianness.big_endian? def self.pack_uint64_big_endian(long_long) [long_long].pack(UINT64) end def self.unpack_uint64_big_endian(data) data.unpack(UINT64) end def self.pack_int16_big_endian(short) [long_long].pack(INT16) end def self.unpack_int16_big_endian(data) data.unpack(INT16) end else def self.pack_uint64_big_endian(long_long) result = [long_long].pack(UINT64) result.bytes.to_a.reverse.map(&:chr).join end def self.unpack_uint64_big_endian(data) data = data.bytes.to_a.reverse.map(&:chr).join data.unpack(UINT64) end def self.pack_int16_big_endian(short) result = [long_long].pack(INT16) result.bytes.to_a.reverse.map(&:chr).join end def self.unpack_int16_big_endian(data) value = data.bytes.to_a.map(&:chr).join.unpack(UINT16_BE)[0] [(value & ~(1 << 15)) - (value & (1 << 15))] end end end # Backwards compatibility Hacks = Pack end amq-protocol-2.3.0/lib/amq/settings.rb0000644000004100000410000001347613226473014017707 0ustar www-datawww-data# encoding: utf-8 require "amq/protocol/client" require "amq/uri" module AMQ module Settings # @private AMQPS = "amqps".freeze # Default connection settings used by AMQ clients # # @see AMQ::Client::Settings.configure def self.default @default ||= { # TCP/IP connection parameters host: "127.0.0.1", port: AMQ::Protocol::DEFAULT_PORT, auth_mechanism: [], # authentication parameters user: "guest", pass: "guest", vhost: "/", # client connection parameters frame_max: (128 * 1024), heartbeat: nil, connection_timeout: nil, channel_max: nil, # ssl parameters ssl: false, verify: false, fail_if_no_peer_cert: false, cacertfile: nil, certfile: nil, keyfile: nil } end # Merges given configuration parameters with defaults and returns # the result. # # @param [Hash] Configuration parameters to use. # # @option settings [String] :host ("127.0.0.1") Hostname AMQ broker runs on. # @option settings [String] :port (5672) Port AMQ broker listens on. # @option settings [String] :vhost ("/") Virtual host to use. # @option settings [String] :user ("guest") Username to use for authentication. # @option settings [String] :pass ("guest") Password to use for authentication. # @option settings [String] :ssl (false) Should be use TLS (SSL) for connection? # @option settings [Fixnum] :frame_max (131072) Maximum frame size to use. If broker cannot support frames this large, broker's maximum value will be used instead. # @option settings [Integer] :heartbeat (nil) Heartbeat timeout value in seconds to negotiate with the server. # @option settings [Integer] :connection_timeout (nil) Time in milliseconds to wait while establishing a TCP connection to the server before giving up. # @option settings [Fixnum] :channel_max (nil) Maximum number of channels to permit on this connection. # @option settings [Array] :auth_mechanism ([]) SASL authentication mechanisms to consider when negotiating a mechanism with the server. This parameter can be specified multiple times to specify multiple mechanisms, e.g. `?auth_mechanism=plain&auth_mechanism=amqplain`. # @option settings [Boolean] :verify (false) Controls peer verification mode. # @option settings [Boolean] :fail_if_no_peer_cert (false) When set to true, TLS connection will be rejected if client fails to provide a certificate. # @option settings [String] :cacertfile (nil) Certificate Authority (CA) certificate file path. # @option settings [String] :certfile (nil) Server certificate file path. # @option settings [String] :keyfile (nil) Server private key file path. # # @option settings [String] :broker (nil) Broker name (use if you intend to use broker-specific features). # # @return [Hash] Merged configuration parameters. def self.configure(settings = nil) case settings when Hash then if username = (settings.delete(:username) || settings.delete(:user)) settings[:user] ||= username end if password = (settings.delete(:password) || settings.delete(:pass)) settings[:pass] ||= password end self.default.merge(settings) when String then settings = self.parse_amqp_url(settings) self.default.merge(settings) when NilClass then self.default end end # Parses AMQP connection URI and returns its components as a hash. # # h2. vhost naming schemes # # It is convenient to be able to specify the AMQP connection # parameters as a URI string, and various "amqp" URI schemes # exist. Unfortunately, there is no standard for these URIs, so # while the schemes share the basic idea, they differ in some # details. This implementation aims to encourage URIs that work # as widely as possible. # # The URI scheme should be "amqp", or "amqps" if SSL is required. # # The host, port, username and password are represented in the # authority component of the URI in the same way as in http URIs. # # The vhost is obtained from the first segment of the path, with the # leading slash removed. The path should contain only a single # segment (i.e, the only slash in it should be the leading one). # If the vhost is to include slashes or other reserved URI # characters, these should be percent-escaped. # # @example How vhost is parsed # # AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com") # => vhost is nil, so default (/) will be used # AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/") # => vhost is an empty string # AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/%2Fvault") # => vhost is /vault # AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/production") # => vhost is production # AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/a.b.c") # => vhost is a.b.c # AMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/foo/bar") # => ArgumentError # # # @param [String] connection_string AMQP connection URI, à la JDBC connection string. For example: amqp://bus.megacorp.internal:5877. # @return [Hash] Connection parameters (:username, :password, :vhost, :host, :port, :ssl) # # @raise [ArgumentError] When connection URI schema is not amqp or amqps, or the path contains multiple segments # # @api public def self.parse_amqp_url(connection_string) AMQ::URI.parse(connection_string) end end end amq-protocol-2.3.0/lib/amq/endianness.rb0000644000004100000410000000031113226473014020156 0ustar www-datawww-datamodule AMQ module Endianness BIG_ENDIAN = ([1].pack("s") == "\x00\x01") def big_endian? BIG_ENDIAN end def little_endian? !BIG_ENDIAN end extend self end end amq-protocol-2.3.0/generate.rb0000755000004100000410000000120513226473014016303 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 # rabbitmq-codegen is Python 3 compatible and so is # the code in this repo but Mako still fails with 3.6 as of May 2017 :( MK. python = ENV.fetch("PYTHON", "python2") def sh(*args) system(*args) end extensions = [] spec = "codegen/rabbitmq-codegen/amqp-rabbitmq-0.9.1.json" unless File.exist?(spec) sh "git submodule update --init" end path = "lib/amq/protocol/client.rb" puts "Running '#{python} ./codegen/codegen.py client #{spec} #{extensions.join(' ')} #{path}'" sh "#{python} ./codegen/codegen.py client #{spec} #{extensions.join(' ')} #{path}" if File.file?(path) sh "ruby -c #{path}" end amq-protocol-2.3.0/profiling/0000755000004100000410000000000013226473014016154 5ustar www-datawww-dataamq-protocol-2.3.0/profiling/stackprof/0000755000004100000410000000000013226473014020150 5ustar www-datawww-dataamq-protocol-2.3.0/profiling/stackprof/body_framing_with_2k_payload.rb0000644000004100000410000000131313226473014026273 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib")) require "amq/protocol/client" FRAME_SIZE = 128 * 1024 puts puts "-" * 80 puts "Profiling on #{RUBY_DESCRIPTION}" n = 250_000 # warm up the JIT, etc puts "Doing a warmup run..." 15_000.times { AMQ::Protocol::Method.encode_body("ab" * 1024, 1, FRAME_SIZE) } require 'stackprof' # preallocate ary = Array.new(n) { "ab" * 1024 } puts "Doing main run..." result = StackProf.run(mode: :wall) do n.times { |i| AMQ::Protocol::Method.encode_body(ary[i], 1, FRAME_SIZE) } end File.open('./profiling/dumps/body_framing_with_2k_payload.dump', "w+") do |f| f.write Marshal.dump(result) end amq-protocol-2.3.0/profiling/README.md0000644000004100000410000000051613226473014017435 0ustar www-datawww-data# Profiling Scripts This directory contains profiling scripts. Currently they use [stackprof](https://github.com/tmm1/stackprof) which requires Ruby 2.1+ (preview2 or later). ## Running the Profiler ruby profiling/stackprof/body_framing_with_2k_payload.rb stackprof profiling/dumps/body_framing_with_2k_payload.dump --text amq-protocol-2.3.0/.gitignore0000644000004100000410000000025313226473014016153 0ustar www-datawww-data/*.gem /.rvmrc /.ruby-version tmp *.pyc *.iml .idea /vendor/bundle /vendor/amq-* /coverage Gemfile.lock .rbx/* .Apple* .bundle/* bin/* *.bundle Makefile profiling/dumps/* amq-protocol-2.3.0/LICENSE0000644000004100000410000000220413226473014015166 0ustar www-datawww-dataCopyright (c) 2010 – 2011 Jakub Šťastný aka Botanicus Copyright (c) 2011 – 2016 Michael S. Klishin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. amq-protocol-2.3.0/benchmarks/0000755000004100000410000000000013226473014016300 5ustar www-datawww-dataamq-protocol-2.3.0/benchmarks/pure/0000755000004100000410000000000013226473014017253 5ustar www-datawww-dataamq-protocol-2.3.0/benchmarks/pure/body_framing_with_256k_payload.rb0000644000004100000410000000120213226473014025546 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib")) require "amq/protocol/client" require "benchmark" FRAME_SIZE = 128 * 1024 puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 250_000 # warm up the JIT, etc puts "Doing a warmup run..." 15_000.times { AMQ::Protocol::Method.encode_body("a" * 256 * 1024, 1, FRAME_SIZE) } t = Benchmark.realtime do n.times { AMQ::Protocol::Method.encode_body("a" * 256 * 1024, 1, FRAME_SIZE) } end r = (n.to_f/t.to_f) puts "AMQ::Protocol::Method.encode_body rate: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 amq-protocol-2.3.0/benchmarks/pure/body_framing_with_2k_payload.rb0000644000004100000410000000117013226473014025377 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib")) require "amq/protocol/client" require "benchmark" FRAME_SIZE = 128 * 1024 puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 250_000 # warm up the JIT, etc puts "Doing a warmup run..." 15_000.times { AMQ::Protocol::Method.encode_body("ab" * 1024, 1, FRAME_SIZE) } t = Benchmark.realtime do n.times { AMQ::Protocol::Method.encode_body("ab" * 1024, 1, FRAME_SIZE) } end r = (n.to_f/t.to_f) puts "AMQ::Protocol::Method.encode_body rate: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 amq-protocol-2.3.0/benchmarks/int_allocator.rb0000644000004100000410000000127213226473014021461 0ustar www-datawww-data$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")) require 'amq/int_allocator' require "benchmark" allocator = AMQ::IntAllocator.new(1,65535) mutex = Mutex.new Benchmark.bm do |x| x.report("allocate") do allocator = AMQ::IntAllocator.new(1,65535) 1.upto(65534) do |i| mutex.synchronize do n = allocator.allocate raise 'it be broke' unless n == i end end end x.report("allocate_with_release") do allocator = AMQ::IntAllocator.new(1,65535) 1.upto(65534) do |i| mutex.synchronize do n = allocator.allocate if i % 5 == 0 allocator.release(n) end end end end endamq-protocol-2.3.0/README.md0000644000004100000410000000315713226473014015450 0ustar www-datawww-data# What is amq-protocol. amq-protocol is an AMQP 0.9.1 serialization library for Ruby. It is not an AMQP client: amq-protocol only handles serialization and deserialization. If you want to write your own AMQP client, this gem will handle all the serialization needs for you, including RabbitMQ extensions to AMQP 0.9.1. ## Supported Ruby Versions amq-protocol `1.9.2` was the last version to support Ruby 1.8 and 1.9. amq-protocol `2.0.0` and later only supports Ruby 2.0+. ## Installation gem install amq-protocol ## Development Make sure you have Python, pip and the mako templating package installed: pip install mako amq-protocol uses RabbitMQ protocol code generation library that is in Python, so there is some Python involved in the build. To regenerate `lib/amq/protocol/client.rb` from the source (`codegen/*` files), run ./generate.rb To make changes, **do not edit client.rb directly**. Instead, edit the `codegen/protocol.rb.pytemplate` and regenerate. To run tests, use bundle install --binstubs ./bin/rspec -c spec spec ## Maintainer Information amq-protocol is maintained by [Michael Klishin](https://github.com/michaelklishin). ## CI Status [![Build Status](https://secure.travis-ci.org/ruby-amqp/amq-protocol.png)](https://travis-ci.org/ruby-amqp/amq-protocol) ## Issues Please report any issues you may find to our [Issue tracker](http://github.com/ruby-amqp/amq-protocol/issues) on GitHub. ## Mailing List Any questions you may have should be sent to the [Ruby AMQP mailing list](http://groups.google.com/group/ruby-amqp). ## License MIT (see LICENSE in the repository root). amq-protocol-2.3.0/.gitmodules0000644000004100000410000000017613226473014016344 0ustar www-datawww-data[submodule "codegen/rabbitmq-codegen"] path = codegen/rabbitmq-codegen url = git://github.com/rabbitmq/rabbitmq-codegen.git amq-protocol-2.3.0/codegen/0000755000004100000410000000000013226473014015567 5ustar www-datawww-dataamq-protocol-2.3.0/codegen/protocol.rb.pytemplate0000644000004100000410000002301613226473014022142 0ustar www-datawww-data# encoding: utf-8 # encoding: binary # THIS IS AN AUTOGENERATED FILE, DO NOT MODIFY # IT DIRECTLY ! FOR CHANGES, PLEASE UPDATE FILES # IN THE ./codegen DIRECTORY OF THE AMQ-PROTOCOL REPOSITORY.<% import codegen_helpers as helpers %><% import re, os, codegen %> require "amq/pack" require "amq/protocol/table" require "amq/protocol/frame" require "amq/protocol/constants" require "amq/protocol/exceptions" module AMQ module Protocol PROTOCOL_VERSION = "${spec.major}.${spec.minor}.${spec.revision}".freeze PREAMBLE = "${'AMQP\\x00\\x%02x\\x%02x\\x%02x' % (spec.major, spec.minor, spec.revision)}".freeze DEFAULT_PORT = ${spec.port} # @return [Array] Collection of subclasses of AMQ::Protocol::Class. def self.classes Protocol::Class.classes end # @return [Array] Collection of subclasses of AMQ::Protocol::Method. def self.methods Protocol::Method.methods end % for tuple in spec.constants: % if tuple[2] == "soft-error" or tuple[2] == "hard-error": class ${codegen.to_ruby_class_name(tuple[0])} < ${codegen.to_ruby_class_name(tuple[2])} VALUE = ${tuple[1]} end % endif % endfor class Class @classes = Array.new def self.method_id @method_id end def self.name @name end def self.inherited(base) if self == Protocol::Class @classes << base end end def self.classes @classes end end class Method @methods = Array.new def self.method_id @method_id end def self.name @name end def self.index @index end def self.inherited(base) if self == Protocol::Method @methods << base end end def self.methods @methods end def self.split_headers(user_headers) properties, headers = {}, {} user_headers.each do |key, value| # key MUST be a symbol since symbols are not garbage-collected if Basic::PROPERTIES.include?(key) properties[key] = value else headers[key] = value end end return [properties, headers] end def self.encode_body(body, channel, frame_size) return [] if body.empty? # 8 = 1 + 2 + 4 + 1 # 1 byte of frame type # 2 bytes of channel number # 4 bytes of frame payload length # 1 byte of payload trailer FRAME_END byte limit = frame_size - 8 return [BodyFrame.new(body, channel)] if body.bytesize < limit # Otherwise String#slice on 1.9 will operate with code points, # and we need bytes. MK. body.force_encoding("ASCII-8BIT") if RUBY_VERSION.to_f >= 1.9 array = Array.new while body && !body.empty? payload, body = body[0, limit], body[limit, body.length - limit] array << BodyFrame.new(payload, channel) end array end def self.instantiate(*args, &block) self.new(*args, &block) end end % for klass in spec.classes : class ${klass.constant_name} < Protocol::Class @name = "${klass.name}" @method_id = ${klass.index} % if klass.fields: ## only the Basic class has fields (refered as properties in the JSON) PROPERTIES = [ % for field in klass.fields: :${field.ruby_name}, # ${spec.resolveDomain(field.domain)} % endfor ] % for f in klass.fields: # <% i = klass.fields.index(f) %>1 << ${15 - i} def self.encode_${f.ruby_name}(value) buffer = '' % for line in helpers.genSingleEncode(spec, "value", f.domain): ${line} % endfor [${i}, ${"0x%04x" % ( 1 << (15-i),)}, buffer] end % endfor % endif % if klass.name == "basic" : def self.encode_properties(body_size, properties) pieces, flags = [], 0 properties.reject {|key, value| value.nil?}.each do |key, value| i, f, result = self.__send__(:"encode_#{key}", value) flags |= f pieces[i] = result end # result = [${klass.index}, 0, body_size, flags].pack('n2Qn') result = [${klass.index}, 0].pack(PACK_UINT16_X2) result += AMQ::Pack.pack_uint64_big_endian(body_size) result += [flags].pack(PACK_UINT16) pieces_joined = pieces.join(EMPTY_STRING) result.force_encoding(pieces_joined.encoding) + pieces_joined end # THIS DECODES ONLY FLAGS DECODE_PROPERTIES = { % for f in klass.fields: ${"0x%04x" % ( 1 << (15 - klass.fields.index(f)),)} => :${f.ruby_name}, % endfor } DECODE_PROPERTIES_TYPE = { % for f in klass.fields: ${"0x%04x" % ( 1 << (15 - klass.fields.index(f)),)} => :${spec.resolveDomain(f.domain)}, % endfor } # Hash doesn't give any guarantees on keys order, we will do it in a # straightforward way DECODE_PROPERTIES_KEYS = [ % for f in klass.fields: ${"0x%04x" % ( 1 << (15 - klass.fields.index(f)),)}, % endfor ] def self.decode_properties(data) offset, data_length, properties = 0, data.bytesize, {} compressed_index = data[offset, 2].unpack(PACK_UINT16)[0] offset += 2 while data_length > offset DECODE_PROPERTIES_KEYS.each do |key| next unless compressed_index >= key compressed_index -= key name = DECODE_PROPERTIES[key] || raise(RuntimeError.new("No property found for index #{index.inspect}!")) case DECODE_PROPERTIES_TYPE[key] when :shortstr size = data[offset, 1].unpack(PACK_CHAR)[0] offset += 1 result = data[offset, size] when :octet size = 1 result = data[offset, size].unpack(PACK_CHAR).first when :timestamp size = 8 result = Time.at(data[offset, size].unpack(PACK_UINT64_BE).last) when :table size = 4 + data[offset, 4].unpack(PACK_UINT32)[0] result = Table.decode(data[offset, size]) end properties[name] = result offset += size end end properties end % endif % for method in klass.methods: class ${method.constant_name} < Protocol::Method @name = "${klass.name}.${method.name}" @method_id = ${method.index} @index = ${method.binary()} @packed_indexes = [${klass.index}, ${method.index}].pack(PACK_UINT16_X2).freeze % if (spec.type == "client" and method.accepted_by("client")) or (spec.type == "server" and method.accepted_by("server") or spec.type == "all"): # @return def self.decode(data) offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method % for line in helpers.genDecodeMethodDefinition(spec, method): ${line} % endfor % if (method.klass.name == "connection" or method.klass.name == "channel") and method.name == "close": self.new(${', '.join([f.ruby_name for f in method.arguments])}) % else: self.new(${', '.join([f.ruby_name for f in method.arguments])}) % endif end % if len(method.arguments) > 0: attr_reader ${', '.join([":" + f.ruby_name for f in method.arguments])} % endif def initialize(${', '.join([f.ruby_name for f in method.arguments])}) % for f in method.arguments: @${f.ruby_name} = ${f.ruby_name} % endfor end % endif def self.has_content? % if method.hasContent: true % else: false % endif end % if (spec.type == "client" and method.accepted_by("server")) or (spec.type == "server" and method.accepted_by("client")) or spec.type == "all": # @return # ${method.params()} % if klass.name == "connection": def self.encode(${(", ").join(method.not_ignored_args())}) % else: def self.encode(${(", ").join(["channel"] + method.not_ignored_args())}) % endif % for argument in method.ignored_args(): ${codegen.convert_to_ruby(argument)} % endfor % if klass.name == "connection": channel = 0 % endif buffer = @packed_indexes.dup % for line in helpers.genEncodeMethodDefinition(spec, method): ${line} % endfor % if "payload" in method.args() or "user_headers" in method.args(): frames = [MethodFrame.new(buffer, channel)] % if "user_headers" in method.args(): properties, _headers = self.split_headers(user_headers) if properties.nil? or properties.empty? raise RuntimeError.new("Properties can not be empty!") end properties_payload = Basic.encode_properties(payload.bytesize, properties) frames << HeaderFrame.new(properties_payload, channel) % endif % if "payload" in method.args(): frames += self.encode_body(payload, channel, frame_size) frames % endif % else: MethodFrame.new(buffer, channel) % endif end % endif end % endfor end % endfor METHODS = begin Method.methods.inject(Hash.new) do |hash, klass| hash.merge!(klass.index => klass) end end end end amq-protocol-2.3.0/codegen/amqp_0.9.1_changes.json0000644000004100000410000000266413226473014021645 0ustar www-datawww-data{"tx": {"select-ok": ["client"], "rollback": ["server"], "commit": ["server"], "rollback-ok": ["client"], "select": ["server"], "commit-ok": ["client"]}, "exchange": {"delete-ok": ["client"], "declare-ok": ["client"], "declare": ["server"], "delete": ["server"], "bind": ["server"], "bind-ok": ["client"], "unbind": ["server"], "unbind-ok": ["client"]}, "queue": {"unbind": ["server"], "unbind-ok": ["client"], "purge-ok": ["client"], "bind": ["server"], "purge": ["server"], "declare-ok": ["client"], "delete-ok": ["client"], "delete": ["server"], "declare": ["server"], "bind-ok": ["client"]}, "connection": {"secure": ["client"], "secure-ok": ["server"], "open-ok": ["client"], "close-ok": ["client", "server"], "start": ["client"], "tune": ["client"], "start-ok": ["server"], "close": ["client", "server"], "open": ["server"], "tune-ok": ["server"]}, "basic": {"qos": ["server"], "consume": ["server"], "reject": ["server"], "get": ["server"], "ack": ["client", "server"], "get-ok": ["client"], "consume-ok": ["client"], "deliver": ["client"], "recover-ok": ["client"], "publish": ["server"], "cancel": ["server", "client"], "recover-async": ["server"], "get-empty": ["client"], "qos-ok": ["client"], "return": ["client"], "recover": ["server"], "cancel-ok": ["client"]}, "channel": {"flow-ok": ["server", "client"], "flow": ["server", "client"], "open-ok": ["client"], "close-ok": ["client", "server"], "close": ["client", "server"], "open": ["server"]}} amq-protocol-2.3.0/codegen/codegen_helpers.py0000644000004100000410000001361313226473014021273 0ustar www-datawww-data# -*- coding: utf-8 -*- from __future__ import print_function def genSingleEncode(spec, cValue, unresolved_domain): buffer = [] type = spec.resolveDomain(unresolved_domain) if type == 'shortstr': buffer.append("buffer << %s.to_s.bytesize.chr" % (cValue,)) buffer.append("buffer << %s.to_s" % (cValue,)) elif type == 'longstr': buffer.append("buffer << [%s.to_s.bytesize].pack(PACK_UINT32)" % (cValue,)) buffer.append("buffer << %s.to_s" % (cValue,)) elif type == 'octet': buffer.append("buffer << [%s].pack(PACK_CHAR)" % (cValue,)) elif type == 'short': buffer.append("buffer << [%s].pack(PACK_UINT16)" % (cValue,)) elif type == 'long': buffer.append("buffer << [%s].pack(PACK_UINT32)" % (cValue,)) elif type == 'longlong': buffer.append("buffer << AMQ::Pack.pack_uint64_big_endian(%s)" % (cValue,)) elif type == 'timestamp': buffer.append("buffer << AMQ::Pack.pack_uint64_big_endian(%s)" % (cValue,)) elif type == 'bit': raise "Can't encode bit in genSingleEncode" elif type == 'table': buffer.append("buffer << AMQ::Protocol::Table.encode(%s)" % (cValue,)) else: raise "Illegal domain in genSingleEncode: {0}".format(type) return buffer def genSingleDecode(spec, field): cLvalue = field.ruby_name unresolved_domain = field.domain if cLvalue == "known_hosts": import sys print(field, field.ignored, file = sys.stderr) type = spec.resolveDomain(unresolved_domain) buffer = [] if type == 'shortstr': buffer.append("length = data[offset, 1].unpack(PACK_CHAR).first") buffer.append("offset += 1") buffer.append("%s = data[offset, length]" % (cLvalue,)) buffer.append("offset += length") elif type == 'longstr': buffer.append("length = data[offset, 4].unpack(PACK_UINT32).first") buffer.append("offset += 4") buffer.append("%s = data[offset, length]" % (cLvalue,)) buffer.append("offset += length") elif type == 'octet': buffer.append("%s = data[offset, 1].unpack(PACK_CHAR).first" % (cLvalue,)) buffer.append("offset += 1") elif type == 'short': buffer.append("%s = data[offset, 2].unpack(PACK_UINT16).first" % (cLvalue,)) buffer.append("offset += 2") elif type == 'long': buffer.append("%s = data[offset, 4].unpack(PACK_UINT32).first" % (cLvalue,)) buffer.append("offset += 4") elif type == 'longlong': buffer.append("%s = AMQ::Pack.unpack_uint64_big_endian(data[offset, 8]).first" % (cLvalue,)) buffer.append("offset += 8") elif type == 'timestamp': buffer.append("%s = data[offset, 8].unpack(PACK_UINT64_BE).first" % (cLvalue,)) buffer.append("offset += 8") elif type == 'bit': raise "Can't decode bit in genSingleDecode" elif type == 'table': buffer.append("table_length = Table.length(data[offset, 4])") buffer.append("%s = Table.decode(data[offset, table_length + 4])" % (cLvalue,)) buffer.append("offset += table_length + 4") else: raise StandardError("Illegal domain '{0}' in genSingleDecode".format(type)) return buffer def genSingleSimpleDecode(spec, field): cLvalue = field.ruby_name unresolved_domain = field.domain if cLvalue == "known_hosts": import sys print >> sys.stderr, field, field.ignored type = spec.resolveDomain(unresolved_domain) buffer = [] if type == 'shortstr': buffer.append("data.to_s") elif type == 'longstr': buffer.append("data.to_s") elif type == 'octet': buffer.append("data.unpack(PACK_INT8).first") elif type == 'short': buffer.append("data.unpack(PACK_UINT16).first") elif type == 'long': buffer.append("data.unpack(PACK_UINT32).first") elif type == 'longlong': buffer.append("AMQ::Pack.unpack_uint64_big_endian(data).first") elif type == 'timestamp': buffer.append("Time.at(data.unpack(PACK_UINT64_BE).last)") elif type == 'bit': raise "Can't decode bit in genSingleDecode" elif type == 'table': buffer.append("Table.decode(data)") else: raise StandardError("Illegal domain '" + type + "' in genSingleSimpleDecode") return buffer def genEncodeMethodDefinition(spec, m): def finishBits(): if bit_index is not None: buffer.append("buffer << [bit_buffer].pack(PACK_CHAR)") bit_index = None buffer = [] for f in m.arguments: if spec.resolveDomain(f.domain) == 'bit': if bit_index is None: bit_index = 0 buffer.append("bit_buffer = 0") if bit_index >= 8: finishBits() buffer.append("bit_buffer = 0") bit_index = 0 buffer.append("bit_buffer = bit_buffer | (1 << %d) if %s" % (bit_index, f.ruby_name)) bit_index = bit_index + 1 else: finishBits() bit_index = None buffer += genSingleEncode(spec, f.ruby_name, f.domain) finishBits() return buffer def genDecodeMethodDefinition(spec, m): buffer = [] bitindex = None for f in m.arguments: if spec.resolveDomain(f.domain) == 'bit': if bitindex is None: bitindex = 0 if bitindex >= 8: bitindex = 0 if bitindex == 0: buffer.append("bit_buffer = data[offset, 1].unpack(PACK_CHAR).first") buffer.append("offset += 1") buffer.append("%s = (bit_buffer & (1 << %d)) != 0" % (f.ruby_name, bitindex)) #### TODO: ADD bitindex TO THE buffer else: buffer.append("%s = (bit_buffer & (1 << %d)) != 0" % (f.ruby_name, bitindex)) bitindex = bitindex + 1 else: bitindex = None buffer += genSingleDecode(spec, f) return buffer amq-protocol-2.3.0/codegen/codegen.py0000755000004100000410000001060613226473014017553 0ustar www-datawww-data#!/usr/bin/env python # -*- coding: utf-8 -*- # Documentation for Mako templates: # http://www.makotemplates.org/docs/syntax.html import os, sys, re sys.path.append(os.path.join("codegen", "rabbitmq-codegen")) from amqp_codegen import * try: from mako.template import Template except ImportError: print("Mako isn't installed. Please install mako via pip or similar.") sys.exit(1) # main class class AmqpSpecObject(AmqpSpec): IGNORED_CLASSES = ["access"] IGNORED_FIELDS = { 'ticket': 0, 'capabilities': '', 'insist' : 0, 'out_of_band': '', 'known_hosts': '', } def __init__(self, path): AmqpSpec.__init__(self, path) def extend_field(field): field.ruby_name = re.sub("[- ]", "_", field.name) field.type = self.resolveDomain(field.domain) field.ignored = bool(field.name in self.__class__.IGNORED_FIELDS) # I. e. deprecated for klass in self.classes: klass.ignored = bool(klass.name in self.__class__.IGNORED_CLASSES) for field in klass.fields: extend_field(field) for method in klass.methods: for field in method.arguments: extend_field(field) self.classes = filter(lambda klass: not klass.ignored, self.classes) original_init = AmqpEntity.__init__ def new_init(self, arg): original_init(self, arg) constant_name = "" for chunk in self.name.split("-"): constant_name += chunk.capitalize() self.constant_name = constant_name AmqpEntity.__init__ = new_init # method.accepted_by("server") # method.accepted_by("client", "server") accepted_by_update = json.loads(open("codegen/amqp_0.9.1_changes.json").read()) def accepted_by(self, *receivers): def get_accepted_by(self): try: return accepted_by_update[self.klass.name][self.name] except KeyError: return ["server", "client"] actual_receivers = get_accepted_by(self) return all(map(lambda receiver: receiver in actual_receivers, receivers)) AmqpMethod.accepted_by = accepted_by def convert_value_to_ruby(value): values = {None: "nil", False: "false", True: "true", "": "EMPTY_STRING"} try: return values[value] except: return value.__repr__() def convert_to_ruby(field): name = re.sub("-", "_", field.name) # TODO: use ruby_name if name == "ticket": return "%s = %s" % (name, field.defaultvalue) # we want to keep it as an int, not as a boolean else: return "%s = %s" % (name, convert_value_to_ruby(field.defaultvalue)) def not_ignored_args(self): if self.hasContent: return ["payload", "user_headers"] + map(lambda argument: argument.ruby_name, filter(lambda argument: not argument.ignored, self.arguments)) + ["frame_size"] else: return map(lambda argument: argument.ruby_name, filter(lambda argument: not argument.ignored, self.arguments)) AmqpMethod.not_ignored_args = not_ignored_args def ignored_args(self): return filter(lambda argument: argument.ignored, self.arguments) AmqpMethod.ignored_args = ignored_args # helpers def to_ruby_name(name): return re.sub("[- ]", "_", name) def to_ruby_class_name(name): parts = re.split("[- ]", name) ruby_class_name = "" for part in parts: ruby_class_name = ruby_class_name + part[0].upper() + part[1:].lower() return ruby_class_name def params(self): buffer = [] for f in self.arguments: buffer.append(convert_to_ruby(f)) if self.hasContent: buffer.append("user_headers = nil") buffer.append("payload = \"\"") buffer.append("frame_size = nil") return buffer AmqpMethod.params = params def args(self): return map(lambda item: item.split(" ")[0], self.params()) AmqpMethod.args = args def binary(self): method_id = self.klass.index << 16 | self.index return "0x%08X # %i, %i, %i" % (method_id, self.klass.index, self.index, method_id) AmqpMethod.binary = binary # helpers def render(path, **context): file = open(path) template = Template(file.read()) return template.render(**context) def generateMain(type): def main(json_spec_path): spec = AmqpSpecObject(json_spec_path) spec.type = type print(render("codegen/protocol.rb.pytemplate", spec = spec)) return main if __name__ == "__main__": do_main_dict({"client": generateMain("client")}) amq-protocol-2.3.0/codegen/__init__.py0000644000004100000410000000000013226473014017666 0ustar www-datawww-data