stomp-1.4.4/0000755000004100000410000000000013120662775012723 5ustar www-datawww-datastomp-1.4.4/adhoc/0000755000004100000410000000000013120662775014001 5ustar www-datawww-datastomp-1.4.4/adhoc/stomp_adhoc_common.rb0000644000004100000410000000463613120662775020207 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Common Stomp 1.1 code. # require "rubygems" if RUBY_VERSION < "1.9" require "stomp" # module Stomp11Common # Port Constants locally STOMP_AMQ_PORT = ENV['STOMP_AMQ_PORT'] ? ENV['STOMP_AMQ_PORT'].to_i : 61613 STOMP_APOLLO_PORT = ENV['STOMP_APOLLO_PORT'] ? ENV['STOMP_APOLLO_PORT'].to_i : 62613 STOMP_ARTEMIS_PORT = ENV['STOMP_ARTEMIS_PORT'] ? ENV['STOMP_ARTEMIS_PORT'].to_i : 31613 STOMP_SSNG_PORT = ENV['STOMP_SSNG_PORT'] ? ENV['STOMP_SSNG_PORT'].to_i : 51613 STOMP_RMQ_PORT = ENV['STOMP_RMQ_PORT'] ? ENV['STOMP_RMQ_PORT'].to_i : 41613 # Vhost Constants STOMP_RMQ_VHOST = ENV['STOMP_RMQ_VHOST'] || '/' STOMP_VHOST = ENV['STOMP_VHOST'] || 'localhost' # Client Protocol List STOMP_PROTOCOL = ENV['STOMP_PROTOCOL'] || "1.2" # User id def login() ENV['STOMP_USER'] || 'guest' end # Password def passcode() ENV['STOMP_PASSCODE'] || 'guest' end # Server host def host() ENV['STOMP_HOST'] || "localhost" # The connect host name end # Server port def port() if ENV['STOMP_AMQ'] STOMP_AMQ_PORT elsif ENV['STOMP_APOLLO'] STOMP_APOLLO_PORT elsif ENV['STOMP_RMQ'] STOMP_RMQ_PORT elsif ENV['STOMP_SSNG'] STOMP_SSNG_PORT elsif ENV['STOMP_PORT'] ENV['STOMP_PORT'].to_i else 61613 # The default ActiveMQ stomp listener port end end # Required vhost name def virt_host() if ENV['STOMP_RMQ'] STOMP_RMQ_VHOST else STOMP_VHOST end end # Create a 1.1 commection def get_connection() conn_hdrs = {"accept-version" => STOMP_PROTOCOL, "host" => virt_host(), # the vhost } conn_hash = { :hosts => [ {:login => login(), :passcode => passcode(), :host => host(), :port => port()}, ], :connect_headers => conn_hdrs, } conn = Stomp::Connection.new(conn_hash) end # Number of messages def nmsgs() (ENV['STOMP_NMSGS'] || 1).to_i # Number of messages end # Queue / Topic Name def make_destination(right_part = nil, topic = false) if ENV['STOMP_DOTQUEUE'] right_part.gsub!('/', '.') end if topic "/topic/#{right_part}" else "/queue/#{right_part}" end end # True if client should supply a receipt block on 'publish' def cli_block() ENV['STOMP_CLI_BLOCK'] end # True if connection should ask for a receipt def conn_receipt() ENV['STOMP_RECEIPT'] end end # module stomp-1.4.4/adhoc/issue121_03.rb0000644000004100000410000000751413120662775016213 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'stomp' require 'tmpdir' # Focus on this gem's capabilities. require 'memory_profiler' # require 'memory-profiler' if Kernel.respond_to?(:require_relative) require_relative("stomp_adhoc_common") require_relative("payload_generator") else $LOAD_PATH << File.dirname(__FILE__) require "stomp_adhoc_common" require("payload_generator") end include Stomp11Common # Round 3 of testing around issue #121. # Different memory profiler gem. class Issue121Examp03 attr_reader :client, :session # Initialize. def initialize(topic = false) @client, @session, @topic = nil, nil, topic @nmsgs = nmsgs() @queue = make_destination("issue121/test_03") @id = "issue121_03" @block = cli_block() # @cmin, @cmax = 1292, 67782 # From the issue discussion PayloadGenerator::initialize(min= @cmin, max= @cmax) @ffmts = "%16.6f" # mps = 5.6 # see issue discussion @to, @nmts, @nts, @umps = 0.0, Time.now.to_f, @nmsgs, mps @tslt = 1.0 / @umps end # initialize # Startup def start # client_hdrs = {"accept-version" => "1.1,1.2", "host" => virt_host, } # client_hash = { :hosts => [ {:login => login(), :passcode => passcode(), :host => host(), :port => port()}, ], :connect_headers => client_hdrs, } # @client = Stomp::Client.new(client_hash) puts "START: Client Connect complete" raise "START: Connection failed!!" unless @client.open? raise "START: Unexpected protocol level!!" if @client.protocol() == Stomp::SPL_10 cf = @client.connection_frame() puts "START: Connection frame\n#{cf}" raise "START: Connect error!!: #{cf.body}" if @client.connection_frame().command == Stomp::CMD_ERROR @session = @client.connection_frame().headers['session'] puts "START: Queue/Topic Name: #{@queue}" puts "START: Session: #{@session}" puts "START: NMSGS: #{@nmsgs}" puts "START: Block: #{@block}" puts "START: Wanted Messages Per Second: #{@umps}" puts "START: Sleep Time: #{@tslt}" $stdout.flush end # start # def shutdown @client.close # te = Time.now.to_f et = te - @nmts avgsz = @to / @nts mps = @nts.to_f / et # fet = sprintf(@ffmts, et) favgsz = sprintf(@ffmts, avgsz) fmps = sprintf(@ffmts, mps) # sep = "=" * 72 puts sep puts "\tNumber of payloads generated: #{@nts}" puts "\tMin Length: #{@cmin}, Max Length: #{@cmax}" puts "\tAVG_SIZE: #{favgsz}, ELAPS_SEC: #{fet}(seconds)" puts "\tNMSGS_PER_SEC: #{fmps}" puts sep # puts "SHUT: Shutdown complete" $stdout.flush end # shutdown # pub def publish m = "Message: " nm = 0 @nmsgs.times do |n| nm += 1 puts "PUB: NEXT MESSAGE NUMBER: #{nm}"; $stdout.flush mo = PayloadGenerator::payload() @to += mo.bytesize() hs = {:session => @session} if @block ip = false @client.publish(@queue, mo, hs) {|m| puts "PUB: HAVE_RECEIPT:\nID: #{m.headers['receipt-id']}" $stdout.flush ip = m } sleep 0.01 until ip else @client.publish(@queue, mo, hs) end # if @block if nm < @nmsgs puts "PUB: start user sleep" sleep @tslt # see issue discussion puts "PUB: end user sleep" end $stdout.flush end # @nmsgs.times do puts "PUB: end of publish" $stdout.flush end # publish end # class # 1.times do |i| rpt = MemoryProfiler.report do e = Issue121Examp03.new e.start e.publish # No subscribes here, just publish # See discussion in issue #121 e.shutdown end n = Time.now nf = "memory_profiler-ng" nf << n.strftime("%Y%m%dT%H%M%S.%N%Z") where_name = File::join(Dir::tmpdir(), nf) rpt.pretty_print(to_file: where_name ) # sleep 1 end # stomp-1.4.4/adhoc/payload_generator_adhoctest.rb0000644000004100000410000000165613120662775022073 0ustar www-datawww-data# -*- encoding: utf-8 -*- if Kernel.respond_to?(:require_relative) require_relative("payload_generator") else $LOAD_PATH << File.dirname(__FILE__) require "payload_generator" end # cmin, cmax = 1292, 67782 ffmts = "%16.6f" # PayloadGenerator::initialize(min= cmin, max= cmax) to, nmts, nts, umps = 0.0, Time.now.to_f, 100, 5.6 # p [ "nmts", nmts ] tslt = 1.0 / umps # p [ "tslt", tslt ] nts.times do |i| ns = PayloadGenerator::payload() to += ns.bytesize # puts "t: #{i+1}, len: #{ns.bytesize}, tslt: #{tslt}" sleep(tslt) # puts "Done sleep!" end # te = Time.now.to_f # p [ "te", te ] et = te - nmts avgsz = to / nts mps = nts.to_f / et # fet = sprintf(ffmts, et) favgsz = sprintf(ffmts, avgsz) fmps = sprintf(ffmts, mps) # puts "=" * 48 puts "\tNumber of payloads generated: #{nts}" puts "\tMin Length: #{cmin}, Max Length: #{cmax}" puts "\tAVG_SIZE: #{favgsz}, ELAPS_SEC: #{fet}(seconds)" puts "\tNMSGS_PER_SEC: #{fmps}" # stomp-1.4.4/adhoc/payload_generator.rb0000644000004100000410000000077213120662775020033 0ustar www-datawww-data# -*- encoding: utf-8 -*- class PayloadGenerator private @@BSTRING = "" public def self.initialize(min = 1, max = 4096) srand() # @@min, @@max = min, max if @@min > @@max @@min, @@max = @@max, @@min warn "Swapping min and max values" end # @@BSTRING = "9" * @@max nil end # of initialize def self.payload i = rand(@@max - @@min) i = 1 if i == 0 i += @@min # puts "DBI: #{i}" @@BSTRING.byteslice(0, i) end end # of class stomp-1.4.4/adhoc/.gitignore0000644000004100000410000000011213120662775015763 0ustar www-datawww-data# No html in this directory *.html *.htm # Typical backup files *~ *.bak stomp-1.4.4/adhoc/issue121_01_conn.rb0000644000004100000410000001004413120662775017216 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'stomp' require 'tmpdir' # Focus on this gem's capabilities. require 'memory_profiler' # require 'memory-profiler' if Kernel.respond_to?(:require_relative) require_relative("stomp_adhoc_common") require_relative("payload_generator") else $LOAD_PATH << File.dirname(__FILE__) require "stomp_adhoc_common" require("payload_generator") end include Stomp11Common # Next of testing around issue #121. # Different memory profiler gem. # Use Stomp#connection to merely send class Issue121Examp01Conn attr_reader :connection, :session # Initialize. def initialize(topic = false) @connection, @session, @topic = nil, nil, topic @nmsgs = nmsgs() @queue = make_destination("issue121/test_01_conn") @id = "issue121_01_conn" @getreceipt = conn_receipt() # @cmin, @cmax = 1292, 67782 # From the issue discussion PayloadGenerator::initialize(min= @cmin, max= @cmax) @ffmts = "%16.6f" # mps = 5.6 # see issue discussion @to, @nmts, @nts, @umps = 0.0, Time.now.to_f, @nmsgs, mps @tslt = 1.0 / @umps end # initialize # Startup def start # connect_hdrs = {"accept-version" => "1.1,1.2", "host" => virt_host, } # connect_hash = { :hosts => [ {:login => login(), :passcode => passcode(), :host => host(), :port => port()}, ], :connect_headers => connect_hdrs, } # @connection = Stomp::Connection.new(connect_hash) puts "START: Connection Connect complete" raise "START: Connection failed!!" unless @connection.open? raise "START: Unexpected protocol level!!" if @connection.protocol == Stomp::SPL_10 cf = @connection.connection_frame puts "START: Connection frame\n#{cf}" raise "START: Connect error!!: #{cf.body}" if @connection.connection_frame.command == Stomp::CMD_ERROR @session = @connection.connection_frame.headers['session'] puts "START: Queue/Topic Name: #{@queue}" puts "START: Session: #{@session}" puts "START: NMSGS: #{@nmsgs}" puts "START: Receipt: #{@getreceipt}" puts "START: Wanted Messages Per Second: #{@umps}" puts "START: Sleep Time: #{@tslt}" $stdout.flush end # start # def shutdown @connection.disconnect() # te = Time.now.to_f et = te - @nmts avgsz = @to / @nts mps = @nts.to_f / et # fet = sprintf(@ffmts, et) favgsz = sprintf(@ffmts, avgsz) fmps = sprintf(@ffmts, mps) # sep = "=" * 72 puts sep puts "\tNumber of payloads generated: #{@nts}" puts "\tMin Length: #{@cmin}, Max Length: #{@cmax}" puts "\tAVG_SIZE: #{favgsz}, ELAPS_SEC: #{fet}(seconds)" puts "\tNMSGS_PER_SEC: #{fmps}" puts sep # puts "SHUT: Shutdown complete" $stdout.flush end # shutdown # def msg_handler m = "Message: " nm = 0 @nmsgs.times do |n| nm += 1 puts "MSH: NEXT MESSAGE NUMBER: #{nm}"; $stdout.flush mo = PayloadGenerator::payload() @to += mo.bytesize() if @getreceipt uuid = @connection.uuid() puts "MSH: Receipt id wanted is #{uuid}" hs = {:session => @session, :receipt => uuid} else hs = {:session => @session} end # Move data out the door @connection.publish(@queue, mo, hs) if @getreceipt r = @connection.receive() puts "MSH: received receipt, id is #{r.headers['receipt-id']}" raise if uuid != r.headers['receipt-id'] end # puts "MSH: start user sleep" sleep @tslt # see issue discussion puts "MSH: end user sleep" $stdout.flush end # @nmsgs.times do puts "MSH: end of msg_handler" $stdout.flush end # msg_handler end # class # 1.times do |i| rpt = MemoryProfiler.report do e = Issue121Examp01Conn.new e.start e.msg_handler # No subscribes here, just msg_handler # See discussion in issue #121 e.shutdown end n = Time.now nf = "memory_profiler-ng" nf << n.strftime("%Y%m%dT%H%M%S.%N%Z") where_name = File::join(Dir::tmpdir(), nf) rpt.pretty_print(to_file: where_name ) # sleep 1 end # stomp-1.4.4/adhoc/issue121_02.rb0000644000004100000410000000745013120662775016211 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'stomp' # Focus on this gem's capabilities. # require 'memory_profiler' require 'memory-profiler' if Kernel.respond_to?(:require_relative) require_relative("stomp_adhoc_common") require_relative("payload_generator") else $LOAD_PATH << File.dirname(__FILE__) require "stomp_adhoc_common" require("payload_generator") end include Stomp11Common # Round 2 of testing around issue #121. class Issue121Examp02 attr_reader :client, :session # Initialize. def initialize(topic = false) @client, @session, @topic = nil, nil, topic @nmsgs = nmsgs() @queue = make_destination("issue121/test_02") @id = "issue121_02" @block = cli_block() # @cmin, @cmax = 1292, 67782 # From the issue discussion PayloadGenerator::initialize(min= @cmin, max= @cmax) @ffmts = "%16.6f" # mps = 5.6 # see issue discussion @to, @nmts, @nts, @umps = 0.0, Time.now.to_f, @nmsgs, mps @tslt = 1.0 / @umps end # initialize # Startup def start # client_hdrs = {"accept-version" => "1.1,1.2", "host" => virt_host, } # client_hash = { :hosts => [ {:login => login(), :passcode => passcode(), :host => host(), :port => port()}, ], :connect_headers => client_hdrs, } # @client = Stomp::Client.new(client_hash) puts "START: Client Connect complete" raise "START: Connection failed!!" unless @client.open? raise "START: Unexpected protocol level!!" if @client.protocol() == Stomp::SPL_10 cf = @client.connection_frame() puts "START: Connection frame\n#{cf}" raise "START: Connect error!!: #{cf.body}" if @client.connection_frame().command == Stomp::CMD_ERROR @session = @client.connection_frame().headers['session'] puts "START: Queue/Topic Name: #{@queue}" puts "START: Session: #{@session}" puts "START: NMSGS: #{@nmsgs}" puts "START: Block: #{@block}" puts "START: Wanted Messages Per Second: #{@umps}" puts "START: Sleep Time: #{@tslt}" $stdout.flush end # start # def shutdown @client.close # te = Time.now.to_f et = te - @nmts avgsz = @to / @nts mps = @nts.to_f / et # fet = sprintf(@ffmts, et) favgsz = sprintf(@ffmts, avgsz) fmps = sprintf(@ffmts, mps) # sep = "=" * 72 puts sep puts "\tNumber of payloads generated: #{@nts}" puts "\tMin Length: #{@cmin}, Max Length: #{@cmax}" puts "\tAVG_SIZE: #{favgsz}, ELAPS_SEC: #{fet}(seconds)" puts "\tNMSGS_PER_SEC: #{fmps}" puts sep # puts "SHUT: Shutdown complete" $stdout.flush end # shutdown # pub def publish m = "Message: " nm = 0 @nmsgs.times do |n| nm += 1 puts "PUB: NEXT MESSAGE NUMBER: #{nm}"; $stdout.flush mo = PayloadGenerator::payload() @to += mo.bytesize() hs = {:session => @session} if @block ip = false @client.publish(@queue, mo, hs) {|m| puts "PUB: HAVE_RECEIPT:\nID: #{m.headers['receipt-id']}" $stdout.flush ip = m } sleep 0.01 until ip else @client.publish(@queue, mo, hs) end # if @block puts "PUB: start user sleep" sleep @tslt # see issue discussion puts "PUB: end user sleep" $stdout.flush end # @nmsgs.times do puts "PUB: end of publish" $stdout.flush end # publish end # class # # :limit => is max number of classes to report on MemoryProfiler::start_daemon( :limit=>25, :delay=>10, :marshal_size=>true, :sort_by=>:absdelta ) # 1.times do |i| rpt = MemoryProfiler.start( :limit=> 25 ) do e = Issue121Examp02.new e.start e.publish # No subscribes here, just publish # See discussion in issue #121 e.shutdown end puts MemoryProfiler.format(rpt) sleep 1 end # MemoryProfiler::stop_daemon stomp-1.4.4/adhoc/README.md0000644000004100000410000000106313120662775015260 0ustar www-datawww-data # The Ruby stomp Gem - Adhoc Items # This directory will contain a variety of artifacts. It is envisioned that content will mostly be of interest to gem developers. However gem clients may find items of interest here as well. ## Issue 121 ## Much of the initial work in this directory has focused on recreating / confirming the high memory use described in [issue 121](https://github.com/stompgem/stomp/issues/121). Resolution of that issue is TDB (2016.05.25). stomp-1.4.4/adhoc/issue121_01.rb0000644000004100000410000000606013120662775016204 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'stomp' # Focus on this gem's capabilities. # require 'memory_profiler' require 'memory-profiler' if Kernel.respond_to?(:require_relative) require_relative("stomp_adhoc_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp_adhoc_common" end include Stomp11Common # Initial testing around issue #121. class Issue121Examp01 attr_reader :client, :session # Initialize. def initialize(topic = false) @client, @session, @topic = nil, nil, topic @nmsgs = nmsgs() @queue = make_destination("issue121/test_01") @id = "issue121_01" @block = cli_block() end # initialize # Startup def start # client_hdrs = {"accept-version" => "1.1,1.2", "host" => virt_host, } # client_hash = { :hosts => [ {:login => login(), :passcode => passcode(), :host => host(), :port => port()}, ], :connect_headers => client_hdrs, } # @client = Stomp::Client.new(client_hash) puts "START: Client Connect complete" raise "START: Connection failed!!" unless @client.open? raise "START: Unexpected protocol level!!" if @client.protocol() == Stomp::SPL_10 cf = @client.connection_frame() puts "START: Connection frame\n#{cf}" raise "START: Connect error!!: #{cf.body}" if @client.connection_frame().command == Stomp::CMD_ERROR @session = @client.connection_frame().headers['session'] puts "START: Queue/Topic Name: #{@queue}" puts "START: Session: #{@session}" puts "START: Block: #{@block}" $stdout.flush end # start # def shutdown @client.close puts "SHUT: Shutdown complete" end # shutdown # pub def publish m = "Message: " nm = 0 @nmsgs.times do |n| nm += 1 puts "PUB: NEXT MESSAGE NUMBER: #{nm}" mo = "#{m} #{n}" puts "PUB: PAYLOAD: #{mo}" hs = {:session => @session} if @block ip = false @client.publish(@queue, mo, hs) {|m| puts "PUB: HAVE_RECEIPT:\nID: #{m.headers['receipt-id']}" ip = m } sleep 0.01 until ip else @client.publish(@queue, mo, hs) end # if @block end # @nmsgs.times do end # publish # sub def subscribe puts "SUB: Subscribe starts For: #{@queue}" rmc, done = 0, false sh = {:id => "#{@id}", :ack => "auto"} @client.subscribe(@queue, sh) {|m| rmc += 1 rm = m puts "SUB: HAVE_MESSAGE:\n#{rm}" if rmc >= @nmsgs puts "SUB: Subscribe is ending for #{@queue}" done = true Thread.done end } sleep 0.01 until done puts "SUB: Receives Done For: #{@queue}" end # subscribe end # class # # puts "BEG: Memory Profiler Version is: #{MemoryProfiler::VERSION}" MemoryProfiler::start_daemon( :limit=>5, :delay=>10, :marshal_size=>true, :sort_by=>:absdelta ) # 5.times do |i| rpt = MemoryProfiler.start( :limit=>10 ) do e = Issue121Examp01.new e.start e.publish e.subscribe e.shutdown end puts MemoryProfiler.format(rpt) sleep 1 end # MemoryProfiler::stop_daemon stomp-1.4.4/Rakefile0000644000004100000410000000464513120662775014401 0ustar www-datawww-data# Copyright 2005-2006 Brian McCallister # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. $:.unshift(File.dirname(__FILE__) + "/lib") require 'rubygems' require 'rake' require 'rake/testtask' require 'rspec/core/rake_task' require "stomp/version" begin require "hanna-nouveau" have_hanna = true rescue LoadError => e have_hanna = false end require "rdoc/task" begin require 'jeweler' Jeweler::Tasks.new do |gem| gem.name = "stomp" gem.version = Stomp::Version::STRING gem.summary = %Q{Ruby client for the Stomp messaging protocol} gem.license = "Apache-2.0" gem.description = %Q{Ruby client for the Stomp messaging protocol. Note that this gem is no longer supported on rubyforge.} gem.email = ["brianm@apache.org", 'marius@stones.com', 'morellon@gmail.com', 'allard.guy.m@gmail.com' ] gem.homepage = "https://github.com/stompgem/stomp" gem.authors = ["Brian McCallister", 'Marius Mathiesen', 'Thiago Morello', 'Guy M. Allard'] gem.add_development_dependency 'rspec', '~> 2.14', '>= 2.14.1' end Jeweler::GemcutterTasks.new rescue LoadError puts "Jeweler not available. Install it with: gem install jeweler" end desc 'Run the specs' RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = ['--colour'] t.pattern = 'spec/**/*_spec.rb' end desc "Rspec : run all with RCov" RSpec::Core::RakeTask.new('spec:rcov') do |t| t.pattern = 'spec/**/*_spec.rb' t.rcov = true t.rcov_opts = ['--exclude', 'gems', '--exclude', 'spec'] end Rake::RDocTask.new do |rdoc| rdoc.main = "README.md" rdoc.rdoc_dir = "doc" rdoc.title = "Stomp" rdoc.options += %w[ --line-numbers --inline-source --charset utf-8 ] if have_hanna rdoc.options += %w[ --format hanna ] end rdoc.rdoc_files.include("README.md", "CHANGELOG.md", "lib/**/*.rb") end Rake::TestTask.new do |t| t.libs << "test" t.test_files = FileList['test/test*.rb'] t.verbose = true end task :default => :spec stomp-1.4.4/bin/0000755000004100000410000000000013120662775013473 5ustar www-datawww-datastomp-1.4.4/bin/catstomp0000755000004100000410000000435513120662775015262 0ustar www-datawww-data#!/usr/bin/env ruby # # Copyright 2006 LogicBlaze Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # begin; require 'rubygems'; rescue; end require 'stomp' # # This simple script is inspired by the netcat utility. It allows you to publish # input into this process to stomp destination. # # Usage: catstomp (destination-name) # # Example: ls | catstomp /topic/foo # Would publish the output of the ls command to the stomp destination /topic/foo # begin if ARGV[0] == '-h' || ARGV[0] == '--help' $stdout.puts $stdout.puts 'Usage: catstomp DESTINATION' $stdout.puts $stdout.puts 'Publishes STDIN to the desired stomp destination' $stdout.puts $stdout.puts 'Example: ls | catstomp /topic/foo' $stdout.puts $stdout.puts 'Defaults:' $stdout.puts "DESTINATION\t/topic/default" $stdout.puts "STOMP_HOST\tlocalhost" $stdout.puts "STOMP_PORT\t61613" $stdout.puts "STOMP_USER" $stdout.puts "STOMP_PASSWORD" $stdout.puts $stdout.puts 'You can override the stomp host, port, user, or password through environment variables' exit 0 end @port = 61613 @host = "localhost" @user = ENV["STOMP_USER"]; @password = ENV["STOMP_PASSWORD"] @host = ENV["STOMP_HOST"] if ENV["STOMP_HOST"] != nil @port = ENV["STOMP_PORT"] if ENV["STOMP_PORT"] != nil @destination = "/topic/default" @destination = $*[0] if $*[0] != nil $stderr.print "Connecting to stomp://#{@host}:#{@port} as #{@user}\n" @conn = Stomp::Connection.open(@user, @password, @host, @port, true) $stderr.print "Sending input to #{@destination}\n" @headers = {'persistent'=>'false'} @headers['reply-to'] = $*[1] if $*[1] != nil STDIN.each_line { |line| @conn.publish @destination, line, @headers } rescue end stomp-1.4.4/bin/stompcat0000755000004100000410000000440313120662775015254 0ustar www-datawww-data#!/usr/bin/env ruby # # Copyright 2006 LogicBlaze Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # begin; require 'rubygems'; rescue; end require 'stomp' # # This simple script is inspired by the netcat utility. It allows you to receive # data from a stomp destination and output it. # # Usage: stompcat (destination-name) # # Example: stompcat /topic/foo # Would display output that arrives at the /topic/foo stomp destination # begin if ARGV[0] == '-h' || ARGV[0] == '--help' $stdout.puts $stdout.puts 'Usage: stompcat DESTINATION' $stdout.puts $stdout.puts 'Receives data from a stomp destination and outputs it to STDOUT' $stdout.puts $stdout.puts 'Example: stompcat /topic/foo' $stdout.puts $stdout.puts 'Defaults:' $stdout.puts "DESTINATION\t/topic/default" $stdout.puts "STOMP_HOST\tlocalhost" $stdout.puts "STOMP_PORT\t61613" $stdout.puts "STOMP_USER\t" $stdout.puts "STOMP_PASSWORD\t" $stdout.puts $stdout.puts 'You can override the host, port, user, or password through environment variables' exit 0 end @port = 61613 @host = "localhost" @user = ENV["STOMP_USER"]; @password = ENV["STOMP_PASSWORD"] @host = ENV["STOMP_HOST"] if ENV["STOMP_HOST"] != nil @port = ENV["STOMP_PORT"] if ENV["STOMP_PORT"] != nil @destination = "/topic/default" @destination = $*[0] if $*[0] != nil $stderr.print "Connecting to stomp://#{@host}:#{@port} as #{@user}\n" @conn = Stomp::Connection.open(@user, @password, @host, @port, true) $stderr.print "Getting output from #{@destination}\n" @conn.subscribe(@destination, { :ack =>"client" }) while true @msg = @conn.receive $stdout.print @msg.body $stdout.flush @conn.ack @msg.headers["message-id"] end rescue end stomp-1.4.4/examples/0000755000004100000410000000000013120662775014541 5ustar www-datawww-datastomp-1.4.4/examples/ssl_ctxoptions.rb0000644000004100000410000000616713120662775020173 0ustar www-datawww-data# -*- encoding: utf-8 -*- # require "rubygems" require "stomp" # # == Demo User Control of SSLContext options contents # # Roughly based on example ssl_uc1.rb. # See comments in that example for more detail. # # Not tested with jruby. YMMV. # class ExampleSSLCtxOptions # Initialize. def initialize end # Run example 1 def run1 require 'openssl' unless defined?(OpenSSL) puts "run method ...." # Define SSL Options to be used. This code is copied from the defaults # in later versions of Ruby V2.x (which has been backported to 1.9.3). # # Connection / Example 1 of 2, user supplied options. # # Build SSL Options per user requirements: this is just one # particular/possible example of setting SSL context options. opts = OpenSSL::SSL::OP_ALL # Perhaps. If you need/want any of these you will know it. # This is exactly what is done in later versions of Ruby 2.x (also has # been backported by Ruby team to later versions of 1.9.3). opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) opts |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) opts |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) urc = defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/ ? true : false # Pass options to SSLParams constructor. ssl_opts = Stomp::SSLParams.new(:ssl_ctxopts => opts, # SSLContext options to set :use_ruby_ciphers => urc, :fsck => true) sport = ENV["STOMP_PORT"].to_i hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => 'localhost', :port => sport, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSLContext Options Set: #{opts}" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" # c.disconnect end # Run example 2 def run2 puts "run2 method ...." # # Connection / Example 2 of 2, gem supplied options. # urc = defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/ ? true : false # Use gem method to define SSL options. Exactly the same as the # options used in Example 1 above. ssl_opts = Stomp::SSLParams.new(:ssl_ctxopts => Stomp::Connection::ssl_v2xoptions(), :use_ruby_ciphers => urc, :fsck => true) sport = ENV["STOMP_PORT"].to_i hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => 'localhost', :port => sport, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSLContext Options Set: #{Stomp::Connection::ssl_v2xoptions()}" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" # c.disconnect end end # e = ExampleSSLCtxOptions.new e.run1 e.run2 stomp-1.4.4/examples/examplogger.rb0000644000004100000410000001651013120662775017403 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'logger' # use the standard Ruby logger ..... # == Example STOMP call back logger class. # # Optional callback methods: # # * on_connecting: connection starting # * on_connected: successful connect # * on_connectfail: unsuccessful connect (will usually be retried) # * on_disconnect: successful disconnect # # * on_miscerr: on miscellaneous xmit/recv errors # # * on_publish: publish called # * on_subscribe: subscribe called # * on_unsubscribe: unsubscribe called # # * on_begin: begin called # * on_ack: ack called # * on_nack: nack called # * on_commit: commit called # * on_abort: abort called # # * on_receive: receive called and successful # # * on_ssl_connecting: SSL connection starting # * on_ssl_connected: successful SSL connect # * on_ssl_connectfail: unsuccessful SSL connect (will usually be retried) # # * on_hbread_fail: unsuccessful Heartbeat read # * on_hbwrite_fail: unsuccessful Heartbeat write # * on_hbfire: on any send or receive heartbeat # # All methods are optional, at the user's requirements. # # If a method is not provided, it is not called (of course.) # # IMPORTANT NOTE: in general, call back logging methods *SHOULD* not raise exceptions, # otherwise the underlying STOMP connection may fail in mysterious ways. # # There are two useful exceptions to this rule for: # # * on_connectfail # * on_ssl_connectfail # # These two methods can raise a Stomp::Errors::LoggerConnectionError. If this # exception is raised, it is passed up the chain to the caller. # # Callback parameters: are a copy of the @parameters instance variable for # the Stomp::Connection. # # A logger class may optionally inherit from the provided NullLogger # # # class Slogger < Stomp::NullLogger # class Slogger # Initialize a new callback logger instance. def initialize(init_parms = nil) _init @log.info("Logger initialization complete.") end def _init @log = Logger::new(STDOUT) # User preference @log.level = Logger::DEBUG # User preference end def marshal_dump [] end def marshal_load(array) _init end # Log connecting events def on_connecting(parms) begin @log.debug "Connecting: #{info(parms)}" rescue @log.debug "Connecting oops" end end # Log connected events def on_connected(parms) begin @log.debug "Connected: #{info(parms)}" rescue @log.debug "Connected oops" end end # Log connectfail events def on_connectfail(parms) begin @log.debug "Connect Fail #{info(parms)}" rescue @log.debug "Connect Fail oops" end =begin rescue # An example LoggerConnectionError raise @log.debug "Connect Fail, will raise" raise Stomp::Error::LoggerConnectionError.new("quit from connect fail") end =end end # Log disconnect events def on_disconnect(parms) begin @log.debug "Disconnected #{info(parms)}" rescue @log.debug "Disconnected oops" end end # Log miscellaneous errors def on_miscerr(parms, errstr) begin @log.debug "Miscellaneous Error #{info(parms)}" @log.debug "Miscellaneous Error String #{errstr}" rescue @log.debug "Miscellaneous Error oops" end end # Log Subscribe def on_subscribe(parms, headers) begin @log.debug "Subscribe Parms #{info(parms)}" @log.debug "Subscribe Headers #{headers}" rescue @log.debug "Subscribe oops" end end # Log UnSubscribe def on_unsubscribe(parms, headers) begin @log.debug "UnSubscribe Parms #{info(parms)}" @log.debug "UnSubscribe Headers #{headers}" rescue @log.debug "UnSubscribe oops" end end # Log Publish def on_publish(parms, message, headers) begin @log.debug "Publish Parms #{info(parms)}" @log.debug "Publish Message #{message}" @log.debug "Publish Headers #{headers}" rescue @log.debug "Publish oops" end end # Log Receive def on_receive(parms, result) begin @log.debug "Receive Parms #{info(parms)}" @log.debug "Receive Result #{result}" rescue @log.debug "Receive oops" end end # Log Begin def on_begin(parms, headers) begin @log.debug "Begin Parms #{info(parms)}" @log.debug "Begin Result #{headers}" rescue @log.debug "Begin oops" end end # Log Ack def on_ack(parms, headers) begin @log.debug "Ack Parms #{info(parms)}" @log.debug "Ack Result #{headers}" rescue @log.debug "Ack oops" end end # Log NAck def on_nack(parms, headers) begin @log.debug "NAck Parms #{info(parms)}" @log.debug "NAck Result #{headers}" rescue @log.debug "NAck oops" end end # Log Commit def on_commit(parms, headers) begin @log.debug "Commit Parms #{info(parms)}" @log.debug "Commit Result #{headers}" rescue @log.debug "Commit oops" end end # Log Abort def on_abort(parms, headers) begin @log.debug "Abort Parms #{info(parms)}" @log.debug "Abort Result #{headers}" rescue @log.debug "Abort oops" end end # Stomp 1.1+ - heart beat read (receive) failed. def on_hbread_fail(parms, ticker_data = {}) begin @log.debug "Hbreadf Parms #{info(parms)}" @log.debug "Hbreadf Result #{ticker_data.inspect}" rescue @log.debug "Hbreadf oops" end end # Stomp 1.1+ - heart beat send (transmit) failed. def on_hbwrite_fail(parms, ticker_data = {}) begin @log.debug "Hbwritef Parms #{info(parms)}" @log.debug "Hbwritef Result #{ticker_data.inspect}" rescue @log.debug "Hbwritef oops" end end # Log SSL connection start. def on_ssl_connecting(parms) begin @log.debug "SSL Connecting Parms #{info(parms)}" rescue @log.debug "SSL Connecting oops" end end # Log a successful SSL connect. def on_ssl_connected(parms) begin @log.debug "SSL Connected Parms #{info(parms)}" rescue @log.debug "SSL Connected oops" end end # Log an unsuccessful SSL connect. def on_ssl_connectfail(parms) begin @log.debug "SSL Connect Fail Parms #{info(parms)}" @log.debug "SSL Connect Fail Exception #{parms[:ssl_exception]}, #{parms[:ssl_exception].message}" rescue @log.debug "SSL Connect Fail oops" end =begin # An example LoggerConnectionError raise @log.debug "SSL Connect Fail, will raise" raise Stomp::Error::LoggerConnectionError.new("quit from SSL connect") =end end # Log heart beat fires def on_hbfire(parms, srind, firedata = {}) begin @log.debug "HeartBeat Fire Parms #{info(parms)}" @log.debug "HeartBeat Fire Send/Receive #{srind}" rescue @log.debug "HeartBeat Fire oops" end end private # Example information extract. def info(parms) # # Available in the parms Hash: # parms[:cur_host] # parms[:cur_port] # parms[:cur_login] # parms[:cur_passcode] # parms[:cur_ssl] # parms[:cur_recondelay] # parms[:cur_parseto] # parms[:cur_conattempts] # parms[:openstat] # # For the on_ssl_connectfail callback these are also available: # parms[:ssl_exception] # "Host: #{parms[:cur_host]}, Port: #{parms[:cur_port]}, Login: #{parms[:cur_login]}, Passcode: #{parms[:cur_passcode]}, ssl: #{parms[:cur_ssl]}" end end # of class stomp-1.4.4/examples/ssl_uc2.rb0000644000004100000410000000331513120662775016442 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # if Kernel.respond_to?(:require_relative) require_relative("./ssl_common") else $LOAD_PATH << File.dirname(__FILE__) require "ssl_common" end include SSLCommon # # == SSL Use Case 2 - server does *not* authenticate client, client *does* authenticate server # # Subcase 2.A - Message broker configuration does *not* require client authentication # # - Expect connection success # - Expect a verify result of 0 becuase the client did authenticate the # server's certificate. # # Subcase 2.B - Message broker configuration *does* require client authentication # # - Expect connection failure (broker must be sent a valid client certificate) # class ExampleSSL2 # Initialize. def initialize # Change the following as needed. @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612 end # Run example. def run puts "Connect host: #{@host}, port: #{@port}" ts_flist = [] # Possibly change/override the cert data here. ts_flist << "#{ca_loc()}/#{ca_cert()}" ssl_opts = Stomp::SSLParams.new(:ts_files => ts_flist.join(","), :fsck => true) # hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSL Use Case 2" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" c.disconnect end end # e = ExampleSSL2.new e.run stomp-1.4.4/examples/ssl_ucx_default_ciphers.rb0000644000004100000410000000164313120662775021773 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # require "rubygems" require "stomp" # # == Example: Use Ruby Supplied Ciphers # # If you use SSLParams, and need the _default_ Ruby ciphers, this is how. # # NOTE: JRuby users may find that this is a *required* action. YMMV. # class ExampleRubyCiphers # Initialize. def initialize end # Run example. def run ssl_opts = Stomp::SSLParams.new(:use_ruby_ciphers => true) # Plus other parameters as needed # # SSL Use Case: Using default Stomp ciphers # hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => 'localhost', :port => 61612, :ssl => ssl_opts}, ] } # puts "Connect starts, SSL , Use Default Ruby Ciphers" c = Stomp::Connection.new(hash) puts "Connect completed" # c.disconnect end end # e = ExampleRubyCiphers.new e.run stomp-1.4.4/examples/ssl_newparm.rb0000644000004100000410000000204713120662775017423 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # require "rubygems" require "stomp" # # == Demo override of SSLContext.new parameters. # # Based roughly on example ssl_uc1.rb. # # class ExampleSSLNewParm # Initialize. def initialize end # Run example. def run ssl_opts = Stomp::SSLParams.new hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => 'localhost', :port => 61612, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner :sslctx_newparm => :SSLv2, # An example should you: # a) Actually want SSLv2 *AND* # b) Your Ruby version supports SSLv2 *AND* # c) Your broker supports and allows SSLv2 } # puts "Connect starts, SSL Use Case 1" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" # c.disconnect end end # e = ExampleSSLNewParm.new e.run stomp-1.4.4/examples/get11conn_ex1.rb0000644000004100000410000000640713120662775017451 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" end include Stomp11Common # # == Stomp 1.1 Receive Example 1 # # Purpose: to demonstrate receiving messages using Stomp 1.1. # class Receive11Example1 # Initialize. def initialize end # Run example. def run conn = get_connection() # Use helper method to obtain a Stomp#connection raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11 # # To start receiving messages, you must first subscribe. This is similar # to using Stomp 1.0. # # However, with Stomp 1.1: # # * for subscribe, the 'id' header is now _required_ # * for unsubscribe, the 'id' header is now _required_ # # The 'id' header specifies a 'subscription id' that _must_ be unique for # the current session. # qname = "/queue/nodea.nodeb.nodec" # # Here is an example of allowed functionality in 1.0 that is not allowed in 1.1: # begin conn.subscribe qname rescue RuntimeError => sre puts "Rescue: #{sre}, #{sre.message}" end # # So, you must specify an 'id' header. And it must be unique within the # current session. # # You can build your own unique ids of course. That is a valid option. # In order to provide you with some assistance in generating unique ids, # two convenience methods are provided with the connection: # # * sha1 - generate a sha1 hash of some data you supply. This may be sufficient for many purposes. # * uuid - generate a type 4 UUID. This would be sufficient in all cases. # # Get a sha1: # sha1 = conn.sha1(qname) # sha1 of the queue name perhaps puts "Queue name: #{qname}, sha1: #{sha1}" # # Or perhaps a different sha1: # tn = Time.now.to_f.to_s # Maybe unique itself. sha1 = conn.sha1(tn) puts "Time now: #{tn}, sha1: #{sha1}" # # Or a Type 4 UUID: # uuid = conn.uuid() puts "Type 4 UUID: #{uuid}" # # You can specify the 'id' in the subscribe call in one of two ways: # # a) In the headers parameter # b) In the third positional parameter, the subId # # So, using the 'uuid', either: # # a) conn.subscribe qname, {'id' => uuid} # b) conn.subscribe qname, {}, uuid # conn.subscribe qname, {'id' => uuid} # First style # # Within a session, you may not subscribe to the same subscription id. # begin conn.subscribe qname, {'id' => uuid} # Second time rescue RuntimeError => sre puts "Rescue: #{sre}, #{sre.message}" end # # Once you have subscribed, you may receive as usual # 1.upto(nmsgs()) do received = conn.receive puts "Received data: #{received.body}" end # # For unsubscribe, you must use the 'id' you used on subscribe. # # You have the same options for placing this id in the headers or in the 3rd # positional parameter. # conn.unsubscribe qname, {}, uuid # Second style # # And finally, disconnect. # conn.disconnect end end # e = Receive11Example1.new e.run stomp-1.4.4/examples/client11_ex1.rb0000644000004100000410000000556013120662775017271 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" end include Stomp11Common # # == Stomp 1.1 Client Example 1 # # Purpose: to demonstrate a connect and disconnect sequence using Stomp 1.1 # with the Stomp#Client interface. # class Client11Example1 # Initialize. def initialize end # Run example. def run # Note: Stomp#Client does not provide a positional set of parameters that # contain a 'connect_headers' parameter. To use the Stomp#Client interface # you _must_ use a 'hashed' set of parameters. # # Create connection headers # ========================= # # The two headers used here are _required_ by the specification. # client_hdrs = {"accept-version" => "1.1", # Demand a 1.1 connection (use a CSV list if you will consider multiple versions) "host" => virt_host, # The 1.1 vhost (could be different than connection host) } # No heartbeats here: there will be none for this connection # # Create the connect hash. # ======================== # client_hash = { :hosts => [ {:login => login, :passcode => passcode, :host => host, :port => port}, ], :connect_headers => client_hdrs, } # # Get a connection # ================ # client = Stomp::Client.new(client_hash) puts "Client Connect complete" # # Let's just do some sanity checks, and look around. # raise "Connection failed!!" unless client.open? # # Is this really a 1.1 conection? (For clients, 'protocol' is a public method. # The value will be '1.0' for those types of connections.) # raise "Unexpected protocol level" if client.protocol() != Stomp::SPL_11 # # The broker _could_ have returned an ERROR frame (unlikely). # For clients, 'connection_frame' is a public method. # raise "Connect error: #{client.connection_frame().body}" if client.connection_frame().command == Stomp::CMD_ERROR # # Examine the CONNECT response (the connection_frame()). # puts "Connected Headers required to be present:" puts "Connect version - \t#{client.connection_frame().headers['version']}" puts puts "Connected Headers that are optional:" puts "Connect server - \t\t#{client.connection_frame().headers['server']}" puts "Session ID - \t\t\t#{client.connection_frame().headers['session']}" puts "Server requested heartbeats - \t#{client.connection_frame().headers['heart-beat']}" # # Finally close # ============= # client.close # Business as usual, just like 1.0 puts "Client close complete" end end # e = Client11Example1.new e.run stomp-1.4.4/examples/logexamp_ssl.rb0000644000004100000410000000557113120662775017573 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' require 'stomp' require 'logger' # for the 'local' logger # if Kernel.respond_to?(:require_relative) require_relative("./examplogger") else $LOAD_PATH << File.dirname(__FILE__) require "examplogger" end # # == A STOMP::Connection program which uses the callback logging facility. # class SSLLoggerExample # Initialize. def initialize end # Run example. def run llog = Logger::new(STDOUT) llog.level = Logger::DEBUG llog.debug "LESSL Starting" # ////////////////////////////////////////////////////////////////////////////// mylog = Slogger::new # The client provided STOMP callback logger # ////////////////////////////////////////////////////////////////////////////// user = ENV['STOMP_USER'] ? ENV['STOMP_USER'] : 'guest' password = ENV['STOMP_PASSWORD'] ? ENV['STOMP_PASSWORD'] : 'guest' host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : 'localhost' port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612 # ////////////////////////////////////////////////////////////////////////////// # A hash type connect *MUST* be used to enable callback logging. # ////////////////////////////////////////////////////////////////////////////// hash = { :hosts => [ {:login => user, :passcode => password, :host => host, :port => port, :ssl => true}, # Or provide your insance of SSLParams instead ], :logger => mylog, # This enables callback logging! :max_reconnect_attempts => 2, } # ////////////////////////////////////////////////////////////////////////////// # For a Connection: llog.debug "LESSL Connection processing starts" conn = Stomp::Connection.new(hash) conn.disconnect # ////////////////////////////////////////////////////////////////////////////// llog.debug "LESSL Connection processing complete" # ////////////////////////////////////////////////////////////////////////////// # For a Client: llog.debug "LESSL Client processing starts" conn = Stomp::Client.new(hash) conn.close # ////////////////////////////////////////////////////////////////////////////// llog.debug "LESSL Client processing complete" # ////////////////////////////////////////////////////////////////////////////// # For a Connection with other calls: llog.debug "LESSL Connection Enhanced processing starts" conn = Stomp::Connection.new(hash) # dest = "/queue/loggerq1" conn.publish dest, "a logger message" conn.subscribe dest msg = conn.receive conn.disconnect # ////////////////////////////////////////////////////////////////////////////// llog.debug "LESSL Connection Enhanced processing complete" # ////////////////////////////////////////////////////////////////////////////// llog.debug "LESSL Ending" end end # e = SSLLoggerExample.new e.run stomp-1.4.4/examples/conn11_hb1.rb0000644000004100000410000000303213120662775016715 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") require_relative("./examplogger") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" require "examplogger" end include Stomp11Common # # == Stomp 1.1 Heartbeat Example 1 # # Purpose: to demonstrate that heart beats can work. # class HeartBeatExample1 # Initialize. def initialize end # Run example. def run # Create connection headers # ========================= # conn_hdrs = {"accept-version" => "1.1", # 1.1 "host" => virt_host, # vhost "heart-beat" => "5000,10000", # heartbeats } # Create a logger for demonstration purposes logger = Slogger.new # Connect - a paramaterized request. conn = Stomp::Connection.new(login, passcode, host, port, # Normal connect parms false, # Not reliable, the default for a paramaterized connection 5, # Connect redelay, the default for a paramaterized connection conn_hdrs) # The 1.1 connection parameters / headers puts "Connection connect complete" # raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11 # conn.set_logger(logger) # Connection uses a logger sleep 65 conn.set_logger(nil) # No logging # conn.disconnect # Get out puts "Connection disconnect complete" end end # e = HeartBeatExample1.new e.run stomp-1.4.4/examples/ssl_uc1.rb0000644000004100000410000000324613120662775016444 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # # if Kernel.respond_to?(:require_relative) require_relative("./ssl_common") else $LOAD_PATH << File.dirname(__FILE__) require "ssl_common" end include SSLCommon # # == SSL Use Case 1 - server does *not* authenticate client, client does *not* authenticate server # # Subcase 1.A - Message broker configuration does *not* require client authentication # # - Expect connection success # - Expect a verify result of 20 becuase the client did not authenticate the # server's certificate. # # Subcase 1.B - Message broker configuration *does* require client authentication # # - Expect connection failure (broker must be sent a valid client certificate) # class ExampleSSL1 # Initialize. def initialize # Change the following as needed. @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612 end # Run example. def run puts "Connect host: #{@host}, port: #{@port}" ssl_opts = Stomp::SSLParams.new # or ssl_opts = true (for this use case) #### ssl_opts = false # for testing HandShakeDetectedError exception hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSL Use Case 1" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" # c.disconnect end end # e = ExampleSSL1.new e.run stomp-1.4.4/examples/topic_consumer.rb0000644000004100000410000000113713120662775020121 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' require 'stomp' # # == Example topic consumer. # class ExampleTopicConsumer # Initialize. def initialize end # Run example. def run client = Stomp::Client.new("failover://(stomp://:@localhost:61613,stomp://:@remotehost:61613)?initialReconnectDelay=5000&randomize=false&useExponentialBackOff=false") puts "Subscribing to /topic/ronaldo" client.subscribe("/topic/ronaldo") do |msg| puts msg.to_s puts "----------------" end loop do sleep(1) puts "." end end end # e = ExampleTopicConsumer.new e.run stomp-1.4.4/examples/topic_publisher.rb0000644000004100000410000000116113120662775020260 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' require 'stomp' # # == Example topic publisher. # class ExampleTopicPublisher # Initialize. def initialize end # Run example. def run client = Stomp::Client.new("failover://(stomp://:@localhost:61613,stomp://:@remotehost:61613)?initialReconnectDelay=5000&randomize=false&useExponentialBackOff=false") message = "ronaldo #{ARGV[0]}" for i in (1..50) puts "Sending message" client.publish("/topic/ronaldo", "#{i}: #{message}") puts "(#{Time.now})Message sent: #{i}" sleep 0.2 end end end # e = ExampleTopicPublisher.new e.run stomp-1.4.4/examples/ssl_uc4_ciphers.rb0000644000004100000410000000516413120662775020165 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # if Kernel.respond_to?(:require_relative) require_relative("./ssl_common") else $LOAD_PATH << File.dirname(__FILE__) require "ssl_common" end include SSLCommon # # == SSL Use Case 4 - User Supplied Ciphers # # If you need your own ciphers list, this is how. # Stomp's default list will work in many cases. If you need to use this, you # will know it because SSL connect will fail. In that case, determining # _what_ should be in the list is your responsibility. # class ExampleSSL4C # Initialize. def initialize # Change the following as needed. @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612 end # Run example. def run ciphers_list = [["DHE-RSA-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["DHE-DSS-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["AES256-SHA", "TLSv1/SSLv3", 256, 256], ["EDH-RSA-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["EDH-DSS-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DHE-RSA-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["DHE-DSS-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["AES128-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-MD5", "TLSv1/SSLv3", 128, 128], ["EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EXP-EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-RC2-CBC-MD5", "TLSv1/SSLv3", 40, 128], ["EXP-RC4-MD5", "TLSv1/SSLv3", 40, 128]] # # SSL Use Case 4 # # Possibly change the cert file(s) name(s) here. ssl_opts = Stomp::SSLParams.new( :key_file => "#{cli_loc()}/#{pck()}", # the client's private key, private data :cert_file => "#{cli_loc()}/#{cli_cert()}", # the client's signed certificate :ts_files => "#{ca_loc()}/#{ca_cert()}", # The CA's signed sertificate :fsck => true # Check that files exist first ) # hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSL Use Case 4" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" c.disconnect end private def pck() "client.key" end end # e = ExampleSSL4C.new e.run stomp-1.4.4/examples/artemis/0000755000004100000410000000000013120662775016205 5ustar www-datawww-datastomp-1.4.4/examples/artemis/cliwaiter_reliable.rb0000644000004100000410000000303213120662775022352 0ustar www-datawww-data# -*- encoding: utf-8 -*- # require 'stomp' # if Kernel.respond_to?(:require_relative) require_relative("artlogger") else $LOAD_PATH << File.dirname(__FILE__) require "artlogger" end # class CliWaiter # Initialize. def initialize # Change the following as needed. # mylog = Slogger::new # a stomp callback logger # @gem_retries = true @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 31613 # Artemis here @slt = 75 @ver = ENV['STOMP_PROTOCOL'] ? ENV['STOMP_PROTOCOL'] : "1.2" @hbdata = ENV['STOMP_HEARTBEATS'] ? ENV['STOMP_HEARTBEATS'] : "0,0" @conn_hdrs = {"accept-version" => @ver, # version "host" => "localhost", # vhost "heart-beat" => @hbdata, # heartbeats } @hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port}, ], :reliable => @gem_retries, # reliable controls retries by the gem :logger => mylog, # This enables callback logging! :autoflush => true, :connect_headers => @conn_hdrs, :initial_reconnect_delay => 1.0, # initial delay before reconnect (secs) :use_exponential_back_off => false, # don't backoff :max_reconnect_attempts => 3, # retry 3 times } end # Run example. def run puts "Connect host: #{@host}, port: #{@port}" puts "Connect hash: #{@hash}" # puts "CliWaiter Starts" c = Stomp::Client.new(@hash) # while true puts "CliWaiter Sleeps: #{@slt} seconds" sleep @slt end # c.close end end # # e = CliWaiter.new e.run stomp-1.4.4/examples/artemis/artlogger.rb0000644000004100000410000000146413120662775020525 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'logger' # use the standard Ruby logger ..... # class Slogger # Initialize a new callback logger instance. def initialize(init_parms = nil) _init @log.info("Logger initialization complete.") end def _init @log = Logger::new(STDERR) # User preference @log.level = Logger::DEBUG # User preference end # Log connecting events def on_connecting(parms) begin @log.debug "Connecting: #{parms}" rescue Exception => ex @log.debug "Connecting oops" print ex.backtrace.join("\n") end end # Log connected events def on_connected(parms) begin @log.debug "Connected: #{parms}" rescue Exception => ex @log.debug "Connected oops" print ex.backtrace.join("\n") end end private end # of class stomp-1.4.4/examples/artemis/cliwaiter_not_reliable.rb0000644000004100000410000000314013120662775023232 0ustar www-datawww-data# -*- encoding: utf-8 -*- # require 'stomp' # class CliWaiter @@retry_num = 0 @@max_retries = 3 # Initialize. def initialize # Change the following as needed. @gem_retries = false @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 31613 # Artemis here @slt = 75 @ver = ENV['STOMP_PROTOCOL'] ? ENV['STOMP_PROTOCOL'] : "1.2" @hbdata = ENV['STOMP_HEARTBEATS'] ? ENV['STOMP_HEARTBEATS'] : "0,0" @conn_hdrs = {"accept-version" => @ver, # version "host" => "localhost", # vhost "heart-beat" => @hbdata, # heartbeats } @hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port}, ], :reliable => @gem_retries, # reliable controls retries by the gem :autoflush => true, :connect_headers => @conn_hdrs, } p [ "DB1", @hash ] @do_retry=true # Retry ourself, gem code will not because of :reliable => end # Run example. def run begin @@retry_num += 1 puts "Try Number: #{@@retry_num}" puts "Connect host: #{@host}, port: #{@port}" puts "Connect hash: #{@hash.inspect}" # puts "CliWaiter Starts" c = Stomp::Client.new(@hash) # puts "CliWaiter Sleeps: #{@slt} seconds" sleep @slt # c.close puts "CliWaiter Ends" rescue Exception => ex puts "Kaboom, we are in trouble" puts "Exception Message: #{ex.message}" puts "Exception Class: #{ex.class}" puts "The gory details:" print ex.backtrace.join("\n") if @do_retry && @@retry_num < @@max_retries retry else raise end end end end # # e = CliWaiter.new e.run stomp-1.4.4/examples/ssl_uc3.rb0000644000004100000410000000374313120662775016450 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # if Kernel.respond_to?(:require_relative) require_relative("./ssl_common") else $LOAD_PATH << File.dirname(__FILE__) require "ssl_common" end include SSLCommon # # == SSL Use Case 3 - server *does* authenticate client, client does *not* authenticate server # # Subcase 3.A - Message broker configuration does *not* require client authentication # # - Expect connection success # - Expect a verify result of 20 becuase the client did not authenticate the # server's certificate. # # Subcase 3.B - Message broker configuration *does* require client authentication # # - Expect connection success if the server can authenticate the client certificate # - Expect a verify result of 20 because the client did not authenticate the # server's certificate. # class ExampleSSL3 # Initialize. def initialize # Change the following as needed. @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612 end # Run example. def run puts "Connect host: #{@host}, port: #{@port}" # Possibly change the cert file(s) name(s) here. ssl_opts = Stomp::SSLParams.new( :key_file => "#{cli_loc()}/#{pck()}", # the client's private key, private data :cert_file => "#{cli_loc()}/#{cli_cert()}", # the client's signed certificate :fsck => true # Check that the files exist first ) # hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSL Use Case 3" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" c.disconnect end private def pck() "client.key" end end # e = ExampleSSL3.new e.run stomp-1.4.4/examples/ssl_common.rb0000644000004100000410000000373413120662775017246 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Common Stomp 1.1 code. # require "rubygems" if RUBY_VERSION < "1.9" require "stomp" # module SSLCommon # CA Data. # CA file location/directory. Change or specify. # This is the author's default. def ca_loc() ENV['CA_FLOC'] || "/ad3/gma/sslwork/2013-extended-02" # The CA cert location end # CA file. Change or specify. # This is the author's default. def ca_cert() ENV['CA_FILE'] || "TestCA.crt" # The CA cert File end # CA private key file. Change or specify. # This is the author's default. # This file should not be exposed to the outside world. # Not currently used in stomp examples. def ca_key() ENV['CA_KEY'] || nil # The CA private key File end # Client Data. # Client file location/directory. Change or specify. # This is the author's default. def cli_loc() ENV['CLI_FLOC'] || "/ad3/gma/sslwork/2013-extended-02" # The client cert location end # Client cert file. Change or specify. # This is the author's default. def cli_cert() ENV['CLI_FILE'] || "client.crt" # The client cert File end # Client private keyfile. Change or specify. # This is the author's default. # This file should not be exposed to the outside world. def cli_key() ENV['CLI_KEY'] || nil # The client private key File end # Server Data. # Server file location/directory. Change or specify. # This is the author's default. # Not currently used in stomp examples. def svr_loc() ENV['SVR_FLOC'] || "/ad3/gma/sslwork/2013-extended-02" # The server cert location end # Server cert file. Change or specify. # This is the author's default. # Not currently used in stomp examples. def svr_cert() ENV['SVR_FILE'] || "server.crt" # The server cert File end # Server private keyfile. Change or specify. # This is the author's default. # This file should not be exposed to the outside world. # Not currently used in stomp examples. def svr_key() ENV['SVR_KEY'] || nil # The server private key File end end stomp-1.4.4/examples/publish_file.rb0000644000004100000410000000401713120662775017535 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' require 'stomp' if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") require_relative("./lflogger") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" require "./lflogger" end include Stomp11Common # # Used primarily for testing performance when sending "large" messages. # "large" => YMMV # class FileSender # Initialize. def initialize end # Run example. def run p [ "pub001", Thread.current ] publogger = Slogger::new start_time = Time.now.to_f fname = ARGV[0] puts "PUBF: File Name: #{fname}" file = open(fname, "r") rs = Time.now.to_f buff = file.read re = Time.now.to_f ppt = sprintf("%20.6f", re - rs) puts "PUBF: File size: #{buff.respond_to?(:bytesize) ? buff.bytesize : buff.length}" puts "PUBF: File read: #{ppt} seconds" file.close # client_hdrs = {"accept-version" => "1.1", "host" => virt_host(), } client_hash = { :hosts => [ {:login => login(), :passcode => passcode(), :host => host(), :port => port()}, ], :connect_headers => client_hdrs, :logger => publogger, :reliable => false, ### *NOTE* } # # p [ "ch", client_hash ] client = Stomp::Client.new(client_hash) qname = ENV['STOMP_DEST'] ? ENV['STOMP_DEST'] : "/queue/a.big.file" puts "PUBF: Qname is: #{qname}" # Try to gracefully handle files that exceed broker size limits. ph = {:presistent => true} ph['suppress_content_length'] = 'yes' if suppresscl() puts "PUBF: Headers are: #{ph.inspect}" begin client.publish(qname, buff, ph) rescue puts "PUBF: exception on publish: #{$!}" end sleep 0.1 e = client.poll() # Check for unsolicited messages from broker puts "PUBF: unexpected broker message: #{e}" if e client.close end_time = Time.now.to_f ppt = sprintf("%20.6f", end_time - start_time) puts "PUBF: File published: #{ppt} seconds" end end # e = FileSender.new e.run stomp-1.4.4/examples/client11_putget1.rb0000644000004100000410000000420713120662775020162 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" end include Stomp11Common # # == Stomp 1.1 Client Putter/Getter Example 1 # # This is much like sending and receiving with a Stomp::Connection. # class Client11PutGet1 # Initialize. def initialize end # Run example. def run # client_hdrs = {"accept-version" => "1.1", # Demand a 1.1 connection (use a CSV list if you will consider multiple versions) "host" => virt_host, # The 1.1 vhost (could be different than connection host) } # No heartbeats here: there will be none for this connection # client_hash = { :hosts => [ {:login => login, :passcode => passcode, :host => host, :port => port}, ], :connect_headers => client_hdrs, } # client = Stomp::Client.new(client_hash) puts "Client Connect complete" # raise "Unexpected protocol level" if client.protocol() != Stomp::SPL_11 # qname = "/queue/client.nodea.nodeb.nodec" data = "message payload: #{Time.now.to_f}" headers = {} # Send it client.publish qname, data puts "Publish complete" # Receive uuid = client.uuid() # uuid for Stomp::Client is a public method message = nil # Clients must pass a receive block. This is business as usual, required for 1.0. # For 1.1, a unique subscription id is required. client.subscribe(qname, {'id' => uuid}) {|m| message = m } sleep 0.1 until message # Wait for completion puts "Subscribe and receive complete" # Unsubscribe, with the unique id client.unsubscribe qname, {'id' => uuid} # Sanity checks for this example .... raise "Unexpected data" if data != message.body raise "Bad subscription header" if uuid != message.headers['subscription'] # client.close # Business as usual, just like 1.0 puts "Client close complete" end end # e = Client11PutGet1.new e.run stomp-1.4.4/examples/ssl_uc2_ciphers.rb0000644000004100000410000000465713120662775020171 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # if Kernel.respond_to?(:require_relative) require_relative("./ssl_common") else $LOAD_PATH << File.dirname(__FILE__) require "ssl_common" end include SSLCommon # # == SSL Use Case 2 - User Supplied Ciphers # # If you need your own ciphers list, this is how. # Stomp's default list will work in many cases. If you need to use this, you # will know it because SSL connect will fail. In that case, determining # _what_ should be in the list is your responsibility. # class ExampleSSL2C # Initialize. def initialize # Change the following as needed. @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612 end # Run example. def run ciphers_list = [["DHE-RSA-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["DHE-DSS-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["AES256-SHA", "TLSv1/SSLv3", 256, 256], ["EDH-RSA-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["EDH-DSS-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DHE-RSA-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["DHE-DSS-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["AES128-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-MD5", "TLSv1/SSLv3", 128, 128], ["EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EXP-EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-RC2-CBC-MD5", "TLSv1/SSLv3", 40, 128], ["EXP-RC4-MD5", "TLSv1/SSLv3", 40, 128]] # # SSL Use Case 2 # ts_flist = [] # Possibly change/override the cert data here. ts_flist << "#{ca_loc()}/#{ca_cert()}" ssl_opts = Stomp::SSLParams.new(:ts_files => ts_flist.join(","), :ciphers => ciphers_list, :fsck => true ) # hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSL Use Case 2" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" c.disconnect end end # e = ExampleSSL2C.new e.run stomp-1.4.4/examples/get11conn_ex2.rb0000644000004100000410000000345513120662775017452 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" end include Stomp11Common # # == Stomp 1.1 Receive Example 2 # # Purpose: to demonstrate receiving messages using Stomp 1.1, and using # 'ack => client'. # class Receive11Example2 # Initialize. def initialize end # Run example. def run conn = get_connection() # Use helper method to obtain a Stomp#connection raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11 # qname = "/queue/nodea.nodeb.nodec" # uuid = conn.uuid() puts "Subscribe id: #{uuid}" # # Subscribe with client ack mode # conn.subscribe qname, {'id' => uuid, 'ack' => 'client'} # # # Once you have subscribed, you may receive as usual # 1.upto(nmsgs()) do received = conn.receive puts "Received data: #{received.body}" # # We want now to ACK this message. In Stomp 1.0, a 'message-id' header was # required for the ACK. In Stomp 1.1, and additional header is required: # # * 'subscription' => id # msgid = received.headers['message-id'] # # What you cannot do: # begin conn.ack msgid rescue RuntimeError => sre puts "Rescue: #{sre}, #{sre.message}" end # # Try a valid 1.1 ACK # conn.ack msgid, {'subscription' => uuid} puts "ACK - msgid: #{msgid}, subscription: #{uuid}" end # # Unsubscribe # conn.unsubscribe qname, {}, uuid # Second style # # And finally, disconnect. # conn.disconnect end end # e = Receive11Example2.new e.run stomp-1.4.4/examples/conn11_ex2.rb0000644000004100000410000000530613120662775016747 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" end include Stomp11Common # # == Stomp 1.1 Connection Example 2 # # Purpose: to demonstrate a connect and disconnect sequence using Stomp 1.1. # # This example is like the 'conn11_ex1.rb' example except that a 'hashed' # connect request is made. # class Connection11Example2 # Initialize. def initialize end # Run example. def run # Create connection headers # ========================= # # The two headers used here are _required_ by the specification. # conn_hdrs = {"accept-version" => "1.1", # Demand a 1.1 connection (use a CSV list if you will consider multiple versions) "host" => virt_host, # The 1.1 vhost (could be different than connection host) } # No heartbeats here: there will be none for this connection # # Create the connect hash. # ======================== # conn_hash = { :hosts => [ {:login => login, :passcode => passcode, :host => host, :port => port}, ], :reliable => false, # Override default :connect_headers => conn_hdrs, } # # Get a connection # ================ # conn = Stomp::Connection.new(conn_hash) puts "Connection complete" # # Let's just do some sanity checks, and look around. # raise "Connection failed!!" unless conn.open? # # Is this really a 1.1 conection? ('protocol' is a read only connection # instance variable. The value will be '1.0' for those types of connections.) # raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11 # # The broker _could_ have returned an ERROR frame (unlikely). # raise "Connect error: #{conn.connection_frame.body}" if conn.connection_frame.command == Stomp::CMD_ERROR # # Examine the CONNECT response (the connection_frame). # puts "Connected Headers required to be present:" puts "Connect version - \t#{conn.connection_frame.headers['version']}" puts puts "Connected Headers that are optional:" puts "Connect server - \t\t#{conn.connection_frame.headers['server']}" puts "Session ID - \t\t\t#{conn.connection_frame.headers['session']}" puts "Server requested heartbeats - \t#{conn.connection_frame.headers['heart-beat']}" # # Finally disconnect # ================== # conn.disconnect # Business as usual, just like 1.0 puts "Disconnect complete" end end # e = Connection11Example2.new e.run stomp-1.4.4/examples/contributors.rb0000755000004100000410000000326413120662775017633 0ustar www-datawww-data#!/usr/bin/env ruby # class UserData public attr_accessor :count attr_reader :ad, :cd # def initialize(ad = nil, cd = nil) @count, @ad, @cd = 1, ad, cd end # def to_s "UserData: AuthorDate=>#{@ad}, CommitDate=>#{@cd}, CommitCount =>#{@count}" end end # tABLE Data ttab_s = "\n" ttab_e = "
\n" # Row Data trow_s = "\n" trow_e = "\n" # Header Data th_s = "\n" th_c1 = "First Author Date" th_c1b = "First Commit Date" th_c2 = "(Commit Count)" th_c3 = "Name / E-mail" th_e = "\n" # User Data (partial) td_s = "\n" td_e = "\n" # puts ttab_s # table start # userList = {} while s = gets do s.chomp! ad, cd, n, e = s.split(";") hk = "#{n}|#{e}" if userList.has_key?(hk) userList[hk].count += 1 else userList[hk] = UserData.new(ad, cd) =begin if ad != cd puts "NE: #{ad}, #{cd}, #{n}, #{e}" end =end end end # puts trow_s # puts th_s puts th_c1 puts th_e # =begin puts th_s puts th_c1b puts th_e =end # puts th_s puts th_c2 puts th_e # puts th_s puts th_c3 puts th_e # puts trow_e # userList.each do |k, v| n, e = k.split("|") oc = "(" + sprintf("%04d", v.count) + ")" # puts "# #{v.time} (#{oc}) #{n} #{e}" puts trow_s # puts td_s puts "#{v.ad}" puts td_e =begin # puts td_s puts "#{v.cd}" puts td_e =end # puts td_s puts oc puts td_e # puts td_s puts "\n" puts "#{n}\n" puts "\n" puts " / #{e}" puts td_e # puts trow_e end # puts ttab_e # table end stomp-1.4.4/examples/ssl_uc3_ciphers.rb0000644000004100000410000000505313120662775020161 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # if Kernel.respond_to?(:require_relative) require_relative("./ssl_common") else $LOAD_PATH << File.dirname(__FILE__) require "ssl_common" end include SSLCommon # # == SSL Use Case 3 - User Supplied Ciphers # # If you need your own ciphers list, this is how. # Stomp's default list will work in many cases. If you need to use this, you # will know it because SSL connect will fail. In that case, determining # _what_ should be in the list is your responsibility. # class ExampleSSL3C # Initialize. def initialize # Change the following as needed. @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612 end # Run example. def run ciphers_list = [["DHE-RSA-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["DHE-DSS-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["AES256-SHA", "TLSv1/SSLv3", 256, 256], ["EDH-RSA-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["EDH-DSS-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DHE-RSA-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["DHE-DSS-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["AES128-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-MD5", "TLSv1/SSLv3", 128, 128], ["EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EXP-EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-RC2-CBC-MD5", "TLSv1/SSLv3", 40, 128], ["EXP-RC4-MD5", "TLSv1/SSLv3", 40, 128]] # # SSL Use Case 3 # # Possibly change the cert file(s) name(s) here. ssl_opts = Stomp::SSLParams.new( :key_file => "#{cli_loc()}/#{pck()}", # the client's private key, private data :cert_file => "#{cli_loc()}/#{cli_cert()}", # the client's signed certificate :fsck => true # Check that the files exist first ) # hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSL Use Case 3" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" c.disconnect end private def pck() "client.key" end end # e = ExampleSSL3C.new e.run stomp-1.4.4/examples/publish_file_conn.rb0000644000004100000410000000407613120662775020557 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' require 'stomp' if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") require_relative("./lflogger") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" require "./lflogger" end include Stomp11Common # # Used primarily for testing performance when sending "large" messages. # "large" => YMMV # class FileSenderConn # Initialize. def initialize end # Run example. def run p [ "pubc001", Thread.current ] publogger = Slogger::new start_time = Time.now.to_f fname = ARGV[0] puts "PUBFC: File Name: #{fname}" file = open(fname, "r") rs = Time.now.to_f buff = file.read re = Time.now.to_f ppt = sprintf("%20.6f", re - rs) puts "PUBFC: File size: #{buff.respond_to?(:bytesize) ? buff.bytesize : buff.length}" puts "PUBFC: File read: #{ppt} seconds" file.close # connection_hdrs = {"accept-version" => "1.1", "host" => virt_host(), } connection_hash = { :hosts => [ {:login => login(), :passcode => passcode(), :host => host(), :port => port()}, ], :connect_headers => connection_hdrs, :logger => publogger, :reliable => false, ### *NOTE* } # # p [ "ch", connection_hash ] connection = Stomp::Connection.new(connection_hash) qname = ENV['STOMP_DEST'] ? ENV['STOMP_DEST'] : "/queue/a.big.file" puts "PUBFC: Qname is: #{qname}" ph = {:presistent => true} ph['suppress_content_length'] = 'yes' if suppresscl() puts "PUBF: Headers are: #{ph.inspect}" # Try to gracefully handle files that exceed broker size limits. begin connection.publish(qname, buff, ph) rescue puts "PUBFC: exception on publish: #{$!}" end e = connection.poll() # Check for unsolicited messages from broker puts "PUBFC: unexpected broker message: #{e}" if e connection.disconnect end_time = Time.now.to_f ppt = sprintf("%20.6f", end_time - start_time) puts "PUBFC: File published: #{ppt} seconds" end end # e = FileSenderConn.new e.run stomp-1.4.4/examples/consumer.rb0000644000004100000410000000133713120662775016725 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' require 'stomp' # # == Example message consumer. # class ExampleConsumer # Initialize. def initialize end # Run example. def run client = Stomp::Client.new("failover://(stomp://:@localhost:61613,stomp://:@remotehost:61613)?initialReconnectDelay=5000&randomize=false&useExponentialBackOff=false") puts "Subscribing ronaldo" client.subscribe("/queue/ronaldo", {:ack => "client", "activemq.prefetchSize" => 1, "activemq.exclusive" => true }) do |msg| File.open("file", "a") do |f| f.write(msg.body) f.write("\n----------------\n") end client.acknowledge(msg) end loop do sleep(1) puts "." end end end # e = ExampleConsumer.new e.run stomp-1.4.4/examples/consume_file.rb0000644000004100000410000000346413120662775017545 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' require 'stomp' if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") require_relative("./lflogger") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" require "./lflogger" end include Stomp11Common # # Used primarily for testing performance when receiving "large" messages. # "large" => YMMV # class FileReader # Initialize. def initialize(pto) @parse_timeout = pto end # Run example. def run conlogger = Slogger::new start_time = Time.now.to_f # connection_hdrs = {"accept-version" => "1.1", "host" => virt_host(), } connection_hash = { :hosts => [ {:login => login(), :passcode => passcode(), :host => host(), :port => port()}, ], :connect_headers => connection_hdrs, :logger => conlogger, :reliable => false, :parse_timeout => @parse_timeout, } # # p [ "ch", connection_hash ] connection = Stomp::Connection.new(connection_hash) qname = ENV['STOMP_DEST'] ? ENV['STOMP_DEST'] : "/queue/a.big.file" puts "CONF: Qname is: #{qname}" ## connection.subscribe(qname, {:destination => qname}, "bigFileSubscriptionID") connection.subscribe(qname, {:destination => qname}, connection.uuid()) ## connection.subscribe(qname, {:destination => qname}, "0") msg = connection.receive() puts "CONF: Message Command: #{msg.command}" puts "CONF: Message Headers: #{msg.headers}" body_length_bytes = msg.body.respond_to?(:bytesize) ? msg.body.bytesize : msg.body.length puts "CONF: Received: #{body_length_bytes} bytes" connection.disconnect end_time = Time.now.to_f ppt = sprintf("%20.6f", end_time - start_time) puts "CONF: File consumed: #{ppt} seconds" end end # e = FileReader.new(60) e.run stomp-1.4.4/examples/amqdurasub.rb0000644000004100000410000000441513120662775017236 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" end =begin A recent experience suggested that we might provide an example of how to establish a "durable" topic subscription using ActiveMQ. This code attempts to do that. References: http://activemq.apache.org/stomp.html http://activemq.apache.org/how-does-a-queue-compare-to-a-topic.html http://activemq.apache.org/how-do-durable-queues-and-topics-work.html Specifically, locate the section titled: ActiveMQ extensions to Stomp. There are two programmatic requirements: 1) On CONNECT, indicate to AMQ the code will be using durable topic subscription(s). Done by providing a "cilent-id" CONNECT header. 2) On SUBSCRIBE, indicate an AMQ specific (uniqie) subscription ID. Done by providing a "activemq.subscriptionName" header to SUBSCRIBE. =end # login hash hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => 'localhost', :port => 61613, :ssl => false}, # AMQ ], :reliable => true, :closed_check => false, :connect_headers => {:host => "localhost", :"accept-version" => "1.0", # Requirement 1, name should be unique. :"client-id" => "dursubcli01", # REF the 1st AMQ link above } } # The topic topic = "/topic/topicName" # Establish the client connection cli = Stomp::Client.open(hash) # SUBSCRIBE Headers sh = { "activemq.subscriptionName" => "subname01" } # REF the 1st AMQ link above # And the client subscribe cli.subscribe(topic, sh) do |msg| puts "msg: #{msg}" end # Wait for a very long time, usually exit via ^C puts "Press ^C to exit" sleep 1000000 puts "Done yet?" =begin At this point open your AMQ admin console (port 8161 usually), and examine the 'subscribers' section. You should see an instance of this client displayed in the "Active Durable Topic Subscribers" section. When you press ^C to exit this program, return to the AMQ console and refresh the display. (Do not restart AMQ). You should see this client in the "Offline Durable Topic Subscribers" section. =end stomp-1.4.4/examples/conn11_ex1.rb0000644000004100000410000000713013120662775016743 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" end include Stomp11Common # # == Stomp 1.1 Connection Example 1 # # Purpose: to demonstrate a connect and disconnect sequence using Stomp 1.1. # # Note: this example assumes that you have at least the 1.2.0 gem release # installed. # # When you: # # * Use a Stomp 1.1 compliant broker # * Want a Stomp 1.1 level connection and functionality # # then your code *must* specifically request that environment. # # You need to supply all of the normal values expected of course: # # * login - the user name # * passcode - the password # * host - the host to connect to # * port - the port to connect to # # Additionaly you are required to supply the 1.1 connection data as documented # in the Stomp 1.1 specification: http://stomp.github.com/stomp-specification-1.1.html # You are urged to become familiar with the spec. It is a short document. # # This includes: # # * The Stomp version(s) you wish the broker to consider # * The broker vhost to connect to # # You may optionally specify other 1.1 data: # # * heartbeat request # # Using the stomp gem, you can specify this data in the "connect_headers" Hash # parameter or a paramaterized connection request. This example uses a # parameterized request. # class Connection11Example1 # Initialize def initialize end # Run example def run # # Create connection headers # ========================= # # The two headers used here are _required_ by the specification. # conn_hdrs = {"accept-version" => "1.1", # Demand a 1.1 connection (use a CSV list if you will consider multiple versions) "host" => virt_host, # The 1.1 vhost (could be different than connection host) } # No heartbeats here: there will be none for this connection # # Get a connection # ================ # conn = Stomp::Connection.new(login, passcode, host, port, # Normal connect parms false, # Not reliable, the default for a parameter connection 5, # Connect redelay, the default conn_hdrs) # The 1.1 connection parameters puts "Connection connect complete" # # Let's just do some sanity checks, and look around. # raise "Connection failed!!" unless conn.open? # # Is this really a 1.1 conection? ('protocol' is a read only connection # instance variable. The value will be '1.0' for those types of connections.) # raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11 # # The broker _could_ have returned an ERROR frame (unlikely). # raise "Connect error: #{conn.connection_frame.body}" if conn.connection_frame.command == Stomp::CMD_ERROR # # Examine the CONNECT response (the connection_frame). # puts "Connected Headers required to be present:" puts "Connect version - \t#{conn.connection_frame.headers['version']}" puts puts "Connected Headers that are optional:" puts "Connect server - \t\t#{conn.connection_frame.headers['server']}" puts "Session ID - \t\t\t#{conn.connection_frame.headers['session']}" puts "Server requested heartbeats - \t#{conn.connection_frame.headers['heart-beat']}" # # Finally disconnect # ================== # conn.disconnect # Business as usual, just like 1.0 puts "Connection disconnect complete" end end # e = Connection11Example1.new e.run stomp-1.4.4/examples/publisher.rb0000644000004100000410000000120013120662775017054 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' require 'stomp' # # == Example message publisher # class ExamplePublisher # Initialize. def initialize end # Run example. def run client = Stomp::Client.new("failover://(stomp://:@localhost:61613,stomp://:@remotehost:61613)?initialReconnectDelay=5000&randomize=false&useExponentialBackOff=false") message = "ronaldo #{ARGV[0]}" for i in (1..50) puts "Sending message" client.publish("/queue/ronaldo", "#{i}: #{message}", {:persistent => true}) puts "(#{Time.now})Message sent: #{i}" sleep 0.2 end end end # e = ExamplePublisher.new e.run stomp-1.4.4/examples/ssl_uc4.rb0000644000004100000410000000403513120662775016444 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # if Kernel.respond_to?(:require_relative) require_relative("./ssl_common") else $LOAD_PATH << File.dirname(__FILE__) require "ssl_common" end include SSLCommon # # == SSL Use Case 4 - server *does* authenticate client, client *does* authenticate server # # Subcase 4.A - Message broker configuration does *not* require client authentication # # - Expect connection success # - Expect a verify result of 0 becuase the client did authenticate the # server's certificate. # # Subcase 4.B - Message broker configuration *does* require client authentication # # - Expect connection success if the server can authenticate the client certificate # - Expect a verify result of 0 because the client did authenticate the # server's certificate. # class ExampleSSL4 # Initialize. def initialize # Change the following as needed. @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612 end # Run example. def run puts "Connect host: #{@host}, port: #{@port}" # Possibly change the cert file(s) name(s) here. ssl_opts = Stomp::SSLParams.new( :key_file => "#{cli_loc()}/#{pck()}", # the client's private key, private data :cert_file => "#{cli_loc()}/#{cli_cert()}", # the client's signed certificate :ts_files => "#{ca_loc()}/#{ca_cert()}", # The CA's signed sertificate :fsck => true # Check that files exist first ) # hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSL Use Case 4" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # puts "SSL Peer Certificate:\n#{ssl_opts.peer_cert}" c.disconnect end private def pck() "client.key" end end # e = ExampleSSL4.new e.run stomp-1.4.4/examples/logexamp.rb0000644000004100000410000000555313120662775016712 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' require 'stomp' require 'logger' # for the 'local' logger # if Kernel.respond_to?(:require_relative) require_relative("./examplogger") else $LOAD_PATH << File.dirname(__FILE__) require "examplogger" end # # == A STOMP::Connection program which uses the callback logging facility. # class LoggerExample # Initialize. def initialize end # Run example. def run llog = Logger::new(STDOUT) llog.level = Logger::DEBUG llog.debug "LE Starting" # ////////////////////////////////////////////////////////////////////////////// mylog = Slogger::new # The client provided STOMP callback logger # ////////////////////////////////////////////////////////////////////////////// user = ENV['STOMP_USER'] ? ENV['STOMP_USER'] : 'guest' password = ENV['STOMP_PASSWORD'] ? ENV['STOMP_PASSWORD'] : 'guest' host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : 'localhost' port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61613 # ////////////////////////////////////////////////////////////////////////////// # A hash type connect *MUST* be used to enable callback logging. # ////////////////////////////////////////////////////////////////////////////// hash = { :hosts => [ {:login => user, :passcode => password, :host => 'noonehome', :port => 2525}, {:login => user, :passcode => password, :host => host, :port => port}, ], :logger => mylog, # This enables callback logging! :max_reconnect_attempts => 5, } # ////////////////////////////////////////////////////////////////////////////// # For a Connection: llog.debug "LE Connection processing starts" conn = Stomp::Connection.new(hash) conn.disconnect # ////////////////////////////////////////////////////////////////////////////// llog.debug "LE Connection processing complete" # ////////////////////////////////////////////////////////////////////////////// # For a Client: llog.debug "LE Client processing starts" conn = Stomp::Client.new(hash) conn.close # ////////////////////////////////////////////////////////////////////////////// llog.debug "LE Client processing complete" # ////////////////////////////////////////////////////////////////////////////// # For a Connection with other calls: llog.debug "LE Connection Enhanced processing starts" conn = Stomp::Connection.new(hash) # dest = "/queue/loggerq1" conn.publish dest, "a logger message" conn.subscribe dest msg = conn.receive conn.disconnect # ////////////////////////////////////////////////////////////////////////////// llog.debug "LE Connection Enhanced processing complete" # ////////////////////////////////////////////////////////////////////////////// llog.debug "LE Ending" end end e = LoggerExample.new e.run stomp-1.4.4/examples/ssl_uc1_ciphers.rb0000644000004100000410000000430213120662775020153 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Reference: https://github.com/stompgem/stomp/wiki/extended-ssl-overview # if Kernel.respond_to?(:require_relative) require_relative("./ssl_common") else $LOAD_PATH << File.dirname(__FILE__) require "ssl_common" end include SSLCommon # # == SSL Use Case 1 - User Supplied Ciphers # # If you need your own ciphers list, this is how. # Stomp's default list will work in many cases. If you need to use this, you # will know it because SSL connect will fail. In that case, determining # _what_ should be in the list is your responsibility. # class ExampleSSL1C # Initialize. def initialize # Change the following as needed. @host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : "localhost" @port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612 end # Run example. def run ciphers_list = [["DHE-RSA-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["DHE-DSS-AES256-SHA", "TLSv1/SSLv3", 256, 256], ["AES256-SHA", "TLSv1/SSLv3", 256, 256], ["EDH-RSA-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["EDH-DSS-DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DES-CBC3-SHA", "TLSv1/SSLv3", 168, 168], ["DHE-RSA-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["DHE-DSS-AES128-SHA", "TLSv1/SSLv3", 128, 128], ["AES128-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-SHA", "TLSv1/SSLv3", 128, 128], ["RC4-MD5", "TLSv1/SSLv3", 128, 128], ["EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EXP-EDH-RSA-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-DES-CBC-SHA", "TLSv1/SSLv3", 40, 56], ["EXP-RC2-CBC-MD5", "TLSv1/SSLv3", 40, 128], ["EXP-RC4-MD5", "TLSv1/SSLv3", 40, 128]] ssl_opts = Stomp::SSLParams.new(:ciphers => ciphers_list) # # SSL Use Case 1 # hash = { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => @host, :port => @port, :ssl => ssl_opts}, ], :reliable => false, # YMMV, to test this in a sane manner } # puts "Connect starts, SSL Use Case 1" c = Stomp::Connection.new(hash) puts "Connect completed" puts "SSL Verify Result: #{ssl_opts.verify_result}" # c.disconnect end end # e = ExampleSSL1C.new e.run stomp-1.4.4/examples/put11conn_ex1.rb0000644000004100000410000000241613120662775017476 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" end include Stomp11Common # # == Stomp 1.1 Publish Example # # Purpose: to demonstrate sending messages using Stomp 1.1. # class Publish11Example1 # Initialize. def initialize end # Run example. def run conn = get_connection() # Use helper method to obtain a Stomp#connection raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11 # # Publishing simple data is as it was with Stomp 1.0. # # Note: Stomp 1.1 brokers seem to prefer using '.' as delimiters in queue # name spaces. Hence, the queue name used here. # qname = "/queue/nodea.nodeb.nodec" data = "message payload" headers = {} # # The 'data' and 'headers' may be omitted, as with Stomp 1.0 # puts "Writing #{nmsgs()} messages." 1.upto(nmsgs()) do |i| msg = "#{data}: #{i}" conn.publish qname, msg , headers puts "Sent data: #{msg}" end # # And finally, disconnect. # conn.disconnect end end # e = Publish11Example1.new e.run stomp-1.4.4/examples/putget11_rh1.rb0000644000004100000410000000554013120662775017316 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # The current require dance for different Ruby versions. # Change this to suit your requirements. # if Kernel.respond_to?(:require_relative) require_relative("./stomp11_common") else $LOAD_PATH << File.dirname(__FILE__) require "stomp11_common" end include Stomp11Common # # == Stomp 1.1 Send/Receive Example - Repeated Headers # # Purpose: to demonstrate sending and receiving using Stomp 1.1, and an unusual # aspect of the specification. What is demonstrated here is the use of # 'repeated headers'. Note that brokers MAY support repeated headers as # demonstrated, but are not required to provide this support. This example # should run against the Apollo broker. It will *not* currently run against # RabbitMQ. YMMV depending on your broker. # # See: http://stomp.github.com/stomp-specification-1.1.html#Repeated_Header_Entries # class RepeatedHeadersExample # Initialize. def initialize end # Run example. def run conn = get_connection() # Use helper method to obtain a Stomp#connection raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11 # # The gem supports repeated headers by allowing the 'value' part of a header # to be an Array instance. # # On 'publish', all values in the Array are placed on the wire and sent to the # broker in order. # # On 'receive', if repeated headers are detected, an Array instance is created # to hold the repeated values. This is presented the the calling client to # be processed per client requirements. # qname = "/queue/nodea.nodeb.nodec" data = "message payload: #{Time.now.to_f}" key2_repeats = ["key2val3", "key2val2", "key2val1" ] headers = {"key1" => "value1", # A normal header "key2" => key2_repeats, # A repeated header "key3" => "value3", # Another normal header } # # Ship it. # conn.publish qname, data , headers puts "Sent data: #{data}" # # Receive phase. # uuid = conn.uuid() conn.subscribe qname, {"id" => uuid} received = conn.receive conn.unsubscribe qname, {"id" => uuid} # # Check that we received what we sent. # raise "Unexpected payload" unless data == received.body raise "Missing key" unless received.headers["key2"] raise "Repeats not present" unless received.headers.has_value?(key2_repeats) raise "Unexpected repeat values" unless key2_repeats == received.headers["key2"] # # Demonstrate how to process repeated headers received by display of those # received headers for a visual check. # received.headers.each_pair do |k,v| if v.is_a?(Array) v.each do |e| puts "#{k}:#{e}" end else puts "#{k}:#{v}" end end # # And finally, disconnect. # conn.disconnect end end # e = RepeatedHeadersExample.new e.run stomp-1.4.4/examples/lflogger.rb0000644000004100000410000001754313120662775016701 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'logger' # use the standard Ruby logger ..... # == Example STOMP call back logger class. # # Optional callback methods: # # * on_connecting: connection starting # * on_connected: successful connect # * on_connectfail: unsuccessful connect (will usually be retried) # * on_disconnect: successful disconnect # # * on_miscerr: on miscellaneous xmit/recv errors # # * on_publish: publish called # * on_subscribe: subscribe called # * on_unsubscribe: unsubscribe called # # * on_begin: begin called # * on_ack: ack called # * on_nack: nack called # * on_commit: commit called # * on_abort: abort called # # * on_receive: receive called and successful # # * on_ssl_connecting: SSL connection starting # * on_ssl_connected: successful SSL connect # * on_ssl_connectfail: unsuccessful SSL connect (will usually be retried) # # * on_hbread_fail: unsuccessful Heartbeat read # * on_hbwrite_fail: unsuccessful Heartbeat write # * on_hbfire: on any send or receive heartbeat # # All methods are optional, at the user's requirements. # # If a method is not provided, it is not called (of course.) # # IMPORTANT NOTE: in general, call back logging methods *SHOULD* not raise exceptions, # otherwise the underlying STOMP connection may fail in mysterious ways. # There are, however, exceptions to this 'rule'. # # Useful exceptions to this rule are: # # * on_connectfail # * on_ssl_connectfail # # These two methods can raise a Stomp::Errors::LoggerConnectionError. If this # exception is raised, it is passed up the chain to the caller. # # Callback parameters: are a copy of the @parameters instance variable for # the Stomp::Connection. # # A logger class may optionally inherit from the provided NullLogger # # # class Slogger < Stomp::NullLogger # class Slogger MAX_BODY_LEN = 1024 # Arbitrary # Initialize a new callback logger instance. def initialize(init_parms = nil) _init @log.info("Logger initialization complete.") end def _init @log = Logger::new(STDOUT) # User preference @log.level = Logger::DEBUG # User preference end def marshal_dump [] end def marshal_load(array) _init end # Log connecting events def on_connecting(parms) begin @log.debug "Connecting: #{info(parms)}" rescue @log.debug "Connecting oops #{$!}" end end # Log connected events def on_connected(parms) begin @log.debug "Connected Info: #{info(parms)}" @log.debug "Connected Parms: #{parms}" rescue @log.debug "Connected oops #{$!}" end end # Log connectfail events def on_connectfail(parms) begin @log.debug "Connect Fail #{info(parms)}" rescue @log.debug "Connect Fail oops #{$!}" end =begin # An example LoggerConnectionError raise @log.debug "Connect Fail, will raise" raise Stomp::Error::LoggerConnectionError.new("quit from connect fail") =end end # Log disconnect events def on_disconnect(parms) begin @log.debug "Disconnected #{info(parms)}" rescue @log.debug "Disconnected oops #{$!}" end end # Log miscellaneous errors def on_miscerr(parms, errstr) begin @log.debug "Miscellaneous Error Info: #{info(parms)}" @log.debug "Miscellaneous Error String: #{errstr}" @log.debug "Miscellaneous Error Parms: #{parms}" rescue @log.debug "Miscellaneous Error oops #{$!}" # Most clients will want to know about this. YMMV raise end end # Log Subscribe def on_subscribe(parms, headers) begin @log.debug "Subscribe Parms #{info(parms)}" @log.debug "Subscribe Headers #{headers}" rescue @log.debug "Subscribe oops #{$!}" end end # Log UnSubscribe def on_unsubscribe(parms, headers) begin @log.debug "UnSubscribe Parms #{info(parms)}" @log.debug "UnSubscribe Headers #{headers}" rescue @log.debug "UnSubscribe oops #{$!}" end end # Log Publish def on_publish(parms, body, headers) begin @log.debug "Publish Parms #{info(parms)}" body_length_bytes = body.respond_to?(:bytesize) ? body.bytesize : body.length @log.debug "Publish Message Body #{body}" if body_length_bytes <= MAX_BODY_LEN @log.debug "Publish Headers #{headers}" rescue @log.debug "Publish oops #{$!}" end end # Log Receive def on_receive(parms, result) begin @log.debug "Receive Parms #{info(parms)}" @log.debug result.is_a? Stomp::Message ## @log.debug "Receive Result #{result}" rescue @log.debug "Receive oops #{$!}" end end # Log Begin def on_begin(parms, headers) begin @log.debug "Begin Parms #{info(parms)}" @log.debug "Begin Result #{headers}" rescue @log.debug "Begin oops #{$!}" end end # Log Ack def on_ack(parms, headers) begin @log.debug "Ack Parms #{info(parms)}" @log.debug "Ack Result #{headers}" rescue @log.debug "Ack oops #{$!}" end end # Log NAck def on_nack(parms, headers) begin @log.debug "NAck Parms #{info(parms)}" @log.debug "NAck Result #{headers}" rescue @log.debug "NAck oops #{$!}" end end # Log Commit def on_commit(parms, headers) begin @log.debug "Commit Parms #{info(parms)}" @log.debug "Commit Result #{headers}" rescue @log.debug "Commit oops #{$!}" end end # Log Abort def on_abort(parms, headers) begin @log.debug "Abort Parms #{info(parms)}" @log.debug "Abort Result #{headers}" rescue @log.debug "Abort oops #{$!}" end end # Stomp 1.1+ - heart beat read (receive) failed. def on_hbread_fail(parms, ticker_data = {}) begin @log.debug "Hbreadf Parms #{info(parms)}" @log.debug "Hbreadf Result #{ticker_data.inspect}" rescue @log.debug "Hbreadf oops #{$!}" end end # Stomp 1.1+ - heart beat send (transmit) failed. def on_hbwrite_fail(parms, ticker_data = {}) begin @log.debug "Hbwritef Parms #{info(parms)}" @log.debug "Hbwritef Result #{ticker_data.inspect}" rescue @log.debug "Hbwritef oops #{$!}" end end # Log SSL connection start. def on_ssl_connecting(parms) begin @log.debug "SSL Connecting Parms #{info(parms)}" rescue @log.debug "SSL Connecting oops #{$!}" end end # Log a successful SSL connect. def on_ssl_connected(parms) begin @log.debug "SSL Connected Parms #{info(parms)}" rescue @log.debug "SSL Connected oops #{$!}" end end # Log an unsuccessful SSL connect. def on_ssl_connectfail(parms) begin @log.debug "SSL Connect Fail Parms #{info(parms)}" @log.debug "SSL Connect Fail Exception #{parms[:ssl_exception]}, #{parms[:ssl_exception].message}" rescue @log.debug "SSL Connect Fail oops #{$!}" end =begin # An example LoggerConnectionError raise @log.debug "SSL Connect Fail, will raise" raise Stomp::Error::LoggerConnectionError.new("quit from SSL connect") =end end # Log heart beat fires def on_hbfire(parms, srind, firedata = {}) begin @log.debug "HeartBeat Fire Parms #{info(parms)}" @log.debug "HeartBeat Fire Send/Receive #{srind}" rescue @log.debug "HeartBeat Fire oops #{$!}" end end private # Example information extract. def info(parms) # # Available in the parms Hash: # parms[:cur_host] # parms[:cur_port] # parms[:cur_login] # parms[:cur_passcode] # parms[:cur_ssl] # parms[:cur_recondelay] # parms[:cur_parseto] # parms[:cur_conattempts] # parms[:cur_failure] # parms[:openstat] # # For the on_ssl_connectfail callback these are also available: # parms[:ssl_exception] # "Host: #{parms[:cur_host]}, Port: #{parms[:cur_port]}, Login: #{parms[:cur_login]}, Passcode: #{parms[:cur_passcode]}, ssl: #{parms[:cur_ssl]}" end end # of class stomp-1.4.4/examples/contrib.sh0000755000004100000410000000031613120662775016540 0ustar www-datawww-data#!/bin/bash # # git log --reverse --all --date=short --pretty --format='%aI;%cI;%cn;%ce' | \ git log --reverse --all --date=short --pretty --format='%ad;%cd;%cn;%ce' | \ ruby $HOME/bin/contributors.rb stomp-1.4.4/examples/stomp11_common.rb0000644000004100000410000000224513120662775017745 0ustar www-datawww-data# -*- encoding: utf-8 -*- # # Common Stomp 1.1 code. # require "rubygems" if RUBY_VERSION < "1.9" require "stomp" # module Stomp11Common # User id def login() ENV['STOMP_USER'] || 'guest' end # Password def passcode() ENV['STOMP_PASSCODE'] || 'guest' end # Server host def host() ENV['STOMP_HOST'] || "localhost" # The connect host name end # Server port def port() (ENV['STOMP_PORT'] || 61613).to_i # !! The author runs AMQ listening here end # Required vhost name def virt_host() ENV['STOMP_VHOST'] || "localhost" # The 1.1 virtual host name end # Create a 1.1 commection def get_connection() conn_hdrs = {"accept-version" => "1.1", # 1.1 only "host" => virt_host, # the vhost } conn_hash = { :hosts => [ {:login => login, :passcode => passcode, :host => host, :port => port}, ], :connect_headers => conn_hdrs, } conn = Stomp::Connection.new(conn_hash) end # Number of messages def nmsgs() (ENV['STOMP_NMSGS'] || 1).to_i # Number of messages end # Include "suppress-content-length' header def suppresscl() ENV['STOMP_SUPPRESS_CL'] end end stomp-1.4.4/notes/0000755000004100000410000000000013120662775014053 5ustar www-datawww-datastomp-1.4.4/notes/heartbeat_readme.txt0000644000004100000410000001274513120662775020101 0ustar www-datawww-dataUsage notes and comments follow (sorry for the length here, please read carefully). Write failures: there is a single type of write failure, and it occurs when an exception of some sort is raised during Heartbeat write. This is the only type of write failure that can be detected. If :reliable is true, an exception on heartbeat write will always causes a fail over attempt. Read failures: this is actually more complex than originally envisioned. There are really three distinct types of read 'failures': 1) An exception is thrown during Heartbeat read. If :reliable is true, this always causes a fail over attempt. 2) Heartbeat reads can not obtain the read_semaphore lock. This will occur when the main connection thread has: -- Called Connection#receive -- Only heartbeats but no Stomp frames are on the inbound wire -- Last Heartbeat read time is being maintained by the #receive attempt 3) Heartbeat reads obtain the read_semaphore lock, but the socket shows no data is available (by IO#ready?). This will occur when: -- The main thread has not called Connection#receive (the thread is doing other work) -- There is no heartbeat to receive (IO#ready? indicates no input data available) The requirement to handle cases 2) and 3) results in not one, but two different counters being taken in to consideration. To handle case 2) add to the connect hash: :max_hbrlck_fails => x # x is a number strictly greater than 0. Default is 0, which disables the functionality. A running count of this failure type is maintained in the HB read thread. The count is incremented when: -- Lock obtain fails, *and* -- A heartbeat is 'late' The count is reset to 0 whenever: -- A heart beat has been received on a timely basis When the running count *reaches* the value specified in :max_hbrlck_fails, a fail over attempt is initiated. Advice: do *not* set this to 1 (in order to avoid fail overs on a transient error). To handle case 3) add to the connect hash: :max_hbread_fails => y # y is a number strictly greater than 0. Default is 0, which disables the functionality. A running count of this failure type is maintained in the HB read thread. The count is incremented when: -- Lock obtain succeeds, *and* -- IO#ready? indicates no data available The count is reset to 0 under two conditions: Condition 1) -- A heartbeat is late, *and* -- Lock obtain succeeds, *and* -- IO#ready? indicates data is available, *and* -- A single character is read from the wire Condition 2) -- A heartbeat has been received in a timely manner (perhaps by the main thread) When the running count *reaches* the value specified in :max_hbread_fails, a fail over attempt is initiated. Advice: do *not* set this to 1 (in order to avoid fail overs on a transient error). ----------------------------------------------------------- General advice: Set your heartbeat intervals to the maximum possible to obtain your desired behavior. Do *not* set them at extremely low values even if the broker allows that. An absurd example: heart-beat:1,1 which will likely not work well. ----------------------------------------------------------- General notes: In your real world apps, think about whether one or both of these parameters are appropriate. Please add the: -- on_hbread_fail -- on_hbwrite_fail methods to a callback logger. In those methods show 'ticker_data.inspect' output. We would want that log output in future problem reports. We make the comment about not setting these values to 1 because every server we test with is prone to transient (single event) failures, particularly for client HB reads. We have done a variety of informal tests here, using both server kill and packet drop strategies as appropriate. We believe more real world testing is required. ----------------------------------------------------------- 08/07/2013 Issue #63 related, specifically fast send heart beats are being used and spurious fail overs occur in rapid succession. Background: Fail over from heartbeat failures was introduced in gem version 1.2.10. Subsequently: This issue has been observed and documented in the following environment: -- JRuby engine 1.7.4 *and* -- ActiveMQ 5.8.0 *and* -- 'fast' client send heartbeats Heartbeat sends were at 2000ms. At this point in time, fast send heart beats and spurious fail overs have *not* been observed using: -- Any native RUBY_ENGINE and ActiveMQ -- Any native RUBY_ENGINE and Apollo (client send rates are limited by default) -- Any native RUBY_ENGINE and RabbitMQ -- JRuby and Apollo (client send rates are limited by default) -- JRuby and RabbitMQ Note that 'fast' will depend on your use case for heartbeats. Observations are that sending heartbeat times less than 5000ms might be considered 'fast' in the targeted environment. The solution / bypass being put in place as of the above date was developed through experimentation and is as follows: - Add 'adjustment' logic to the heartbeat sender (thanks to ppaul for this idea). - Re-introduce tolerance logic removed in d922fa. - Add a new connection hash parameter to adjust heartbeat sends. The newly introduced connection hash parameter is: :fast_hbs_adjust => 0.0 # The default, no adjustment to sender sleep times (sec) Recommendation for gem users that: - Use fast send heartbeats - Actually notice spurious fail overs is to provide a very sender sleep time adjustment when connecting. Examples: :fast_hbs_adjust => 0.05 # 50 milliseconds :fast_hbs_adjust => 0.10 # 100 milliseconds As usual, YMMV. stomp-1.4.4/spec/0000755000004100000410000000000013120662775013655 5ustar www-datawww-datastomp-1.4.4/spec/connection_spec.rb0000644000004100000410000004562713120662775017371 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'spec_helper' describe Stomp::Connection do before(:each) do @parameters = { :hosts => [ {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false}, {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false} ], :reliable => true, :initial_reconnect_delay => 0.01, :max_reconnect_delay => 30.0, :use_exponential_back_off => true, :back_off_multiplier => 2, :max_reconnect_attempts => 0, :randomize => false, :connect_timeout => 0, :parse_timeout => 5, :connect_headers => {}, :dmh => false, :closed_check => true, :hbser => false, :stompconn => false, :usecrlf => false, :max_hbread_fails => 0, :max_hbrlck_fails => 0, :fast_hbs_adjust => 0.0, :connread_timeout => 0, :tcp_nodelay => true, :start_timeout => 0, :sslctx_newparm => nil, :ssl_post_conn_check => true, } #POG: class Stomp::Connection def _receive( s, connread = false ) end end # clone() does a shallow copy, we want a deep one so we can garantee the hosts order normal_parameters = Marshal::load(Marshal::dump(@parameters)) @tcp_socket = double(:tcp_socket, :close => nil, :puts => nil, :write => nil, :setsockopt => nil, :flush => true) allow(TCPSocket).to receive(:open).and_return @tcp_socket @connection = Stomp::Connection.new(normal_parameters) end describe "autoflush" do let(:parameter_hash) { { "hosts" => [ {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false}, {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false} ], "reliable" => true, "initialReconnectDelay" => 0.01, "maxReconnectDelay" => 30.0, "useExponentialBackOff" => true, "backOffMultiplier" => 2, "maxReconnectAttempts" => 0, "randomize" => false, "connect_timeout" => 0, "parse_timeout" => 5, } } it "should call flush on the socket when autoflush is true" do expect(@tcp_socket).to receive(:flush) @connection = Stomp::Connection.new(parameter_hash.merge("autoflush" => true)) @connection.publish "/queue", "message", :suppress_content_length => false end it "should not call flush on the socket when autoflush is false" do expect(@tcp_socket).not_to receive(:flush) @connection = Stomp::Connection.new(parameter_hash) @connection.publish "/queue", "message", :suppress_content_length => false end end describe "(created using a hash)" do it "should uncamelize and symbolize the main hash keys" do used_hash = { "hosts" => [ {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false}, {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false} ], "reliable" => true, "initialReconnectDelay" => 0.01, "maxReconnectDelay" => 30.0, "useExponentialBackOff" => true, "backOffMultiplier" => 2, "maxReconnectAttempts" => 0, "randomize" => false, "connectTimeout" => 0, "parseTimeout" => 5, "usecrlf" => false, :maxHbreadFails => 0, :maxHbrlckFails => 0, :fastHbsAdjust => 0.0, :connreadTimeout => 0, :tcpNodelay => true, :startTimeout => 0, :sslctxNewparm => nil, } @connection = Stomp::Connection.new(used_hash) expect(@connection.instance_variable_get(:@parameters)).to eq(@parameters) end it "should start with first host in array" do expect(@connection.instance_variable_get(:@host)).to eq("localhost") end it "should change host to next one with randomize false" do @connection.send(:change_host) # use .send(:name) to test a private method! expect(@connection.instance_variable_get(:@host)).to eq("remotehost") end it "should use default port (61613) if none is given" do hash = {:hosts => [{:login => "login2", :passcode => "passcode2", :host => "remotehost", :ssl => false}]} @connection = Stomp::Connection.new hash expect(@connection.instance_variable_get(:@port)).to eq(61613) end context "should be able pass reliable as part of hash" do it "should be false if reliable is set to false" do hash = @parameters.merge({:reliable => false }) connection = Stomp::Connection.new(hash) expect(connection.instance_variable_get(:@reliable)).to be false end it "should be true if reliable is set to true" do hash = @parameters.merge({:reliable => true }) connection = Stomp::Connection.new(hash) expect(connection.instance_variable_get(:@reliable)).to be true end it "should be true if reliable is not set" do connection = Stomp::Connection.new(@parameters) expect(connection.instance_variable_get(:@reliable)).to be true end end context "when dealing with content-length header" do it "should not suppress it when receiving :suppress_content_length => false" do expect(@tcp_socket).to receive(:puts).with("content-length:7") @connection.publish "/queue", "message", :suppress_content_length => false end it "should not suppress it when :suppress_content_length is nil" do expect(@tcp_socket).to receive(:puts).with("content-length:7") @connection.publish "/queue", "message" end it "should suppress it when receiving :suppress_content_length => true" do expect(@tcp_socket).not_to receive(:puts).with("content-length:7") @connection.publish "/queue", "message", :suppress_content_length => true end it "should get the correct byte length when dealing with Unicode characters" do expect(@tcp_socket).to receive(:puts).with("content-length:18") @connection.publish "/queue", "сообщение" # 'сообщение' is 'message' in Russian end end describe "when unacknowledging a message" do before :each do @message = Stomp::Message.new(nil) @message.body = "message body" @message.headers = {"destination" => "/queue/original", "message-id" => "ID"} @transaction_id = "transaction-#{@message.headers["message-id"]}-0" @retry_headers = { :destination => @message.headers["destination"], :transaction => @transaction_id, :retry_count => 1 } end it "should use a transaction" do expect(@connection).to receive(:begin).with(@transaction_id).ordered expect(@connection).to receive(:commit).with(@transaction_id).ordered @connection.unreceive @message end it "should acknowledge the original message if ack mode is client" do expect(@connection).to receive(:ack).with(@message.headers["message-id"], :transaction => @transaction_id) @connection.subscribe(@message.headers["destination"], :ack => "client") @connection.unreceive @message end it "should acknowledge the original message if forced" do @connection.subscribe(@message.headers["destination"]) expect(@connection).to receive(:ack) @connection.unreceive(@message, :force_client_ack => true) end it "should not acknowledge the original message if ack mode is not client or it did not subscribe to the queue" do @connection.subscribe(@message.headers["destination"], :ack => "client") expect(@connection).to receive(:ack) @connection.unreceive @message # At this time the message headers are symbolized @connection.unsubscribe(@message.headers[:destination]) expect(@connection).not_to receive(:ack) @connection.unreceive @message @connection.subscribe(@message.headers[:destination], :ack => "individual") @connection.unreceive @message end it "should send the message back to the queue it came" do @connection.subscribe(@message.headers["destination"], :ack => "client") expect(@connection).to receive(:publish).with(@message.headers["destination"], @message.body, @retry_headers) @connection.unreceive @message end it "should increment the retry_count header" do @message.headers["retry_count"] = 4 @connection.unreceive @message expect(@message.headers[:retry_count]).to eq(5) end it "should not send the message to the dead letter queue as persistent if retry_count is less than max redeliveries" do max_redeliveries = 5 dead_letter_queue = "/queue/Dead" @message.headers["retry_count"] = max_redeliveries - 1 transaction_id = "transaction-#{@message.headers["message-id"]}-#{@message.headers["retry_count"]}" @retry_headers = @retry_headers.merge :transaction => transaction_id, :retry_count => @message.headers["retry_count"] + 1 expect(@connection).to receive(:publish).with(@message.headers["destination"], @message.body, @retry_headers) @connection.unreceive @message, :dead_letter_queue => dead_letter_queue, :max_redeliveries => max_redeliveries end # If the retry_count has reached max_redeliveries, then we're done. it "should send the message to the dead letter queue as persistent if max redeliveries have been reached" do max_redeliveries = 5 dead_letter_queue = "/queue/Dead" @message.headers["retry_count"] = max_redeliveries transaction_id = "transaction-#{@message.headers["message-id"]}-#{@message.headers["retry_count"]}" @retry_headers = @retry_headers.merge :persistent => true, :transaction => transaction_id, :retry_count => @message.headers["retry_count"] + 1, :original_destination=> @message.headers["destination"] expect(@connection).to receive(:publish).with(dead_letter_queue, @message.body, @retry_headers) @connection.unreceive @message, :dead_letter_queue => dead_letter_queue, :max_redeliveries => max_redeliveries end it "should rollback the transaction and raise the exception if happened during transaction" do expect(@connection).to receive(:publish).and_raise "Error" expect(@connection).to receive(:abort).with(@transaction_id) expect {@connection.unreceive @message}.to raise_error("Error") end end describe "when sending a nil message body" do it "should should not raise an error" do @connection = Stomp::Connection.new("niluser", "nilpass", "localhost", 61613) expect { @connection.publish("/queue/nilq", nil) }.not_to raise_error end end describe "when using ssl" do # Mocking ruby's openssl extension, so we can test without requiring openssl module ::OpenSSL module SSL VERIFY_NONE = 0 VERIFY_PEER = 1 class SSLSocket end class SSLContext attr_accessor :verify_mode end end end before(:each) do ssl_context = double(:verify_mode => OpenSSL::SSL::VERIFY_PEER) ssl_parameters = {:hosts => [{:login => "login2", :passcode => "passcode2", :host => "remotehost", :ssl => true}]} @ssl_socket = double(:ssl_socket, :puts => nil, :write => nil, :setsockopt => nil, :flush => true, :context => ssl_context) allow(@ssl_socket).to receive(:sync_close=) expect(TCPSocket).to receive(:open).and_return @tcp_socket expect(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(@ssl_socket) expect(@ssl_socket).to receive(:connect) expect(@ssl_socket).to receive(:post_connection_check) @connection = Stomp::Connection.new ssl_parameters end it "should use ssl socket if ssl use is enabled" do expect(@connection.instance_variable_get(:@socket)).to eq(@ssl_socket) end it "should use default port for ssl (61612) if none is given" do expect(@connection.instance_variable_get(:@port)).to eq(61612) end end describe "when called to increase reconnect delay" do it "should exponentialy increase when use_exponential_back_off is true" do expect(@connection.send(:increase_reconnect_delay)).to eq(0.02) expect(@connection.send(:increase_reconnect_delay)).to eq(0.04) expect(@connection.send(:increase_reconnect_delay)).to eq(0.08) end it "should not increase when use_exponential_back_off is false" do @parameters[:use_exponential_back_off] = false @connection = Stomp::Connection.new(@parameters) expect(@connection.send(:increase_reconnect_delay)).to eq(0.01) expect(@connection.send(:increase_reconnect_delay)).to eq(0.01) end it "should not increase when max_reconnect_delay is reached" do @parameters[:initial_reconnect_delay] = 8.0 @connection = Stomp::Connection.new(@parameters) expect(@connection.send(:increase_reconnect_delay)).to eq(16.0) expect(@connection.send(:increase_reconnect_delay)).to eq(30.0) end it "should change to next host on socket error" do @connection.instance_variable_set(:@failure, "some exception") #retries the same host expect(TCPSocket).to receive(:open).and_raise "exception" #tries the new host expect(TCPSocket).to receive(:open).and_return @tcp_socket @connection.send(:socket) expect(@connection.instance_variable_get(:@host)).to eq("remotehost") end it "should use default options if those where not given" do expected_hash = { :hosts => [ {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false}, # Once connected the host is sent to the end of array {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false} ], :reliable => true, :initial_reconnect_delay => 0.01, :max_reconnect_delay => 30.0, :use_exponential_back_off => true, :back_off_multiplier => 2, :max_reconnect_attempts => 0, :randomize => false, :connect_timeout => 0, :parse_timeout => 5, :connect_headers => {}, :dmh => false, :closed_check => true, :hbser => false, :stompconn => false, :max_hbread_fails => 0, :max_hbrlck_fails => 0, :fast_hbs_adjust => 0.0, :connread_timeout => 0, :tcp_nodelay => true, :start_timeout => 0, :sslctx_newparm => nil, :ssl_post_conn_check => true, } used_hash = { :hosts => [ {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false}, {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false} ] } @connection = Stomp::Connection.new(used_hash) expect(@connection.instance_variable_get(:@parameters)).to eq(expected_hash) end it "should use the given options instead of default ones" do used_hash = { :hosts => [ {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false}, {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false} ], :initial_reconnect_delay => 5.0, :max_reconnect_delay => 100.0, :use_exponential_back_off => false, :back_off_multiplier => 3, :max_reconnect_attempts => 10, :randomize => true, :reliable => false, :connect_timeout => 0, :parse_timeout => 20, :connect_headers => {:lerolero => "ronaldo"}, :dead_letter_queue => "queue/Error", :max_redeliveries => 10, :dmh => false, :closed_check => true, :hbser => true, :stompconn => true, :usecrlf => true, :max_hbread_fails => 123, :max_hbrlck_fails => 456, :fast_hbs_adjust => 0.2, :connread_timeout => 42, :tcp_nodelay => false, :start_timeout => 6, :sslctx_newparm=>:TLSv1, :ssl_post_conn_check =>false, } @connection = Stomp::Connection.new(used_hash) received_hash = @connection.instance_variable_get(:@parameters) # Using randomize we can't assure the hosts order received_hash.delete(:hosts) used_hash.delete(:hosts) expect(received_hash).to eq(used_hash) end end end describe "when closing a socket" do it "should close the tcp connection" do expect(@tcp_socket).to receive(:close) expect(@connection.__send__(:close_socket)).to be true # Use Object.__send__ end it "should ignore exceptions" do expect(@tcp_socket).to receive(:close).and_raise "exception" expect(@connection.__send__(:close_socket)).to be true # Use Object.__send__ end end describe "when checking if max reconnect attempts have been reached" do it "should return false if not using failover" do host = @parameters[:hosts][0] @connection = Stomp::Connection.new(host[:login], host[:passcode], host[:host], host[:port], reliable = true, 5, connect_headers = {}) @connection.instance_variable_set(:@connection_attempts, 10000) expect(@connection.send(:max_reconnect_attempts?)).to be false end it "should return false if max_reconnect_attempts = 0" do @connection.instance_variable_set(:@connection_attempts, 10000) expect(@connection.send(:max_reconnect_attempts?)).to be false end it "should return true if connection attempts > max_reconnect_attempts" do limit = 10000 @parameters[:max_reconnect_attempts] = limit @connection = Stomp::Connection.new(@parameters) @connection.instance_variable_set(:@connection_attempts, limit-1) expect(@connection.send(:max_reconnect_attempts?)).to be false @connection.instance_variable_set(:@connection_attempts, limit) expect(@connection.send(:max_reconnect_attempts?)).to be true end # These should be raised for the user to deal with it "should not rescue MaxReconnectAttempts" do @connection = Stomp::Connection.new(@parameters) allow(@connection).to receive(:socket).and_raise(Stomp::Error::MaxReconnectAttempts) expect { @connection.receive() }.to raise_error(RuntimeError) end end end stomp-1.4.4/spec/client_shared_examples.rb0000644000004100000410000000360613120662775020711 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'spec_helper' shared_examples_for "standard Client" do before(:each) do @destination = "/queue/test/ruby/client" @message_text = "test_client-#{Time.now.to_i}" end describe "the closed? method" do it "should be false when the connection is open" do allow(@mock_connection).to receive(:closed?).and_return(false) expect(@client.closed?).to eq(false) end it "should be true when the connection is closed" do allow(@mock_connection).to receive(:closed?).and_return(true) expect(@client.closed?).to eq(true) end end describe "the open? method" do it "should be true when the connection is open" do allow(@mock_connection).to receive(:open?).and_return(true) expect(@client.open?).to eq(true) end it "should be false when the connection is closed" do allow(@mock_connection).to receive(:open?).and_return(false) expect(@client.open?).to eq(false) end end describe "the subscribe method" do before(:each) do allow(@mock_connection).to receive(:subscribe).and_return(true) end it "should raise RuntimeError if not passed a block" do expect { @client.subscribe(@destination) }.to raise_error(RuntimeError) end it "should not raise an error when passed a block" do expect { @client.subscribe(@destination) {|msg| received = msg} }.not_to raise_error end it "should raise RuntimeError on duplicate subscriptions" do expect { @client.subscribe(@destination) @client.subscribe(@destination) }.to raise_error(RuntimeError) end it "should raise RuntimeError with duplicate id headers" do expect { @client.subscribe(@destination, {'id' => 'abcdef'}) @client.subscribe(@destination, {'id' => 'abcdef'}) }.to raise_error(RuntimeError) end end end stomp-1.4.4/spec/spec_helper.rb0000644000004100000410000000046113120662775016474 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'rubygems' if RUBY_VERSION < "1.9" require 'rspec' dir = File.dirname(__FILE__) lib_path = File.expand_path("#{dir}/../lib") $LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path) puts "RSpec version: #{RSpec::Version::STRING}" $stdout.flush require 'stomp' stomp-1.4.4/spec/client_spec.rb0000644000004100000410000005214313120662775016477 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'spec_helper' require 'client_shared_examples' describe Stomp::Client do let(:null_logger) { double("mock Stomp::NullLogger") } before(:each) do allow(Stomp::NullLogger).to receive(:new).and_return(null_logger) @mock_connection = double('connection', :autoflush= => true) allow(Stomp::Connection).to receive(:new).and_return(@mock_connection) end describe "(created with no params)" do before(:each) do @client = Stomp::Client.new end it "should not return any errors" do expect { @client = Stomp::Client.new }.not_to raise_error end it "should not return any errors when created with the open constructor" do expect { @client = Stomp::Client.open }.not_to raise_error end it_should_behave_like "standard Client" end describe 'delegated params' do before :each do @mock_connection = double('connection', :autoflush= => true, :login => 'dummy login', :passcode => 'dummy passcode', :port => 12345, :host => 'dummy host', :ssl => 'dummy ssl') allow(Stomp::Connection).to receive(:new).and_return(@mock_connection) @client = Stomp::Client.new end describe 'it should delegate parameters to its connection' do subject { @client } describe '#login' do subject { super().login } it { should eql 'dummy login' } end describe '#passcode' do subject { super().passcode } it { should eql 'dummy passcode' } end describe '#port' do subject { super().port } it { should eql 12345 } end describe '#host' do subject { super().host } it { should eql 'dummy host' } end describe '#ssl' do subject { super().ssl } it { should eql 'dummy ssl' } end end end describe "(autoflush)" do it "should delegate to the connection for accessing the autoflush property" do expect(@mock_connection).to receive(:autoflush) Stomp::Client.new.autoflush end it "should delegate to the connection for setting the autoflush property" do expect(@mock_connection).to receive(:autoflush=).with(true) Stomp::Client.new.autoflush = true end it "should set the autoflush property on the connection when passing in autoflush as a parameter to the Stomp::Client" do expect(@mock_connection).to receive(:autoflush=).with(true) Stomp::Client.new("login", "password", 'localhost', 61613, false, true) end end describe "(created with invalid params)" do it "should return ArgumentError if port is nil" do expect { @client = Stomp::Client.new('login', 'passcode', 'localhost', nil) }.to raise_error(ArgumentError) end it "should return ArgumentError if port is < 1" do expect { @client = Stomp::Client.new('login', 'passcode', 'localhost', 0) }.to raise_error(ArgumentError) end it "should return ArgumentError if port is > 65535" do expect { @client = Stomp::Client.new('login', 'passcode', 'localhost', 65536) }.to raise_error(ArgumentError) end it "should return ArgumentError if port is empty" do expect { @client = Stomp::Client.new('login', 'passcode', 'localhost', '') }.to raise_error(ArgumentError) end it "should return ArgumentError if reliable is something other than true or false" do expect { @client = Stomp::Client.new('login', 'passcode', 'localhost', '12345', 'foo') }.to raise_error(ArgumentError) end end describe "(created with positional params)" do before(:each) do @client = Stomp::Client.new('testlogin', 'testpassword', 'localhost', '12345', false) @cli_thread = @client.parameters[:client_main] end it "should properly parse the URL provided" do expect(Stomp::Connection).to receive(:new).with(:hosts => [{:login => 'testlogin', :passcode => 'testpassword', :host => 'localhost', :port => 12345}], :logger => null_logger, :reliable => false, :client_main => @cli_thread) Stomp::Client.new('testlogin', 'testpassword', 'localhost', '12345', false) end it_should_behave_like "standard Client" end describe "(created with non-authenticating stomp:// URL and non-TLD host)" do before(:each) do @client = Stomp::Client.new('stomp://foobar:12345') @cli_thread = @client.parameters[:client_main] end it "should properly parse the URL provided" do expect(Stomp::Connection).to receive(:new).with(:hosts => [{:login => '', :passcode => '', :host => 'foobar', :port => 12345}], :logger => null_logger, :reliable => false, :client_main => @cli_thread) Stomp::Client.new('stomp://foobar:12345') end it_should_behave_like "standard Client" end describe "(created with non-authenticating stomp:// URL and a host with a '-')" do before(:each) do @client = Stomp::Client.new('stomp://foo-bar:12345') @cli_thread = @client.parameters[:client_main] end it "should properly parse the URL provided" do expect(Stomp::Connection).to receive(:new).with(:hosts => [{:login => '', :passcode => '', :host => 'foo-bar', :port => 12345}], :logger => null_logger, :reliable => false, :client_main => @cli_thread) Stomp::Client.new('stomp://foo-bar:12345') end it_should_behave_like "standard Client" end describe "(created with authenticating stomp:// URL and non-TLD host)" do before(:each) do @client = Stomp::Client.new('stomp://test-login:testpasscode@foobar:12345') @cli_thread = @client.parameters[:client_main] end it "should properly parse the URL provided" do expect(Stomp::Connection).to receive(:new).with(:hosts => [{:login => 'test-login', :passcode => 'testpasscode', :host => 'foobar', :port => 12345}], :logger => null_logger, :reliable => false, :client_main => @cli_thread) Stomp::Client.new('stomp://test-login:testpasscode@foobar:12345') end it_should_behave_like "standard Client" end describe "(created with authenticating stomp:// URL and a host with a '-')" do before(:each) do @client = Stomp::Client.new('stomp://test-login:testpasscode@foo-bar:12345') @cli_thread = @client.parameters[:client_main] end it "should properly parse the URL provided" do expect(Stomp::Connection).to receive(:new).with(:hosts => [{:login => 'test-login', :passcode => 'testpasscode', :host => 'foo-bar', :port => 12345}], :logger => null_logger, :reliable => false, :client_main => @cli_thread) Stomp::Client.new('stomp://test-login:testpasscode@foo-bar:12345') end it_should_behave_like "standard Client" end describe "(created with non-authenticating stomp:// URL and TLD host)" do before(:each) do @client = Stomp::Client.new('stomp://host.foobar.com:12345') @cli_thread = @client.parameters[:client_main] end after(:each) do end it "should properly parse the URL provided" do expect(Stomp::Connection).to receive(:new).with(:hosts => [{:login => '', :passcode => '', :host => 'host.foobar.com', :port => 12345}], :logger => null_logger, :reliable => false, :client_main => @cli_thread) Stomp::Client.new('stomp://host.foobar.com:12345') end it_should_behave_like "standard Client" end describe "(created with authenticating stomp:// URL and non-TLD host)" do before(:each) do @client = Stomp::Client.new('stomp://testlogin:testpasscode@host.foobar.com:12345') @cli_thread = @client.parameters[:client_main] end it "should properly parse the URL provided" do expect(Stomp::Connection).to receive(:new).with(:hosts => [{:login => 'testlogin', :passcode => 'testpasscode', :host => 'host.foobar.com', :port => 12345}], :logger => null_logger, :reliable => false, :client_main => @cli_thread) Stomp::Client.new('stomp://testlogin:testpasscode@host.foobar.com:12345') end it_should_behave_like "standard Client" end describe "(created with failover URL)" do before(:each) do @client = Stomp::Client.new('failover://(stomp://login1:passcode1@localhost:61616,stomp://login2:passcode2@remotehost:61617)') @cli_thread = @client.parameters[:client_main] #default options @parameters = { :initial_reconnect_delay => 0.01, :max_reconnect_delay => 30.0, :use_exponential_back_off => true, :back_off_multiplier => 2, :max_reconnect_attempts => 0, :randomize => false, :connect_timeout => 0, :reliable => true } end it "should properly parse a URL with failover://" do url = "failover://(stomp://login1:passcode1@localhost:61616,stomp://login2:passcode2@remotehost:61617)" @parameters[:hosts] = [ {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false}, {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false} ] @parameters.merge!({:logger => null_logger}) expect(Stomp::Connection).to receive(:new).with(@parameters) @parameters[:client_main] = @cli_thread client = Stomp::Client.new(url) expect(client.parameters).to eq(@parameters) end it "should properly parse a URL with failover:" do url = "failover:(stomp://login1:passcode1@localhost:61616,stomp://login2:passcode2@remotehost1:61617,stomp://login3:passcode3@remotehost2:61618)" @parameters[:hosts] = [ {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false}, {:login => "login2", :passcode => "passcode2", :host => "remotehost1", :port => 61617, :ssl => false}, {:login => "login3", :passcode => "passcode3", :host => "remotehost2", :port => 61618, :ssl => false} ] @parameters.merge!({:logger => null_logger}) @parameters[:client_main] = @cli_thread expect(Stomp::Connection).to receive(:new).with(@parameters) client = Stomp::Client.new(url) expect(client.parameters).to eq(@parameters) end it "should properly parse a URL without user and password" do url = "failover:(stomp://localhost:61616,stomp://remotehost:61617)" @parameters[:hosts] = [ {:login => "", :passcode => "", :host => "localhost", :port => 61616, :ssl => false}, {:login => "", :passcode => "", :host => "remotehost", :port => 61617, :ssl => false} ] @parameters.merge!({:logger => null_logger}) @parameters[:client_main] = @cli_thread expect(Stomp::Connection).to receive(:new).with(@parameters) client = Stomp::Client.new(url) @parameters[:client_main] = client.parameters[:client_main] expect(client.parameters).to eq(@parameters) end it "should properly parse a URL with user and/or password blank" do url = "failover:(stomp://@localhost:61616,stomp://@remotehost:61617)" @parameters[:hosts] = [ {:login => "", :passcode => "", :host => "localhost", :port => 61616, :ssl => false}, {:login => "", :passcode => "", :host => "remotehost", :port => 61617, :ssl => false} ] @parameters.merge!({:logger => null_logger}) @parameters[:client_main] = @cli_thread expect(Stomp::Connection).to receive(:new).with(@parameters) client = Stomp::Client.new(url) @parameters[:client_main] = client.parameters[:client_main] expect(client.parameters).to eq(@parameters) end it "should properly parse a URL with the options query" do query = "initialReconnectDelay=5000&maxReconnectDelay=60000&useExponentialBackOff=false&backOffMultiplier=3" query += "&maxReconnectAttempts=4&randomize=true&backup=true&timeout=10000" url = "failover:(stomp://login1:passcode1@localhost:61616,stomp://login2:passcode2@remotehost:61617)?#{query}" # @parameters = { :initial_reconnect_delay => 5.0, :max_reconnect_delay => 60.0, :use_exponential_back_off => false, :back_off_multiplier => 3, :max_reconnect_attempts => 4, :randomize => true, :connect_timeout => 0, :reliable => true } @parameters[:hosts] = [ {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false}, {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false} ] @parameters.merge!({:logger => null_logger}) @parameters[:client_main] = @cli_thread expect(Stomp::Connection).to receive(:new).with(@parameters) client = Stomp::Client.new(url) @parameters[:client_main] = client.parameters[:client_main] expect(client.parameters).to eq(@parameters) end end describe '#error_listener' do context 'on getting a ResourceAllocationException' do let(:message) do message = Stomp::Message.new('') message.body = "javax.jms.ResourceAllocationException: Usage" message.headers = {'message' => %q{message = "Usage Manager Memory Limit reached. Stopping producer (ID:producer) to prevent flooding queue://errors. See } } message.command = Stomp::CMD_ERROR message end it 'should handle ProducerFlowControlException errors by raising' do expect do @client = Stomp::Client.new @error_listener = @client.instance_variable_get(:@error_listener) @error_listener.call(message) end.to raise_exception(Stomp::Error::ProducerFlowControlException) end end end describe '(used with custom headers)' do before :each do @client = Stomp::Client.new end def original_headers {:custom_header => 'value'} end let(:connection_headers) { original_headers } let(:headers) { original_headers } shared_examples_for 'argument-safe method' do describe 'given headers hash' do subject { headers } it 'is immutable' do expect match(original_headers) end end end describe '#begin' do before { expect(@mock_connection).to receive(:begin).with('name', connection_headers) @client.begin('name', headers) } it_behaves_like 'argument-safe method' end describe '#abort' do before { expect(@mock_connection).to receive(:abort).with('name', connection_headers) @client.abort('name', headers) } it_behaves_like 'argument-safe method' end describe '#commit' do before { expect(@mock_connection).to receive(:commit).with('name', connection_headers) @client.commit('name', headers) } it_behaves_like 'argument-safe method' end describe '#subscribe' do let(:connection_headers) { original_headers.merge({:id => Digest::SHA1.hexdigest('destination')}) } before { expect(@mock_connection).to receive(:subscribe).with('destination', connection_headers) @client.subscribe('destination', headers) {|dummy_subscriber| } } it_behaves_like 'argument-safe method' end describe '#unsubscribe' do let(:connection_headers) { original_headers.merge({:id => Digest::SHA1.hexdigest('destination')}) } before { expect(@mock_connection).to receive(:unsubscribe).with('destination', connection_headers) @client.unsubscribe('destination', headers) {|dummy_subscriber| } } it_behaves_like 'argument-safe method' end describe '#ack' do describe 'with STOMP 1.0' do let(:message) { double('message', :headers => {'message-id' => 'id'}) } before { allow(@client).to receive(:protocol).and_return(Stomp::SPL_10) expect(@mock_connection).to receive(:ack).with('id', connection_headers) @client.ack(message, headers) } it_behaves_like 'argument-safe method' end describe 'with STOMP 1.1' do let(:message) { double('message', :headers => {'message-id' => 'id', 'subscription' => 'subscription_name'}) } let(:connection_headers) { original_headers.merge({:subscription => 'subscription_name'}) } before { allow(@client).to receive(:protocol).and_return(Stomp::SPL_11) expect(@mock_connection).to receive(:ack).with('id', connection_headers) @client.ack(message, headers) } it_behaves_like 'argument-safe method' end describe 'with STOMP 1.2' do let(:message) { double('message', :headers => {'ack' => 'id'}) } before { allow(@client).to receive(:protocol).and_return(Stomp::SPL_12) expect(@mock_connection).to receive(:ack).with('id', connection_headers) @client.ack(message, headers) } it_behaves_like 'argument-safe method' end end describe '#nack' do describe 'with STOMP 1.0' do let(:message) { double('message', :headers => {'message-id' => 'id'}) } before { allow(@client).to receive(:protocol).and_return(Stomp::SPL_10) expect(@mock_connection).to receive(:nack).with('id', connection_headers) @client.nack(message, headers) } it_behaves_like 'argument-safe method' end describe 'with STOMP 1.1' do let(:message) { double('message', :headers => {'message-id' => 'id', 'subscription' => 'subscription_name'}) } let(:connection_headers) { original_headers.merge({:subscription => 'subscription_name'}) } before { allow(@client).to receive(:protocol).and_return(Stomp::SPL_11) expect(@mock_connection).to receive(:nack).with('id', connection_headers) @client.nack(message, headers) } it_behaves_like 'argument-safe method' end describe 'with STOMP 1.2' do let(:message) { double('message', :headers => {'ack' => 'id'}) } before { allow(@client).to receive(:protocol).and_return(Stomp::SPL_12) expect(@mock_connection).to receive(:nack).with('id', connection_headers) @client.nack(message, headers) } it_behaves_like 'argument-safe method' end end describe '#publish' do describe 'without listener' do let(:message) { double('message') } before { expect(@mock_connection).to receive(:publish).with('destination', message, connection_headers) @client.publish('destination', message, headers) } it_behaves_like 'argument-safe method' end describe 'with listener' do let(:message) { double('message') } let(:connection_headers) { original_headers.merge({:receipt => 'receipt-uuid'}) } before { allow(@client).to receive(:uuid).and_return('receipt-uuid') expect(@mock_connection).to receive(:publish).with('destination', message, connection_headers) @client.publish('destination', message, headers) {|dummy_listener| } } it_behaves_like 'argument-safe method' end end end end stomp-1.4.4/spec/message_spec.rb0000644000004100000410000000324213120662775016641 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'spec_helper' describe Stomp::Message do context 'when initializing a new message' do context 'with invalid parameters' do it 'should return an empty message when receiving an empty string or nil parameter' do message = Stomp::Message.new('') expect(message).to be_empty # message.should be_empty end it 'should raise Stomp::Error::InvalidFormat when receiving a invalid formated message' do expect{ Stomp::Message.new('any invalid format') }.to raise_error(Stomp::Error::InvalidFormat) end end context 'with valid parameters' do subject do @message = ["CONNECTED\n", "session:host_address\n", "\n", "body value\n", "\000\n"] Stomp::Message.new(@message.join) end it 'should parse the headers' do expect(subject.headers).to eq({'session' => 'host_address'}) end it 'should parse the body' do expect(subject.body).to eq(@message[3]) end it 'should parse the command' do expect(subject.command).to eq(@message[0].chomp) end end context 'with multiple line ends on the body' do subject do @message = ["CONNECTED\n", "session:host_address\n", "\n", "body\n\n value\n\n\n", "\000\n"] Stomp::Message.new(@message.join) end it 'should parse the headers' do expect(subject.headers).to eq({'session' => 'host_address'}) end it 'should parse the body' do expect(subject.body).to eq(@message[3]) end it 'should parse the command' do expect(subject.command).to eq(@message[0].chomp) end end end end stomp-1.4.4/lib/0000755000004100000410000000000013120662775013471 5ustar www-datawww-datastomp-1.4.4/lib/stomp.rb0000644000004100000410000000311213120662775015155 0ustar www-datawww-data# -*- encoding: utf-8 -*- # Copyright 2005-2006 Brian McCallister # Copyright 2006 LogicBlaze Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'stomp/constants' # Constants first require 'stomp/ext/hash' # #Hash additions require 'stomp/connection' # Main Stomp#Connection require 'stomp/client' # Main Stomp#Client require 'stomp/message' # Stomp#Message require 'stomp/version' # Stomp#Version#STRING require 'stomp/errors' # All Stomp# exceptions require 'stomp/codec' # Stomp 1.1 codec require 'stomp/sslparams' # Stomp SSL support require 'stomp/null_logger' # A NullLogger class # Private methods in #Client require 'client/utils' # private Client Utility methods # Private methods in #Connection require 'connection/utils' # private Connection Utility methods require 'connection/netio' # private Network io methods require 'connection/heartbeats' # private 1.1+ heartbeat related methods require 'connection/utf8' # private 1.1+ UTF8 related methods module Stomp end stomp-1.4.4/lib/client/0000755000004100000410000000000013120662775014747 5ustar www-datawww-datastomp-1.4.4/lib/client/utils.rb0000644000004100000410000001634313120662775016443 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'thread' require 'digest/sha1' module Stomp class Client private def parse_hash_params(params) return false unless params.is_a?(Hash) @parameters = params # Do not override user choice of false. @parameters[:reliable] = true unless @parameters[:reliable] == false true end def parse_stomp_url(login) original_verbose, $VERBOSE = $VERBOSE, nil # shut off warnings regexp = /^stomp:\/\/#{URL_REPAT}/ url = regexp.match(login) $VERBOSE = original_verbose return false unless url @login = url[3] || "" @passcode = url[4] || "" @host = url[5] @port = url[6].to_i @parameters = { :reliable => false, :hosts => [ { :login => @login, :passcode => @passcode, :host => @host, :port => @port} ] } true end # e.g. failover://(stomp://login1:passcode1@localhost:61616,stomp://login2:passcode2@remotehost:61617)?option1=param def parse_failover_url(login) rval = nil original_verbose, $VERBOSE = $VERBOSE, nil # shut off warnings md = FAILOVER_REGEX.match(login) $VERBOSE = original_verbose if md finhosts = parse_hosts(login) options = {} if md_last = md[-1] parts = md_last.split(/&|=/) raise Stomp::Error::MalformedFailoverOptionsError unless ( parts.size % 2 ) == 0 options = Hash[*parts] end @parameters = {:hosts => finhosts}.merge!(filter_options(options)) @parameters[:reliable] = true rval = true end rval end def parse_positional_params(login, passcode, host, port, reliable) @parameters = { :reliable => reliable, :hosts => [ { :login => login, :passcode => passcode, :host => host, :port => port.to_i } ] } true end # Set a subscription id in the headers hash if one does not already exist. # For simplicities sake, all subscriptions have a subscription ID. # setting an id in the SUBSCRIPTION header is described in the stomp protocol docs: # http://stomp.github.com/ def set_subscription_id_if_missing(destination, headers) headers[:id] = build_subscription_id(destination, headers) end def build_subscription_id(destination, headers) return headers[:id] until headers[:id].nil? return headers['id'] until headers['id'].nil? # p [ "DBBSID1", destination, headers ] Digest::SHA1.hexdigest(destination) end # Parse a stomp URL. def parse_hosts(url) hosts = [] original_verbose, $VERBOSE = $VERBOSE, nil # shut off warnings host_match = /stomp(\+ssl)?:\/\/#{URL_REPAT}/ url.scan(host_match).each do |match| host = {} host[:ssl] = match[0] == "+ssl" ? true : false host[:login] = match[3] || "" host[:passcode] = match[4] || "" host[:host] = match[5] host[:port] = match[6].to_i hosts << host end $VERBOSE = original_verbose hosts end # A sanity check of required arguments. def check_arguments!() raise ArgumentError.new("missing :hosts parameter") unless @parameters[:hosts] raise ArgumentError.new("invalid :hosts type") unless @parameters[:hosts].is_a?(Array) @parameters[:hosts].each do |hv| # Validate port requested raise ArgumentError.new("empty :port value in #{hv.inspect}") if hv[:port] == '' unless hv[:port].nil? tpv = hv[:port].to_i raise ArgumentError.new("invalid :port value=#{tpv} from #{hv.inspect}") if tpv < 1 || tpv > 65535 end # Validate host requested (no validation here. if nil or '', localhost will # be used in #Connection.) end raise ArgumentError unless @parameters[:reliable].is_a?(TrueClass) || @parameters[:reliable].is_a?(FalseClass) # if @parameters[:reliable] && @start_timeout > 0 warn "WARN detected :reliable == true and :start_timeout > 0" warn "WARN this may cause incorrect fail-over behavior" warn "WARN use :start_timeout => 0 to correct fail-over behavior" end end # filter_options returns a new Hash of filtered options. def filter_options(options) new_options = {} new_options[:initial_reconnect_delay] = (options["initialReconnectDelay"] || 10).to_f / 1000 # In ms new_options[:max_reconnect_delay] = (options["maxReconnectDelay"] || 30000 ).to_f / 1000 # In ms new_options[:use_exponential_back_off] = !(options["useExponentialBackOff"] == "false") # Default: true new_options[:back_off_multiplier] = (options["backOffMultiplier"] || 2 ).to_i new_options[:max_reconnect_attempts] = (options["maxReconnectAttempts"] || 0 ).to_i new_options[:randomize] = options["randomize"] == "true" # Default: false new_options[:connect_timeout] = 0 new_options end # find_listener returns the listener for a given subscription in a given message. def find_listener(message) subscription_id = message.headers['subscription'] if subscription_id == nil # For backward compatibility, some messages may already exist with no # subscription id, in which case we can attempt to synthesize one. set_subscription_id_if_missing(message.headers['destination'], message.headers) subscription_id = message.headers[:id] end listener = @listeners[subscription_id] listener.call(message) if listener end # Register a receipt listener. def register_receipt_listener(listener) id = uuid @receipt_listeners[id] = listener id end def find_receipt_listener(message) listener = @receipt_listeners[message.headers['receipt-id']] if listener listener.call(message) @receipt_listeners.delete(message.headers['receipt-id']) end end def create_listener_maps @listeners = {} @receipt_listeners = {} @replay_messages_by_txn = {} @listener_map = Hash.new do |message| unless @connection.slog(:on_miscerr, @connection.log_params, "Received unknown frame type: '#{message.command}'\n") warn "Received unknown frame type: '#{message.command}'\n" end end @listener_map[Stomp::CMD_MESSAGE] = lambda {|message| find_listener(message) } @listener_map[Stomp::CMD_RECEIPT] = lambda {|message| find_receipt_listener(message) } @listener_map[Stomp::CMD_ERROR] = @error_listener end # Start a single listener thread. Misnamed I think. def start_listeners() create_listener_maps @listener_thread = Thread.start do loop do message = @connection.receive # AMQ specific behavior if message.nil? && (!@parameters[:reliable]) raise Stomp::Error::NilMessageError end next unless message # message can be nil on rapid AMQ stop/start sequences @listener_map[message.command].call(message) end end end # method start_listeners end # class Client end # module Stomp stomp-1.4.4/lib/stomp/0000755000004100000410000000000013120662775014633 5ustar www-datawww-datastomp-1.4.4/lib/stomp/codec.rb0000644000004100000410000000172413120662775016241 0ustar www-datawww-data# -*- encoding: utf-8 -*- module Stomp # # == Purpose # # A general CODEC for STOMP 1.1 header keys and values. # # See: # # * http://stomp.github.com/index.html # # for encode/decode rules. # class HeaderCodec public # encode encodes header data per the STOMP 1.1 specification. def self.encode(in_string = nil) return in_string unless in_string ev = Stomp::ENCODE_VALUES # avoid typing below os = in_string + "" 0.step(ev.length-2,2) do |i| # [encoded, decoded] os.gsub!(ev[i+1], ev[i]) end os end # decode decodes header data per the STOMP 1.1 specification. def self.decode(in_string = nil) return in_string unless in_string ev = Stomp::DECODE_VALUES # avoid typing below os = in_string + "" 0.step(ev.length-2,2) do |i| # [encoded, decoded] os.gsub!(ev[i], ev[i+1]) end os end end # of class HeaderCodec end # of module Stomp stomp-1.4.4/lib/stomp/connection.rb0000644000004100000410000006023313120662775017323 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'socket' require 'timeout' require 'io/wait' require 'digest/sha1' module Stomp # Low level connection which maps commands and supports # synchronous receives class Connection public # The CONNECTED frame from the broker. attr_reader :connection_frame # Any disconnect RECEIPT frame if requested. attr_reader :disconnect_receipt # The Stomp Protocol version. attr_reader :protocol # A unique session ID, assigned by the broker. attr_reader :session # Heartbeat receive has been on time. attr_reader :hb_received # Heartbeat received on time # Heartbeat send has been successful. attr_reader :hb_sent # Heartbeat sent successfully # JRuby detected attr_reader :jruby # Autoflush forces a flush on each transmit. This may be changed # dynamically by calling code. attr_accessor :autoflush # Currently-connected host and port attr_reader :host, :port # default_port returns the default port used by the gem for TCP or SSL. def self.default_port(ssl) ssl ? 61612 : 61613 end # SSL Helper def self.ssl_v2xoptions() require 'openssl' unless defined?(OpenSSL) # Mimic code in later versions of Ruby 2.x (and backported to later # versions of 1.9.3). opts = OpenSSL::SSL::OP_ALL opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) opts |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) opts |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) end # A new Connection object can be initialized using two forms: # # Hash (this is the recommended Connection initialization method): # # hash = { # :hosts => [ # {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false}, # {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false} # ], # # These are the default parameters and do not need to be set # :reliable => true, # reliable (use failover) # :initial_reconnect_delay => 0.01, # initial delay before reconnect (secs) # :max_reconnect_delay => 30.0, # max delay before reconnect # :use_exponential_back_off => true, # increase delay between reconnect attpempts # :back_off_multiplier => 2, # next delay multiplier # :max_reconnect_attempts => 0, # retry forever, use # for maximum attempts # :randomize => false, # do not radomize hosts hash before reconnect # :connect_timeout => 0, # Timeout for TCP/TLS connects, use # for max seconds # :connect_headers => {}, # user supplied CONNECT headers (req'd for Stomp 1.1+) # :parse_timeout => 5, # IO::select wait time on socket reads # :logger => nil, # user suplied callback logger instance # :dmh => false, # do not support multihomed IPV4 / IPV6 hosts during failover # :closed_check => true, # check first if closed in each protocol method # :hbser => false, # raise on heartbeat send exception # :stompconn => false, # Use STOMP instead of CONNECT # :usecrlf => false, # Use CRLF command and header line ends (1.2+) # :max_hbread_fails => 0, # Max HB read fails before retry. 0 => never retry # :max_hbrlck_fails => 0, # Max HB read lock obtain fails before retry. 0 => never retry # :fast_hbs_adjust => 0.0, # Fast heartbeat senders sleep adjustment, seconds, needed ... # # For fast heartbeat senders. 'fast' == YMMV. If not # # correct for your environment, expect unnecessary fail overs # :connread_timeout => 0, # Timeout during CONNECT for read of CONNECTED/ERROR, secs # :tcp_nodelay => true, # Turns on the TCP_NODELAY socket option; disables Nagle's algorithm # :start_timeout => 0, # Timeout around Stomp::Client initialization # :sslctx_newparm => nil, # Param for SSLContext.new # :ssl_post_conn_check => true, # Further verify broker identity # } # # e.g. c = Stomp::Connection.new(hash) # # Positional parameters: # # login (String, default : '') # passcode (String, default : '') # host (String, default : 'localhost') # port (Integer, default : 61613) # reliable (Boolean, default : false) # reconnect_delay (Integer, default : 5) # # e.g. c = Stomp::Connection.new("username", "password", "localhost", 61613, true) # def initialize(login = '', passcode = '', host = 'localhost', port = 61613, reliable = false, reconnect_delay = 5, connect_headers = {}) @protocol = Stomp::SPL_10 # Assumed at first @hb_received = true # Assumed at first @hb_sent = true # Assumed at first @hbs = @hbr = false # Sending/Receiving heartbeats. Assume no for now. @jruby = false # Assumed at first # Initialize some variables @closed, @socket, @hhas10, @rt, @st = true, nil, false, nil, nil if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/ @jruby = true end if login.is_a?(Hash) hashed_initialize(login) else @host = host @port = port @login = login @passcode = passcode @reliable = reliable @reconnect_delay = reconnect_delay @connect_headers = connect_headers @ssl = false @parameters = nil @parse_timeout = 5 # To override, use hashed parameters @connect_timeout = 0 # To override, use hashed parameters @logger = Stomp::NullLogger.new # To override, use hashed parameters @autoflush = false # To override, use hashed parameters or setter @closed_check = true # Run closed check in each protocol method @hbser = false # Raise if heartbeat send exception @stompconn = false # If true, use STOMP rather than CONNECT @usecrlf = false # If true, use \r\n as line ends (1.2 only) @max_hbread_fails = 0 # 0 means never retry for HB read failures @max_hbrlck_fails = 0 # 0 means never retry for HB read lock failures @fast_hbs_adjust = 0.0 # Fast heartbeat senders sleep adjustment @connread_timeout = 0 # Connect read CONNECTED/ERROR timeout @tcp_nodelay = true # Disable Nagle @start_timeout = 0 # Client only, startup timeout @sslctx_newparm = nil # SSLContext.new paramater @ssl_post_conn_check = true # Additional broker verification warn "login looks like a URL, do you have the correct parameters?" if @login =~ /:\/\// end # Use Mutexes: only one lock per each thread. # Reverted to original implementation attempt using Mutex. @transmit_semaphore = Mutex.new @read_semaphore = Mutex.new @socket_semaphore = Mutex.new @gets_semaphore = Mutex.new @subscriptions = {} @failure = nil @connection_attempts = 0 socket end # hashed_initialize prepares a new connection with a Hash of initialization # parameters. def hashed_initialize(params) lp = _hdup(params) @parameters = refine_params(lp) @reliable = @parameters[:reliable] @reconnect_delay = @parameters[:initial_reconnect_delay] @connect_headers = @parameters[:connect_headers] @parse_timeout = @parameters[:parse_timeout] @connect_timeout = @parameters[:connect_timeout] @logger = @parameters[:logger] || Stomp::NullLogger.new @autoflush = @parameters[:autoflush] @closed_check = @parameters[:closed_check] @hbser = @parameters[:hbser] @stompconn = @parameters[:stompconn] @usecrlf = @parameters[:usecrlf] @max_hbread_fails = @parameters[:max_hbread_fails] @max_hbrlck_fails = @parameters[:max_hbrlck_fails] @fast_hbs_adjust = @parameters[:fast_hbs_adjust] @connread_timeout = @parameters[:connread_timeout] @sslctx_newparm = @parameters[:sslctx_newparm] @ssl_post_conn_check = @parameters[:ssl_post_conn_check] # # Try to support Ruby 1.9.x and 2.x ssl. unless defined?(RSpec) @parameters[:hosts].each do |ah| ah[:ssl] = Stomp::SSLParams.new if ah[:ssl] == true end end #sets the first host to connect change_host end # open is syntactic sugar for 'Connection.new', see 'initialize' for usage. def Connection.open(login = '', passcode = '', host = 'localhost', port = 61613, reliable = false, reconnect_delay = 5, connect_headers = {}) Connection.new(login, passcode, host, port, reliable, reconnect_delay, connect_headers) end # open? tests if this connection is open. def open? !@closed end # closed? tests if this connection is closed. def closed? @closed end # Begin starts a transaction, and requires a name for the transaction def begin(name, headers = {}) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? raise Stomp::Error::ProtocolErrorEmptyHeaderKey if headers.has_key?("") raise Stomp::Error::ProtocolErrorEmptyHeaderValue if @protocol == Stomp::SPL_10 && headers.has_value?("") headers = headers.symbolize_keys headers[:transaction] = name _headerCheck(headers) slog(:on_begin, log_params, headers) transmit(Stomp::CMD_BEGIN, headers) end # Acknowledge a message, used when a subscription has specified # client acknowledgement e.g.: # # connection.subscribe("/queue/a", :ack => 'client') # # connection.subscribe("/queue/a", :ack => 'client-individual') # # as appropriate for the protocol level. # # Accepts an optional transaction header ( :transaction => 'some_transaction_id' ). # # When the connection protocol level is 1.0 or 1.1 the message_or_ack_id parameter # should match the message-id header of the MESSAGE being acknowledged e.g.: # # connection.ack(message.headers['message-id']) # # When the connection protocol level is 1.2 the message_or_ack_id parameter # should match the ack header of the MESSAGE being acknowledged e.g.: # # connection.ack(message.headers['ack']) # # In summary, the behavior is protocol level dependent, see the specifications # and comments in the code. def ack(message_or_ack_id, headers = {}) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? raise Stomp::Error::ProtocolErrorEmptyHeaderKey if headers.has_key?("") raise Stomp::Error::ProtocolErrorEmptyHeaderValue if @protocol == Stomp::SPL_10 && headers.has_value?("") raise Stomp::Error::MessageIDRequiredError if message_or_ack_id.nil? || message_or_ack_id == "" headers = headers.symbolize_keys case @protocol when Stomp::SPL_12 # The ACK frame MUST include an "id" header matching the "ack" header # of the MESSAGE being acknowledged. headers[:id] = message_or_ack_id when Stomp::SPL_11 # ACK has two REQUIRED headers: "message-id", which MUST contain a value # matching the message-id header of the MESSAGE being acknowledged and # "subscription", which MUST be set to match the value of SUBSCRIBE's # id header. headers[:'message-id'] = message_or_ack_id raise Stomp::Error::SubscriptionRequiredError unless headers[:subscription] else # Stomp::SPL_10 # ACK has one required header, "message-id", which must contain a value # matching the message-id for the MESSAGE being acknowledged. headers[:'message-id'] = message_or_ack_id end _headerCheck(headers) slog(:on_ack, log_params, headers) transmit(Stomp::CMD_ACK, headers) end # STOMP 1.1+ NACK. # # When the connection protocol level is 1.1 the message_or_ack_id parameter # should match the message-id header of the MESSAGE being acknowledged. # # When the connection protocol level is 1.2 the message_or_ack_id parameter # should match the ack header of the MESSAGE being acknowledged. # # Behavior is protocol level dependent, see the specifications and comments below. def nack(message_or_ack_id, headers = {}) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? raise Stomp::Error::UnsupportedProtocolError if @protocol == Stomp::SPL_10 raise Stomp::Error::ProtocolErrorEmptyHeaderKey if headers.has_key?("") raise Stomp::Error::MessageIDRequiredError if message_or_ack_id.nil? || message_or_ack_id == "" headers = headers.symbolize_keys case @protocol when Stomp::SPL_12 # The NACK frame MUST include an id header matching the ack header # of the MESSAGE being acknowledged. headers[:id] = message_or_ack_id else # Stomp::SPL_11 only # NACK has two REQUIRED headers: message-id, which MUST contain a value # matching the message-id for the MESSAGE being acknowledged and # subscription, which MUST be set to match the value of the subscription's # id header. headers[:'message-id'] = message_or_ack_id raise Stomp::Error::SubscriptionRequiredError unless headers[:subscription] end _headerCheck(headers) slog(:on_nack, log_params, headers) transmit(Stomp::CMD_NACK, headers) end # Commit commits a transaction by name. def commit(name, headers = {}) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? raise Stomp::Error::ProtocolErrorEmptyHeaderKey if headers.has_key?("") raise Stomp::Error::ProtocolErrorEmptyHeaderValue if @protocol == Stomp::SPL_10 && headers.has_value?("") headers = headers.symbolize_keys headers[:transaction] = name _headerCheck(headers) slog(:on_commit, log_params, headers) transmit(Stomp::CMD_COMMIT, headers) end # Abort aborts a transaction by name. def abort(name, headers = {}) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? raise Stomp::Error::ProtocolErrorEmptyHeaderKey if headers.has_key?("") raise Stomp::Error::ProtocolErrorEmptyHeaderValue if @protocol == Stomp::SPL_10 && headers.has_value?("") headers = headers.symbolize_keys headers[:transaction] = name _headerCheck(headers) slog(:on_abort, log_params, headers) transmit(Stomp::CMD_ABORT, headers) end # Subscribe subscribes to a destination. A subscription name is required. # For Stomp 1.1+ a session unique subscription ID is also required. def subscribe(destination, headers = {}, subId = nil) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? raise Stomp::Error::ProtocolErrorEmptyHeaderKey if headers.has_key?("") raise Stomp::Error::ProtocolErrorEmptyHeaderValue if @protocol == Stomp::SPL_10 && headers.has_value?("") headers = headers.symbolize_keys raise Stomp::Error::DestinationRequired unless destination headers[:destination] = destination if @protocol >= Stomp::SPL_11 raise Stomp::Error::SubscriptionRequiredError if (headers[:id].nil? && subId.nil?) headers[:id] = subId if headers[:id].nil? end _headerCheck(headers) slog(:on_subscribe, log_params, headers) # Store the subscription so that we can replay if we reconnect. if @reliable subId = destination if subId.nil? raise Stomp::Error::DuplicateSubscription if @subscriptions[subId] @subscriptions[subId] = headers end transmit(Stomp::CMD_SUBSCRIBE, headers) end # Unsubscribe from a destination. A subscription name is required. # For Stomp 1.1+ a session unique subscription ID is also required. def unsubscribe(destination, headers = {}, subId = nil) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? raise Stomp::Error::ProtocolErrorEmptyHeaderKey if headers.has_key?("") raise Stomp::Error::ProtocolErrorEmptyHeaderValue if @protocol == Stomp::SPL_10 && headers.has_value?("") headers = headers.symbolize_keys raise Stomp::Error::DestinationRequired unless destination headers[:destination] = destination if @protocol >= Stomp::SPL_11 raise Stomp::Error::SubscriptionRequiredError if (headers[:id].nil? && subId.nil?) headers[:id] = subId unless headers[:id] end _headerCheck(headers) slog(:on_unsubscribe, log_params, headers) transmit(Stomp::CMD_UNSUBSCRIBE, headers) if @reliable subId = destination if subId.nil? @subscriptions.delete(subId) end end # Publish message to destination. # To disable content length header use header ( :suppress_content_length => true ). # Accepts a transaction header ( :transaction => 'some_transaction_id' ). def publish(destination, message, headers = {}) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? raise Stomp::Error::ProtocolErrorEmptyHeaderKey if headers.has_key?("") raise Stomp::Error::ProtocolErrorEmptyHeaderValue if @protocol == Stomp::SPL_10 && headers.has_value?("") headers = headers.symbolize_keys raise Stomp::Error::DestinationRequired unless destination headers[:destination] = destination _headerCheck(headers) slog(:on_publish, log_params, message, headers) transmit(Stomp::CMD_SEND, headers, message) end # Send a message back to the source or to the dead letter queue. # Accepts a dead letter queue option ( :dead_letter_queue => "/queue/DLQ" ). # Accepts a limit number of redeliveries option ( :max_redeliveries => 6 ). # Accepts a force client acknowledgement option (:force_client_ack => true). def unreceive(message, options = {}) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? options = { :dead_letter_queue => "/queue/DLQ", :max_redeliveries => 6 }.merge(options) # Lets make sure all keys are symbols message.headers = message.headers.symbolize_keys retry_count = message.headers[:retry_count].to_i || 0 message.headers[:retry_count] = retry_count + 1 transaction_id = "transaction-#{message.headers[:'message-id']}-#{retry_count}" message_id = message.headers.delete(:'message-id') # Prevent duplicate 'subscription' headers on subsequent receives message.headers.delete(:subscription) if message.headers[:subscription] begin self.begin transaction_id if client_ack?(message) || options[:force_client_ack] self.ack(message_id, :transaction => transaction_id) end if message.headers[:retry_count] <= options[:max_redeliveries] self.publish(message.headers[:destination], message.body, message.headers.merge(:transaction => transaction_id)) else # Poison ack, sending the message to the DLQ self.publish(options[:dead_letter_queue], message.body, message.headers.merge(:transaction => transaction_id, :original_destination => message.headers[:destination], :persistent => true)) end self.commit transaction_id rescue Exception => exception self.abort transaction_id raise exception end end # client_ack? determines if headers contain :ack => "client". def client_ack?(message) headers = @subscriptions[message.headers[:destination]] !headers.nil? && headers[:ack] == "client" end # disconnect closes this connection. If requested, a disconnect RECEIPT # will be received. def disconnect(headers = {}) raise Stomp::Error::NoCurrentConnection if @closed_check && closed? raise Stomp::Error::ProtocolErrorEmptyHeaderKey if headers.has_key?("") raise Stomp::Error::ProtocolErrorEmptyHeaderValue if @protocol == Stomp::SPL_10 && headers.has_value?("") headers = headers.symbolize_keys _headerCheck(headers) if @protocol >= Stomp::SPL_11 @st.kill if @st # Kill ticker thread if any @rt.kill if @rt # Kill ticker thread if any end transmit(Stomp::CMD_DISCONNECT, headers) @disconnect_receipt = receive if headers[:receipt] slog(:on_disconnect, log_params) close_socket end # poll returns a pending message if one is available, otherwise # returns nil. def poll() raise Stomp::Error::NoCurrentConnection if @closed_check && closed? # No need for a read lock here. The receive method eventually fulfills # that requirement. return nil if @socket.nil? || !@socket.ready? receive() end # receive returns the next Message off of the wire. this can return nil # in cases where: # * the broker has closed the connection # * the connection is not reliable def receive() raise Stomp::Error::NoCurrentConnection if @closed_check && closed? super_result = __old_receive() if super_result.nil? && @reliable && !closed? errstr = "connection.receive returning EOF as nil - resetting connection.\n" unless slog(:on_miscerr, log_params, "es_recv: " + errstr) $stderr.print errstr end # !!! This initiates a re-connect !!! # The call to __old_receive() will in turn call socket(). Before # that we should change the target host, otherwise the host that # just failed may be attempted first. _reconn_prep() # super_result = __old_receive() end # if super_result.nil? && !@reliable @st.kill if @st # Kill ticker thread if any @rt.kill if @rt # Kill ticker thread if any close_socket() @closed = true warn 'warning: broker sent EOF, and connection not reliable' unless defined?(Test) end slog(:on_receive, log_params, super_result) return super_result end # set_logger selects a new callback logger instance. def set_logger(logger) @logger = logger end # valid_utf8? returns an indicator if the given string is a valid UTF8 string. def valid_utf8?(s) case RUBY_VERSION when /1\.8/ rv = _valid_utf8?(s) else rv = s.encoding.name != Stomp::UTF8 ? false : s.valid_encoding? end rv end # sha1 returns a SHA1 digest for arbitrary string data. def sha1(data) Digest::SHA1.hexdigest(data) end # uuid returns a type 4 UUID. def uuid() b = [] 0.upto(15) do |i| b << rand(255) end b[6] = (b[6] & 0x0F) | 0x40 b[8] = (b[8] & 0xbf) | 0x80 # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 rs = sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]) rs end # hbsend_interval returns the connection's heartbeat send interval. def hbsend_interval() return 0 unless @hbsend_interval @hbsend_interval / 1000.0 # ms end # hbrecv_interval returns the connection's heartbeat receive interval. def hbrecv_interval() return 0 unless @hbrecv_interval @hbrecv_interval / 1000.0 # ms end # hbsend_count returns the current connection's heartbeat send count. def hbsend_count() return 0 unless @hbsend_count @hbsend_count end # hbrecv_count returns the current connection's heartbeat receive count. def hbrecv_count() return 0 unless @hbrecv_count @hbrecv_count end # log call router def slog(name, *parms) return false unless @logger @logger.send(name, *parms) if @logger.respond_to?(:"#{name}") @logger.respond_to?(:"#{name}") end end # class end # module stomp-1.4.4/lib/stomp/errors.rb0000644000004100000410000002225113120662775016476 0ustar www-datawww-data# -*- encoding: utf-8 -*- module Stomp # Module level container for Stomp gem error classes. module Error # NoListenerGiven is raised if: # * No listener block is passed to Client#subscribe class NoListenerGiven < RuntimeError def message "No listener given" end end # DestinationRequired is raised if: # * No destination is passed to subscribe, unsubscribe, publish class DestinationRequired < RuntimeError def message "Destination required" end end # InvalidFormat is raised if: # * During frame parsing if a malformed frame is detected. class InvalidFormat < RuntimeError def message "Invalid message - invalid format" end end # InvalidServerCommand is raised if: # * An invalid STOMP COMMAND is received from the server. class InvalidServerCommand < RuntimeError def message "Invalid command from server" end end # InvalidMessageLength is raised if: # * An invalid message length is detected during a frame read. class InvalidMessageLength < RuntimeError def message "Invalid content length received" end end # PacketParsingTimeout is raised if: # * A frame read has started, but does not complete in 5 seconds. # * Use :parse_timeout connect parameter to override the timeout. class PacketParsingTimeout < RuntimeError def message "Packet parsing timeout" end end # ReceiveTimeout is raised if: # * The read socket shows "not ready" at any time after the timeout # specified by :parse_timeout in the connection hash. class ReceiveTimeout < RuntimeError def message "Receive status timeout" end end # SocketOpenTimeout is raised if: # * A timeout occurs during a socket open. class SocketOpenTimeout < RuntimeError def message "Socket open timeout" end end # NoCurrentConnection is raised if: # * Any method is called when a current connection does not exist. # * And @closed_check is true (the default). class NoCurrentConnection < RuntimeError def message "no current connection exists" end end # MaxReconnectAttempts is raised if: # * The maximum number of retries has been reached for a reliable connection. class MaxReconnectAttempts < RuntimeError def message "Maximum number of reconnection attempts reached" end end # DuplicateSubscription is raised if: # * A duplicate subscription ID is detected in the current session. class DuplicateSubscription < RuntimeError def message "duplicate subscriptions are disallowed" end end # ProtocolErrorEmptyHeaderKey is raised if: # * Any header key is empty ("") class ProtocolErrorEmptyHeaderKey < RuntimeError def message "Empty header key" end end # ProtocolErrorEmptyHeaderValue is raised if: # * Any header value is empty ("") *and* # * Connection protocol level == 1.0 class ProtocolErrorEmptyHeaderValue < RuntimeError def message "Empty header value, STOMP 1.0" end end # ProtocolError11p - base class of 1.1 CONNECT errors class ProtocolError11p < RuntimeError def message "STOMP 1.1+ CONNECT error" end end # ProtocolErrorConnect is raised if: # * Incomplete Stomp 1.1 headers are detected during a connect. class ProtocolErrorConnect < ProtocolError11p def message "STOMP 1.1+ CONNECT error, missing/incorrect CONNECT headers" end end # UnsupportedProtocolError is raised if: # * No supported Stomp protocol levels are detected during a connect. class UnsupportedProtocolError < ProtocolError11p def message "unsupported protocol level(s)" end end # InvalidHeartBeatHeaderError is raised if: # * A "heart-beat" header is present, but the values are malformed. class InvalidHeartBeatHeaderError < ProtocolError11p def message "heart-beat header value is malformed" end end # SubscriptionRequiredError is raised if: # * No subscription id is specified for a Stomp 1.1 subscribe. class SubscriptionRequiredError < RuntimeError def message "a valid subscription id header is required" end end # UTF8ValidationError is raised if: # * Stomp 1.1 headers are not valid UTF8. class UTF8ValidationError < RuntimeError def message "header is invalid UTF8" end end # MessageIDRequiredError is raised if: # * No messageid parameter is specified for ACK or NACK. class MessageIDRequiredError < RuntimeError def message "a valid message id is required for ACK/NACK" end end # SSLClientParamsError is raised if: # * Incomplete SSLParams are specified for an SSL connect. class SSLClientParamsError < RuntimeError def message "certificate and key files are both required" end end # StompServerError is raised if: # * Invalid (nil) data is received from the Stomp server. class StompServerError < RuntimeError def message "Connected, header read is nil, is this really a Stomp Server?" end end # SSLNoKeyFileError is raised if: # * A supplied key file does not exist. class SSLNoKeyFileError < RuntimeError def message "client key file does not exist" end end # SSLUnreadableKeyFileError is raised if: # * A supplied key file is not readable. class SSLUnreadableKeyFileError < RuntimeError def message "client key file can not be read" end end # SSLNoCertFileError is raised if: # * A supplied SSL cert file does not exist. class SSLNoCertFileError < RuntimeError def message "client cert file does not exist" end end # SSLUnreadableCertFileError is raised if: # * A supplied SSL cert file is not readable. class SSLUnreadableCertFileError < RuntimeError def message "client cert file can not be read" end end # SSLNoTruststoreFileError is raised if: # * A supplied SSL trust store file does not exist. class SSLNoTruststoreFileError < RuntimeError def message "a client truststore file does not exist" end end # SSLUnreadableTruststoreFileError is raised if: # * A supplied SSL trust store file is not readable. class SSLUnreadableTruststoreFileError < RuntimeError def message "a client truststore file can not be read" end end # LoggerConnectionError is not raised by the gem. It may be # raised by client logic in callback logger methods to signal # that a connection should not proceed. class LoggerConnectionError < RuntimeError end # NilMessageError is raised if: # * Invalid (nil) data is received from the Stomp server in a client's # listener thread, and the connection is not reliable. class NilMessageError < RuntimeError def message "Received message is nil, and connection not reliable" end end # MalformedFailoverOptionsError is raised if failover URL # options can not be parsed class MalformedFailoverOptionsError < RuntimeError def message "failover options are malformed" end end # ConnectReadTimeout is raised if: # * A read for CONNECTED/ERROR is untimely class ConnectReadTimeout < RuntimeError def message "Connect read for CONNECTED/ERROR timeout" end end class StompException < RuntimeError; end class BrokerException < StompException attr_reader :headers, :message, :receipt_id, :broker_backtrace def initialize(message) @message = message.headers.delete('message') @receipt_id = message.headers.delete('receipt-id') || 'no receipt id' @headers = message.headers @broker_backtrace = message.body end end class ProducerFlowControlException < BrokerException attr_reader :producer_id, :dest_name def initialize(message) super(message) msg_headers = /.*producer\s+\((.*)\).*to\s+prevent\s+flooding\s+([^\s]*)\.\s+/i.match(@message) @producer_id = msg_headers && msg_headers[1] @dest_name = msg_headers && msg_headers[2] end end class ProtocolException < BrokerException def initialize(message) super(message) end end class StartTimeoutException < StompException def initialize(timeout) @timeout = timeout end def message "Client failed to start in #{@timeout} seconds" end end class ReadReceiptTimeoutException < StompException def initialize(timeout) @timeout = timeout end def message "Read receipt not received after #{@timeout} seconds" end end # HandShakeDetectedError is raised if: # * A normal read detects inbound handskake data class HandShakeDetectedError < RuntimeError def message "Handshake data found, possible mismatched port and sslparams" end end end # module Error end # module Stomp stomp-1.4.4/lib/stomp/sslparams.rb0000644000004100000410000000534613120662775017175 0ustar www-datawww-data# -*- encoding: utf-8 -*- module Stomp # # == Purpose # # Parameters for STOMP ssl connections. # class SSLParams # The trust store files. Normally the certificate of the CA that signed # the server's certificate. One file name, or a CSV list of file names. attr_accessor :ts_files # The client certificate file. attr_accessor :cert_file # The client private key file. attr_accessor :key_file # The client private key password. attr_accessor :key_password # SSL Connect Verify Result. The result of the handshake. attr_accessor :verify_result # The certificate of the connection peer (the server), received during # the handshake. attr_accessor :peer_cert # Optional list of SSL ciphers to be used. In the format documented for # Ruby's OpenSSL. attr_accessor :ciphers # Absolute command to use Ruby default ciphers. attr_reader :use_ruby_ciphers # Back reference to the OpenSSL::SSL::SSLContext instance, gem sets before connect. attr_accessor :ctx # Set by the gem during connect, before the callbacks # Client wants file existance check on initialize. true/value or false/nil. attr_reader :fsck # # SSLContext options. attr_reader :ssl_ctxopts # # initialize returns a valid instance of SSLParams or raises an error. def initialize(opts={}) # Server authentication parameters @ts_files = opts[:ts_files] # A trust store file, normally a CA's cert # or a CSV list of cert file names # Client authentication parameters @cert_file = opts[:cert_file] # Client cert @key_file = opts[:key_file] # Client key @key_password = opts[:key_password] # Client key password # raise Stomp::Error::SSLClientParamsError if @cert_file.nil? && !@key_file.nil? raise Stomp::Error::SSLClientParamsError if !@cert_file.nil? && @key_file.nil? # @ciphers = opts[:ciphers] @use_ruby_ciphers = opts[:use_ruby_ciphers] ? opts[:use_ruby_ciphers] : false # if opts[:fsck] if @cert_file raise Stomp::Error::SSLNoCertFileError if !File::exists?(@cert_file) raise Stomp::Error::SSLUnreadableCertFileError if !File::readable?(@cert_file) end if @key_file raise Stomp::Error::SSLNoKeyFileError if !File::exists?(@key_file) raise Stomp::Error::SSLUnreadableKeyFileError if !File::readable?(@key_file) end if @ts_files tsa = @ts_files.split(",") tsa.each do |fn| raise Stomp::Error::SSLNoTruststoreFileError if !File::exists?(fn) raise Stomp::Error::SSLUnreadableTruststoreFileError if !File::readable?(fn) end end end # SSLContext options. See example: ssl_ctxoptions.rb. @ssl_ctxopts = opts[:ssl_ctxopts] # nil or options to set end end # of class SSLParams end # of module Stomp stomp-1.4.4/lib/stomp/client.rb0000644000004100000410000003156113120662775016444 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'thread' require 'digest/sha1' require 'timeout' require 'forwardable' module Stomp # Typical Stomp client class. Uses a listener thread to receive frames # from the server, any thread can send. # # Receives all happen in one thread, so consider not doing much processing # in that thread if you have much message volume. class Client extend Forwardable # Parameters hash attr_reader :parameters def_delegators :@connection, :login, :passcode, :port, :host, :ssl def_delegator :@parameters, :reliable # A new Client object can be initialized using three forms: # # Hash (this is the recommended Client initialization method): # # hash = { # :hosts => [ # {:login => "login1", :passcode => "passcode1", :host => "localhost", :port => 61616, :ssl => false}, # {:login => "login2", :passcode => "passcode2", :host => "remotehost", :port => 61617, :ssl => false} # ], # # These are the default parameters and do not need to be set # :reliable => true, # reliable (use failover) # :initial_reconnect_delay => 0.01, # initial delay before reconnect (secs) # :max_reconnect_delay => 30.0, # max delay before reconnect # :use_exponential_back_off => true, # increase delay between reconnect attpempts # :back_off_multiplier => 2, # next delay multiplier # :max_reconnect_attempts => 0, # retry forever, use # for maximum attempts # :randomize => false, # do not radomize hosts hash before reconnect # :connect_timeout => 0, # Timeout for TCP/TLS connects, use # for max seconds # :connect_headers => {}, # user supplied CONNECT headers (req'd for Stomp 1.1+) # :parse_timeout => 5, # IO::select wait time on socket reads # :logger => nil, # user suplied callback logger instance # :dmh => false, # do not support multihomed IPV4 / IPV6 hosts during failover # :closed_check => true, # check first if closed in each protocol method # :hbser => false, # raise on heartbeat send exception # :stompconn => false, # Use STOMP instead of CONNECT # :usecrlf => false, # Use CRLF command and header line ends (1.2+) # :max_hbread_fails => 0, # Max HB read fails before retry. 0 => never retry # :max_hbrlck_fails => 0, # Max HB read lock obtain fails before retry. 0 => never retry # :fast_hbs_adjust => 0.0, # Fast heartbeat senders sleep adjustment, seconds, needed ... # # For fast heartbeat senders. 'fast' == YMMV. If not # # correct for your environment, expect unnecessary fail overs # :connread_timeout => 0, # Timeout during CONNECT for read of CONNECTED/ERROR, secs # :tcp_nodelay => true, # Turns on the TCP_NODELAY socket option; disables Nagle's algorithm # :start_timeout => 0, # Timeout around Stomp::Client initialization # :sslctx_newparm => nil, # Param for SSLContext.new # :ssl_post_conn_check => true, # Further verify broker identity # } # # e.g. c = Stomp::Client.new(hash) # # Positional parameters: # login (String, default : '') # passcode (String, default : '') # host (String, default : 'localhost') # port (Integer, default : 61613) # reliable (Boolean, default : false) # # e.g. c = Stomp::Client.new('login', 'passcode', 'localhost', 61613, true) # # Stomp URL : # A Stomp URL must begin with 'stomp://' and can be in one of the following forms: # # stomp://host:port # stomp://host.domain.tld:port # stomp://login:passcode@host:port # stomp://login:passcode@host.domain.tld:port # # e.g. c = Stomp::Client.new(urlstring) # def initialize(login = '', passcode = '', host = 'localhost', port = 61613, reliable = false, autoflush = false) parse_hash_params(login) || parse_stomp_url(login) || parse_failover_url(login) || parse_positional_params(login, passcode, host, port, reliable) @logger = @parameters[:logger] ||= Stomp::NullLogger.new @start_timeout = @parameters[:start_timeout] || 0 @parameters[:client_main] = Thread::current ## p [ "CLINDBG", @parameters[:client_main] ] check_arguments!() # p [ "cldbg01", @parameters ] begin Timeout::timeout(@start_timeout) { create_error_handler create_connection(autoflush) start_listeners() } rescue TimeoutError # p [ "cldbg02" ] ex = Stomp::Error::StartTimeoutException.new(@start_timeout) raise ex end end def create_error_handler client_thread = Thread.current @error_listener = lambda do |error| exception = case error.body when /ResourceAllocationException/i Stomp::Error::ProducerFlowControlException.new(error) when /ProtocolException/i Stomp::Error::ProtocolException.new(error) else Stomp::Error::BrokerException.new(error) end @receipt_listeners.delete(error.headers['receipt-id']) if error.headers['receipt-id'] client_thread.raise exception end end def create_connection(autoflush) # p [ "ccon01", @parameters ] @connection = Connection.new(@parameters) @connection.autoflush = autoflush end private :create_connection # open is syntactic sugar for 'Client.new', see 'initialize' for usage. def self.open(login = '', passcode = '', host = 'localhost', port = 61613, reliable = false) Client.new(login, passcode, host, port, reliable) end # join the listener thread for this client, # generally used to wait for a quit signal. def join(limit = nil) @listener_thread.join(limit) end # Begin starts work in a a transaction by name. def begin(name, headers = {}) @connection.begin(name, headers) end # Abort aborts work in a transaction by name. def abort(name, headers = {}) @connection.abort(name, headers) # replay any ack'd messages in this transaction replay_list = @replay_messages_by_txn[name] if replay_list replay_list.each do |message| find_listener(message) # find_listener also calls the listener end end end # Commit commits work in a transaction by name. def commit(name, headers = {}) txn_id = headers[:transaction] @replay_messages_by_txn.delete(txn_id) @connection.commit(name, headers) end # Subscribe to a destination, must be passed a block # which will be used as a callback listener. # Accepts a transaction header ( :transaction => 'some_transaction_id' ). def subscribe(destination, headers = {}) raise Stomp::Error::NoListenerGiven unless block_given? headers = headers.symbolize_keys raise Stomp::Error::DestinationRequired unless destination # use subscription id to correlate messages to subscription. As described in # the SUBSCRIPTION section of the protocol: http://stomp.github.com/. # If no subscription id is provided, generate one. headers = headers.merge(:id => build_subscription_id(destination, headers)) if @listeners[headers[:id]] raise Stomp::Error::DuplicateSubscription end @listeners[headers[:id]] = lambda {|msg| yield msg} @connection.subscribe(destination, headers) end # Unsubscribe from a subscription by name. def unsubscribe(destination, headers = {}) headers = headers.symbolize_keys raise Stomp::Error::DestinationRequired unless destination headers = headers.merge(:id => build_subscription_id(destination, headers)) @connection.unsubscribe(destination, headers) @listeners[headers[:id]] = nil end # Acknowledge a message, used when a subscription has specified # client acknowledgement ( connection.subscribe("/queue/a",{:ack => 'client'}). # Accepts a transaction header ( :transaction => 'some_transaction_id' ). def ack(message, headers = {}) txn_id = headers[:transaction] if txn_id # lets keep around messages ack'd in this transaction in case we rollback replay_list = @replay_messages_by_txn[txn_id] if replay_list.nil? replay_list = [] @replay_messages_by_txn[txn_id] = replay_list end replay_list << message end if block_given? headers = headers.merge(:receipt => register_receipt_listener(lambda {|r| yield r})) end context = ack_context_for(message, headers) @connection.ack context[:message_id], context[:headers] end # For posterity, we alias: alias acknowledge ack # Stomp 1.1+ NACK. def nack(message, headers = {}) context = ack_context_for(message, headers) @connection.nack context[:message_id], context[:headers] end # def ack_context_for(message, headers) id = case protocol when Stomp::SPL_12 'ack' when Stomp::SPL_11 headers = headers.merge(:subscription => message.headers['subscription']) 'message-id' else 'message-id' end {:message_id => message.headers[id], :headers => headers} end # Unreceive a message, sending it back to its queue or to the DLQ. def unreceive(message, options = {}) @connection.unreceive(message, options) end # Publishes message to destination. # If a block is given a receipt will be requested and passed to the # block on receipt. # Accepts a transaction header ( :transaction => 'some_transaction_id' ). def publish(destination, message, headers = {}) headers = headers.symbolize_keys raise Stomp::Error::DestinationRequired unless destination if block_given? headers = headers.merge(:receipt => register_receipt_listener(lambda {|r| yield r})) end @connection.publish(destination, message, headers) end # Return the broker's CONNECTED frame to the client. Misnamed. def connection_frame() @connection.connection_frame end # Return any RECEIPT frame received by DISCONNECT. def disconnect_receipt() @connection.disconnect_receipt end # open? tests if this client connection is open. def open? @connection.open?() end # close? tests if this client connection is closed. def closed?() @connection.closed?() end # jruby? tests if the connection has detcted a JRuby environment def jruby?() @connection.jruby end # close frees resources in use by this client. The listener thread is # terminated, and disconnect on the connection is called. def close(headers={}) @listener_thread.exit @connection.disconnect(headers) end # running checks if the thread was created and is not dead. def running() @listener_thread && !!@listener_thread.status end # set_logger identifies a new callback logger. def set_logger(logger) @logger = logger @connection.set_logger(logger) end # protocol returns the current client's protocol level. def protocol() @connection.protocol() end # valid_utf8? validates any given string for UTF8 compliance. def valid_utf8?(s) @connection.valid_utf8?(s) end # sha1 returns a SHA1 sum of a given string. def sha1(data) @connection.sha1(data) end # uuid returns a type 4 UUID. def uuid() @connection.uuid() end # hbsend_interval returns the connection's heartbeat send interval. def hbsend_interval() @connection.hbsend_interval() end # hbrecv_interval returns the connection's heartbeat receive interval. def hbrecv_interval() @connection.hbrecv_interval() end # hbsend_count returns the current connection's heartbeat send count. def hbsend_count() @connection.hbsend_count() end # hbrecv_count returns the current connection's heartbeat receive count. def hbrecv_count() @connection.hbrecv_count() end # Poll for asynchronous messages issued by broker. # Return nil of no message available, else the message def poll() @connection.poll() end # autoflush= sets the current connection's autoflush setting. def autoflush=(af) @connection.autoflush = af end # autoflush returns the current connection's autoflush setting. def autoflush() @connection.autoflush() end end # Class end # Module stomp-1.4.4/lib/stomp/slogger.rb0000644000004100000410000001632413120662775016630 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'logger' # use the standard Ruby logger ..... # == Example STOMP call back logger class. # # Optional callback methods: # # * on_connecting: connection starting # * on_connected: successful connect # * on_connectfail: unsuccessful connect (will usually be retried) # * on_disconnect: successful disconnect # # * on_miscerr: on miscellaneous xmit/recv errors # # * on_publish: publish called # * on_subscribe: subscribe called # * on_unsubscribe: unsubscribe called # # * on_begin: begin called # * on_ack: ack called # * on_nack: nack called # * on_commit: commit called # * on_abort: abort called # # * on_receive: receive called and successful # # * on_ssl_connecting: SSL connection starting # * on_ssl_connected: successful SSL connect # * on_ssl_connectfail: unsuccessful SSL connect (will usually be retried) # # * on_hbread_fail: unsuccessful Heartbeat read # * on_hbwrite_fail: unsuccessful Heartbeat write # * on_hbfire: on any send or receive heartbeat # # All methods are optional, at the user's requirements. # # If a method is not provided, it is not called (of course.) # # IMPORTANT NOTE: in general, call back logging methods *SHOULD* not raise exceptions, # otherwise the underlying STOMP connection may fail in mysterious ways. # # There are two useful exceptions to this rule for: # # * on_connectfail # * on_ssl_connectfail # # These two methods can raise a Stomp::Errors::LoggerConnectionError. If this # exception is raised, it is passed up the chain to the caller. # # Callback parameters: are a copy of the @parameters instance variable for # the Stomp::Connection. # class Slogger < Stomp::NullLogger # Initialize a new callback logger instance. def initialize(init_parms = nil) _init @log.info("Logger initialization complete.") end def _init @log = Logger::new(STDOUT) # User preference @log.level = Logger::DEBUG # User preference end def marshal_dump [] end def marshal_load(array) _init end # Log connecting events def on_connecting(parms) begin @log.debug "Connecting: #{info(parms)}" rescue @log.debug "Connecting oops" end end # Log connected events def on_connected(parms) begin @log.debug "Connected: #{info(parms)}" rescue @log.debug "Connected oops" end end # Log connectfail events def on_connectfail(parms) begin @log.debug "Connect Fail #{info(parms)}" rescue @log.debug "Connect Fail oops" end =begin # An example LoggerConnectionError raise @log.debug "Connect Fail, will raise" raise Stomp::Error::LoggerConnectionError.new("quit from connect fail") =end end # Log disconnect events def on_disconnect(parms) begin @log.debug "Disconnected #{info(parms)}" rescue @log.debug "Disconnected oops" end end # Log miscellaneous errors def on_miscerr(parms, errstr) begin @log.debug "Miscellaneous Error #{info(parms)}" @log.debug "Miscellaneous Error String #{errstr}" rescue @log.debug "Miscellaneous Error oops" end end # Log Subscribe def on_subscribe(parms, headers) begin @log.debug "Subscribe Parms #{info(parms)}" @log.debug "Subscribe Headers #{headers}" rescue @log.debug "Subscribe oops" end end # Log UnSubscribe def on_unsubscribe(parms, headers) begin @log.debug "UnSubscribe Parms #{info(parms)}" @log.debug "UnSubscribe Headers #{headers}" rescue @log.debug "UnSubscribe oops" end end # Log Publish def on_publish(parms, message, headers) begin @log.debug "Publish Parms #{info(parms)}" @log.debug "Publish Message #{message}" @log.debug "Publish Headers #{headers}" rescue @log.debug "Publish oops" end end # Log Receive def on_receive(parms, result) begin @log.debug "Receive Parms #{info(parms)}" @log.debug "Receive Result #{result}" rescue @log.debug "Receive oops" end end # Log Begin def on_begin(parms, headers) begin @log.debug "Begin Parms #{info(parms)}" @log.debug "Begin Result #{headers}" rescue @log.debug "Begin oops" end end # Log Ack def on_ack(parms, headers) begin @log.debug "Ack Parms #{info(parms)}" @log.debug "Ack Result #{headers}" rescue @log.debug "Ack oops" end end # Log NAck def on_nack(parms, headers) begin @log.debug "NAck Parms #{info(parms)}" @log.debug "NAck Result #{headers}" rescue @log.debug "NAck oops" end end # Log Commit def on_commit(parms, headers) begin @log.debug "Commit Parms #{info(parms)}" @log.debug "Commit Result #{headers}" rescue @log.debug "Commit oops" end end # Log Abort def on_abort(parms, headers) begin @log.debug "Abort Parms #{info(parms)}" @log.debug "Abort Result #{headers}" rescue @log.debug "Abort oops" end end # Stomp 1.1+ - heart beat read (receive) failed. def on_hbread_fail(parms, ticker_data = {}) begin @log.debug "Hbreadf Parms #{info(parms)}" @log.debug "Hbreadf Result #{ticker_data.inspect}" rescue @log.debug "Hbreadf oops" end end # Stomp 1.1+ - heart beat send (transmit) failed. def on_hbwrite_fail(parms, ticker_data = {}) begin @log.debug "Hbwritef Parms #{info(parms)}" @log.debug "Hbwritef Result #{ticker_data.inspect}" rescue @log.debug "Hbwritef oops" end end # Log SSL connection start. def on_ssl_connecting(parms) begin @log.debug "SSL Connecting Parms #{info(parms)}" rescue @log.debug "SSL Connecting oops" end end # Log a successful SSL connect. def on_ssl_connected(parms) begin @log.debug "SSL Connected Parms #{info(parms)}" rescue @log.debug "SSL Connected oops" end end # Log an unsuccessful SSL connect. def on_ssl_connectfail(parms) begin @log.debug "SSL Connect Fail Parms #{info(parms)}" @log.debug "SSL Connect Fail Exception #{parms[:ssl_exception]}, #{parms[:ssl_exception].message}" rescue @log.debug "SSL Connect Fail oops" end =begin # An example LoggerConnectionError raise @log.debug "SSL Connect Fail, will raise" raise Stomp::Error::LoggerConnectionError.new("quit from SSL connect") =end end # Log heart beat fires def on_hbfire(parms, srind, firedata = {}) begin @log.debug "HeartBeat Fire Parms #{info(parms)}" @log.debug "HeartBeat Fire Send/Receive #{srind}" rescue @log.debug "HeartBeat Fire oops" end end private # Example information extract. def info(parms) # # Available in the parms Hash: # parms[:cur_host] # parms[:cur_port] # parms[:cur_login] # parms[:cur_passcode] # parms[:cur_ssl] # parms[:cur_recondelay] # parms[:cur_parseto] # parms[:cur_conattempts] # parms[:openstat] # # For the on_ssl_connectfail callback these are also available: # parms[:ssl_exception] # "Host: #{parms[:cur_host]}, Port: #{parms[:cur_port]}, Login: #{parms[:cur_login]}, Passcode: #{parms[:cur_passcode]}, ssl: #{parms[:cur_ssl]}" end end # of class stomp-1.4.4/lib/stomp/version.rb0000644000004100000410000000033513120662775016646 0ustar www-datawww-data# -*- encoding: utf-8 -*- module Stomp # Define the gem version. module Version #:nodoc: all MAJOR = 1 MINOR = 4 PATCH = 4 # PATCH = "4.plvl.001" STRING = "#{MAJOR}.#{MINOR}.#{PATCH}" end end stomp-1.4.4/lib/stomp/message.rb0000644000004100000410000001007113120662775016603 0ustar www-datawww-data# -*- encoding: utf-8 -*- module Stomp # Message is a container class for frames. Misnamed technically. class Message public # The COMMAND value. attr_accessor :command # The Headers Hash. attr_accessor :headers # The message Body. attr_accessor :body # The original input(s). attr_accessor :original # Commands that are allowed from the wire per the specifications. @@allowed_commands = [ Stomp::CMD_CONNECTED, Stomp::CMD_MESSAGE, Stomp::CMD_RECEIPT, Stomp::CMD_ERROR ] # initialize returns a Message from a raw physical frame. def initialize(frame, protocol11p = false) # p [ "00", frame, frame.encoding ] # Set default empty values self.command = '' self.headers = {} self.body = '' self.original = frame return self if is_blank?(frame) # Figure out where individual parts of the frame begin and end. command_index = frame.index("\n") raise Stomp::Error::InvalidFormat, 'command index' unless command_index # headers_index = frame.index("\n\n", command_index+1) raise Stomp::Error::InvalidFormat, 'headers index' unless headers_index # lastnull_index = frame.rindex("\0") raise Stomp::Error::InvalidFormat, 'lastnull index' unless lastnull_index # Extract working copies of each frame part work_command = frame[0..command_index-1] raise Stomp::Error::InvalidServerCommand, "invalid command: #{work_command.inspect}" unless @@allowed_commands.include?(work_command) # work_headers = frame[command_index+1..headers_index-1] raise Stomp::Error::InvalidFormat, 'nil headers' unless work_headers # work_body = frame[headers_index+2..lastnull_index-1] raise Stomp::Error::InvalidFormat, 'nil body' unless work_body # Set the frame values if protocol11p work_command.force_encoding(Stomp::UTF8) if work_command.respond_to?(:force_encoding) end self.command = work_command work_headers.split("\n").map do |value| fc = value.index(":") raise Stomp::Error::InvalidFormat, 'parsed header value' unless fc # pk = value[0...fc] pv = value[fc+1..-1] # if protocol11p pk.force_encoding(Stomp::UTF8) if pk.respond_to?(:force_encoding) pv.force_encoding(Stomp::UTF8) if pv.respond_to?(:force_encoding) # Stomp 1.1+ - Servers may put multiple values for a single key on the wire. # If so, we support reading those, and passing them to the user. if self.headers[pk] if self.headers[pk].is_a?(Array) # The 3rd and any subsequent ones for this key self.headers[pk] << pv else # The 2nd one for this key tv = self.headers[pk] + "" self.headers[pk] = [] self.headers[pk] << tv << pv end else self.headers[pk] = pv # The 1st one for this key end else # Stomp 1.0 self.headers[pk.strip] = pv.strip unless self.headers[pk.strip] # Only receive the 1st one end end raise Stomp::Error::ProtocolErrorEmptyHeaderKey if self.headers.has_key?("") raise Stomp::Error::ProtocolErrorEmptyHeaderValue if (!protocol11p) && self.headers.has_value?("") body_length = -1 if self.headers['content-length'] body_length = self.headers['content-length'].to_i raise Stomp::Error::InvalidMessageLength if work_body.length != body_length end self.body = work_body[0..body_length] end # to_s returns a string prepresentation of this Message. def to_s "" end # is_blank? tests if a data value is nil or empty. def is_blank?(value) value.nil? || (value.respond_to?(:empty?) && value.empty?) end # empty? tests if a Message has any blank parts. def empty? is_blank?(command) && is_blank?(headers) && is_blank?(body) end end # class Message end # module Stomp stomp-1.4.4/lib/stomp/constants.rb0000644000004100000410000001446613120662775017207 0ustar www-datawww-data# -*- encoding: utf-8 -*- module Stomp # Client generated frames CMD_CONNECT = "CONNECT" CMD_STOMP = "STOMP" CMD_DISCONNECT = "DISCONNECT" CMD_SEND = "SEND" CMD_SUBSCRIBE = "SUBSCRIBE" CMD_UNSUBSCRIBE = "UNSUBSCRIBE" CMD_ACK = "ACK" CMD_NACK = "NACK" CMD_BEGIN = "BEGIN" CMD_COMMIT = "COMMIT" CMD_ABORT = "ABORT" # Server generated names CMD_CONNECTED = "CONNECTED" CMD_MESSAGE = "MESSAGE" CMD_RECEIPT = "RECEIPT" CMD_ERROR = "ERROR" # Protocols SPL_10 = "1.0" SPL_11 = "1.1" SPL_12 = "1.2" # Stomp 1.0 and 1.1 and 1.2 SUPPORTED = [SPL_10, SPL_11, SPL_12] # UTF-8 Encoding Name UTF8 = "UTF-8" # # Octet 0 # NULL = "\0" # # New line # NL = "\n" NL_ASCII = 0x0a # # Line Feed (New Line) # LF = "\n" LF_ASCII = 0x0a # # New line # CR = "\r" CR_ASCII = 0x0d # # Back Slash # BACK_SLASH = "\\" BACK_SLASH_ASCII = 0x5c # # Literal colon # LITERAL_COLON = ":" COLON_ASCII = 0x3a # # Literal letter c # LITERAL_C = "c" C_ASCII = 0x63 # # Literal letter n # LITERAL_N = "n" N_ASCII = 0x6e # # Codec from/to values. # ENCODE_VALUES = [ "\\\\\\\\", "\\", # encoded, decoded "\\" + "n", "\n", "\\" + "r", "\r", "\\c", ":", ] # DECODE_VALUES = [ "\\\\", "\\", # encoded, decoded "\\" + "n", "\n", "\\" + "r", "\r", "\\c", ":", ] # A fairly safe and generally supported ciphers list. DEFAULT_CIPHERS = [ ["AES128-GCM-SHA256","TLSv1/SSLv3",128,128], ["AES128-SHA256","TLSv1/SSLv3",128,128], ["AES128-SHA","TLSv1/SSLv3",128,128], ["AES256-GCM-SHA384","TLSv1/SSLv3",256,256], ["AES256-SHA256","TLSv1/SSLv3",256,256], ["AES256-SHA","TLSv1/SSLv3",256,256], ["CAMELLIA128-SHA","TLSv1/SSLv3",128,128], ["CAMELLIA256-SHA","TLSv1/SSLv3",256,256], ["DES-CBC3-SHA","TLSv1/SSLv3",168,168], ["DES-CBC-SHA","TLSv1/SSLv3",56,56], ["DHE-DSS-AES128-GCM-SHA256","TLSv1/SSLv3",128,128], ["DHE-DSS-AES128-SHA256","TLSv1/SSLv3",128,128], ["DHE-DSS-AES128-SHA","TLSv1/SSLv3",128,128], ["DHE-DSS-AES256-GCM-SHA384","TLSv1/SSLv3",256,256], ["DHE-DSS-AES256-SHA256","TLSv1/SSLv3",256,256], ["DHE-DSS-AES256-SHA","TLSv1/SSLv3",256,256], ["DHE-DSS-CAMELLIA128-SHA","TLSv1/SSLv3",128,128], ["DHE-DSS-CAMELLIA256-SHA","TLSv1/SSLv3",256,256], ["DHE-DSS-SEED-SHA","TLSv1/SSLv3",128,128], ["DHE-RSA-AES128-GCM-SHA256","TLSv1/SSLv3",128,128], ["DHE-RSA-AES128-SHA256","TLSv1/SSLv3",128,128], ["DHE-RSA-AES128-SHA","TLSv1/SSLv3",128,128], ["DHE-RSA-AES256-GCM-SHA384","TLSv1/SSLv3",256,256], ["DHE-RSA-AES256-SHA256","TLSv1/SSLv3",256,256], ["DHE-RSA-AES256-SHA","TLSv1/SSLv3",256,256], ["DHE-RSA-CAMELLIA128-SHA","TLSv1/SSLv3",128,128], ["DHE-RSA-CAMELLIA256-SHA","TLSv1/SSLv3",256,256], ["DHE-RSA-SEED-SHA","TLSv1/SSLv3",128,128], ["ECDH-ECDSA-AES128-GCM-SHA256","TLSv1/SSLv3",128,128], ["ECDH-ECDSA-AES128-SHA256","TLSv1/SSLv3",128,128], ["ECDH-ECDSA-AES128-SHA","TLSv1/SSLv3",128,128], ["ECDH-ECDSA-AES256-GCM-SHA384","TLSv1/SSLv3",256,256], ["ECDH-ECDSA-AES256-SHA384","TLSv1/SSLv3",256,256], ["ECDH-ECDSA-AES256-SHA","TLSv1/SSLv3",256,256], ["ECDH-ECDSA-DES-CBC3-SHA","TLSv1/SSLv3",168,168], ["ECDH-ECDSA-RC4-SHA","TLSv1/SSLv3",128,128], ["ECDHE-ECDSA-AES128-GCM-SHA256","TLSv1/SSLv3",128,128], ["ECDHE-ECDSA-AES128-SHA256","TLSv1/SSLv3",128,128], ["ECDHE-ECDSA-AES128-SHA","TLSv1/SSLv3",128,128], ["ECDHE-ECDSA-AES256-GCM-SHA384","TLSv1/SSLv3",256,256], ["ECDHE-ECDSA-AES256-SHA384","TLSv1/SSLv3",256,256], ["ECDHE-ECDSA-AES256-SHA","TLSv1/SSLv3",256,256], ["ECDHE-ECDSA-DES-CBC3-SHA","TLSv1/SSLv3",168,168], ["ECDHE-ECDSA-RC4-SHA","TLSv1/SSLv3",128,128], ["ECDHE-RSA-AES128-GCM-SHA256","TLSv1/SSLv3",128,128], ["ECDHE-RSA-AES128-SHA256","TLSv1/SSLv3",128,128], ["ECDHE-RSA-AES128-SHA","TLSv1/SSLv3",128,128], ["ECDHE-RSA-AES256-GCM-SHA384","TLSv1/SSLv3",256,256], ["ECDHE-RSA-AES256-SHA384","TLSv1/SSLv3",256,256], ["ECDHE-RSA-AES256-SHA","TLSv1/SSLv3",256,256], ["ECDHE-RSA-DES-CBC3-SHA","TLSv1/SSLv3",168,168], ["ECDHE-RSA-RC4-SHA","TLSv1/SSLv3",128,128], ["ECDH-RSA-AES128-GCM-SHA256","TLSv1/SSLv3",128,128], ["ECDH-RSA-AES128-SHA256","TLSv1/SSLv3",128,128], ["ECDH-RSA-AES128-SHA","TLSv1/SSLv3",128,128], ["ECDH-RSA-AES256-GCM-SHA384","TLSv1/SSLv3",256,256], ["ECDH-RSA-AES256-SHA384","TLSv1/SSLv3",256,256], ["ECDH-RSA-AES256-SHA","TLSv1/SSLv3",256,256], ["ECDH-RSA-DES-CBC3-SHA","TLSv1/SSLv3",168,168], ["ECDH-RSA-RC4-SHA","TLSv1/SSLv3",128,128], ["EDH-DSS-DES-CBC3-SHA","TLSv1/SSLv3",168,168], ["EDH-DSS-DES-CBC-SHA", "TLSv1/SSLv3", 56, 56], ["EDH-DSS-DES-CBC-SHA","TLSv1/SSLv3",56,56], ["EDH-RSA-DES-CBC3-SHA","TLSv1/SSLv3",168,168], ["EDH-RSA-DES-CBC-SHA","TLSv1/SSLv3",56,56], ["EXP-DES-CBC-SHA","TLSv1/SSLv3",40,56], ["EXP-EDH-DSS-DES-CBC-SHA","TLSv1/SSLv3",40,56], ["EXP-EDH-RSA-DES-CBC-SHA","TLSv1/SSLv3",40,56], ["EXP-RC2-CBC-MD5","TLSv1/SSLv3",40,128], ["EXP-RC4-MD5", "TLSv1/SSLv3", 40, 128], ["PSK-3DES-EDE-CBC-SHA","TLSv1/SSLv3",168,168], ["PSK-AES128-CBC-SHA","TLSv1/SSLv3",128,128], ["PSK-AES256-CBC-SHA","TLSv1/SSLv3",256,256], ["PSK-RC4-SHA","TLSv1/SSLv3",128,128], ["RC4-MD5","TLSv1/SSLv3",128,128], ["RC4-SHA","TLSv1/SSLv3",128,128], ["SEED-SHA","TLSv1/SSLv3",128,128], ["SRP-DSS-3DES-EDE-CBC-SHA","TLSv1/SSLv3",168,168], ["SRP-DSS-AES-128-CBC-SHA","TLSv1/SSLv3",128,128], ["SRP-DSS-AES-256-CBC-SHA","TLSv1/SSLv3",256,256], ["SRP-RSA-3DES-EDE-CBC-SHA","TLSv1/SSLv3",168,168], ["SRP-RSA-AES-128-CBC-SHA","TLSv1/SSLv3",128,128], ["SRP-RSA-AES-256-CBC-SHA","TLSv1/SSLv3",256,256], ] HAND_SHAKE_DATA = "\x15\x03\x03\x00\x02\x02\n" original_verbose, $VERBOSE = $VERBOSE, nil # try to shut off warnings # stomp URL regex pattern, for e.g. login:passcode@host:port or host:port URL_REPAT = '((([\w~!@#$%^&*()\-+=.?:<>,.]*\w):([\w~!@#$%^&*()\-+=.?:<>,.]*))?@)?([\w\.\-]+):(\d+)' # Failover URL regex, for e.g. #failover:(stomp+ssl://login1:passcode1@remotehost1:61612,stomp://login2:passcode2@remotehost2:61613) FAILOVER_REGEX = /^failover:(\/\/)?\(stomp(\+ssl)?:\/\/#{URL_REPAT}(,stomp(\+ssl)?:\/\/#{URL_REPAT})*\)(\?(.*))?$/ $VERBOSE = original_verbose end # Module Stomp stomp-1.4.4/lib/stomp/ext/0000755000004100000410000000000013120662775015433 5ustar www-datawww-datastomp-1.4.4/lib/stomp/ext/hash.rb0000644000004100000410000000134313120662775016704 0ustar www-datawww-data# -*- encoding: utf-8 -*- class ::Hash # Returns self with keys uncamelized and converted to symbols. def uncamelize_and_symbolize_keys self.uncamelize_and_stringify_keys.symbolize_keys end # Returns self with keys uncamelized and converted to strings. def uncamelize_and_stringify_keys uncamelized = {} self.each_pair do |key, value| new_key = key.to_s.split(/(?=[A-Z])/).join('_').downcase uncamelized[new_key] = value end uncamelized end # Returns self with all keys symbolized. def symbolize_keys symbolized = {} self.each_pair do |key, value| symbolized[key.to_sym] = value end symbolized end unless self.method_defined?(:symbolize_keys) end # class Hash stomp-1.4.4/lib/stomp/null_logger.rb0000644000004100000410000000177213120662775017500 0ustar www-datawww-data# -*- encoding: utf-8 -*- module Stomp class NullLogger def on_miscerr(parms, error_msg) $stderr.print "\non_miscerr\n" $stderr.print parms.inspect $stderr.print "\n" $stderr.print error_msg $stderr.print "\n" end def on_connecting(parms); end def on_connected(parms); end def on_connectfail(parms); end def on_disconnect(parms); end def on_subscribe(parms, headers); end def on_unsubscribe(parms, headers); end def on_publish(parms, message, headers); end def on_receive(parms, result); end def on_begin(parms, headers); end def on_ack(parms, headers); end def on_nack(parms, headers); end def on_commit(parms, headers); end def on_abort(parms, headers); end def on_hbread_fail(parms, ticker_data); end def on_hbwrite_fail(parms, ticker_data); end def on_ssl_connecting(parms); end def on_ssl_connected(parms); end def on_ssl_connectfail(parms); end def on_hbfire(parms, srind, curt); end end end stomp-1.4.4/lib/connection/0000755000004100000410000000000013120662775015630 5ustar www-datawww-datastomp-1.4.4/lib/connection/utils.rb0000644000004100000410000002234313120662775017321 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'socket' require 'timeout' require 'io/wait' require 'digest/sha1' module Stomp class Connection private # Support multi-homed servers. def _expand_hosts(hash) new_hash = hash.clone new_hash[:hosts_cloned] = hash[:hosts].clone new_hash[:hosts] = [] # hash[:hosts].each do |host_parms| ai = Socket.getaddrinfo(host_parms[:host], nil, nil, Socket::SOCK_STREAM) next if ai.nil? || ai.size == 0 info6 = ai.detect {|info| info[4] == Socket::AF_INET6} info4 = ai.detect {|info| info[4] == Socket::AF_INET} if info6 new_hostp = host_parms.clone new_hostp[:host] = info6[3] new_hash[:hosts] << new_hostp end if info4 new_hostp = host_parms.clone new_hostp[:host] = info4[3] new_hash[:hosts] << new_hostp end end return new_hash end # Handle 1.9+ character representation. def parse_char(char) RUBY_VERSION > '1.9' ? char : char.chr end # Create parameters for any callback logger. def log_params() lparms = @parameters.clone if @parameters lparms = {} unless lparms lparms[:cur_host] = @host lparms[:cur_port] = @port lparms[:cur_login] = @login lparms[:cur_passcode] = @passcode lparms[:cur_ssl] = @ssl lparms[:cur_recondelay] = @reconnect_delay lparms[:cur_parseto] = @parse_timeout lparms[:cur_conattempts] = @connection_attempts lparms[:cur_failure] = @failure # To assist in debugging lparms[:openstat] = open? # lparms end # _pre_connect handles low level logic just prior to a physical connect. def _pre_connect() @connect_headers = @connect_headers.symbolize_keys raise Stomp::Error::ProtocolErrorConnect if (@connect_headers[:"accept-version"] && !@connect_headers[:host]) raise Stomp::Error::ProtocolErrorConnect if (!@connect_headers[:"accept-version"] && @connect_headers[:host]) return unless (@connect_headers[:"accept-version"] && @connect_headers[:host]) # 1.0 # Try 1.1 or greater @hhas10 = false okvers = [] avers = @connect_headers[:"accept-version"].split(",") avers.each do |nver| if Stomp::SUPPORTED.index(nver) okvers << nver @hhas10 = true if nver == Stomp::SPL_10 end end raise Stomp::Error::UnsupportedProtocolError if okvers == [] @connect_headers[:"accept-version"] = okvers.join(",") # This goes to server # Heartbeats - pre connect return unless @connect_headers[:"heart-beat"] _validate_hbheader() end # _post_connect handles low level logic just after a physical connect. def _post_connect() return unless (@connect_headers[:"accept-version"] && @connect_headers[:host]) # 1.0 if @connection_frame.command == Stomp::CMD_ERROR @connection_frame.headers = _decodeHeaders(@connection_frame.headers) return end # We are CONNECTed cfh = @connection_frame.headers.symbolize_keys @protocol = cfh[:version] if @protocol # Should not happen, but check anyway raise Stomp::Error::UnsupportedProtocolError unless Stomp::SUPPORTED.index(@protocol) else # CONNECTed to a 1.0 server that does not return *any* 1.1 type headers @protocol = Stomp::SPL_10 # reset return end # Heartbeats return unless @connect_headers[:"heart-beat"] _init_heartbeats() end # socket creates and returns a new socket for use by the connection. def socket() @socket_semaphore.synchronize do used_socket = @socket used_socket = nil if closed? while used_socket.nil? || !@failure.nil? @failure = nil begin used_socket = open_socket() # sets @closed = false if OK # Open is complete connect(used_socket) slog(:on_connected, log_params) @connection_attempts = 0 rescue @failure = $! used_socket = nil @closed = true raise unless @reliable raise if @failure.is_a?(Stomp::Error::LoggerConnectionError) # Catch errors which are: # a) emitted from corrupted 1.1+ 'connect' (caller programming error) # b) should never be retried raise if @failure.is_a?(Stomp::Error::ProtocolError11p) begin unless slog(:on_connectfail,log_params) $stderr.print "connect to #{@host} failed: #{$!} will retry(##{@connection_attempts}) in #{@reconnect_delay}\n" end rescue Exception => aex raise if aex.is_a?(Stomp::Error::LoggerConnectionError) end if max_reconnect_attempts? $stderr.print "In socket() Reached MaxReconnectAttempts" ### _dump_threads() mt = @parameters[:client_main] if !mt.nil? mt.raise Stomp::Error::MaxReconnectAttempts Thread::exit end raise Stomp::Error::MaxReconnectAttempts end sleep(@reconnect_delay) @connection_attempts += 1 if @parameters change_host() increase_reconnect_delay() end end end @socket = used_socket end end # refine_params sets up defaults for a Hash initialize. def refine_params(params) params = params.uncamelize_and_symbolize_keys default_params = { :connect_headers => {}, :reliable => true, # Failover parameters :initial_reconnect_delay => 0.01, :max_reconnect_delay => 30.0, :use_exponential_back_off => true, :back_off_multiplier => 2, :max_reconnect_attempts => 0, :randomize => false, :connect_timeout => 0, # Parse Timeout :parse_timeout => 5, :dmh => false, # Closed check logic :closed_check => true, :hbser => false, :stompconn => false, :max_hbread_fails => 0, :max_hbrlck_fails => 0, :fast_hbs_adjust => 0.0, :connread_timeout => 0, :tcp_nodelay => true, :start_timeout => 0, :sslctx_newparm => nil, :ssl_post_conn_check => true, } res_params = default_params.merge(params) if res_params[:dmh] res_params = _expand_hosts(res_params) end return res_params end # change_host selects the next host for retries. def change_host @parameters[:hosts] = @parameters[:hosts].sort_by { rand } if @parameters[:randomize] # Set first as master and send it to the end of array current_host = @parameters[:hosts].shift @parameters[:hosts] << current_host @ssl = current_host[:ssl] @host = current_host[:host] @port = current_host[:port] || Connection::default_port(@ssl) @login = current_host[:login] || "" @passcode = current_host[:passcode] || "" end # Duplicate parameters hash def _hdup(h) ldup = {} ldup.merge!(h) ldup[:hosts] = [] hvals = h[:hosts].nil? ? h["hosts"] : h[:hosts] hvals.each do |hv| ldup[:hosts] << hv.dup end ldup end # max_reconnect_attempts? returns nil or the number of maximum reconnect # attempts. def max_reconnect_attempts? !(@parameters.nil? || @parameters[:max_reconnect_attempts].nil?) && @parameters[:max_reconnect_attempts] != 0 && @connection_attempts >= @parameters[:max_reconnect_attempts] end # increase_reconnect_delay increases the reconnect delay for the next connection # attempt. def increase_reconnect_delay @reconnect_delay *= @parameters[:back_off_multiplier] if @parameters[:use_exponential_back_off] @reconnect_delay = @parameters[:max_reconnect_delay] if @reconnect_delay > @parameters[:max_reconnect_delay] @reconnect_delay end # __old_receive receives a frame, blocks until the frame is received. def __old_receive() # The receive may fail so we may need to retry. while true begin used_socket = socket() return _receive(used_socket) rescue Stomp::Error::MaxReconnectAttempts unless slog(:on_miscerr, log_params, "Reached MaxReconnectAttempts") $stderr.print "Reached MaxReconnectAttempts\n" end raise rescue @failure = $! raise unless @reliable errstr = "receive failed: #{$!}" unless slog(:on_miscerr, log_params, "es1_oldrecv: " + errstr) $stderr.print "\non_miscerr\n" $stderr.print log_params.inspect $stderr.print "\n" $stderr.print "es2_oldrecv: " + errstr $stderr.print "\n" end # !!! This initiates a re-connect !!! _reconn_prep() end end end # _reconn_prep prepares for a reconnect retry def _reconn_prep() close_socket() if @parameters change_host() end @st.kill if @st # Kill ticker thread if any @rt.kill if @rt # Kill ticker thread if any @socket = nil end end # class Connection end # module Stomp stomp-1.4.4/lib/connection/netio.rb0000644000004100000410000004717213120662775017306 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'socket' require 'timeout' require 'io/wait' require 'digest/sha1' module Stomp class Connection def _interruptible_gets(read_socket) # The gets thread may be interrupted by the heartbeat thread. Ensure that # if so interrupted, a new gets cannot start until after the heartbeat # thread finishes its work. This is PURELY to avoid a segfault bug # involving OpenSSL::Buffer. @gets_semaphore.synchronize { @getst = Thread.current } read_socket.gets ensure @gets_semaphore.synchronize { @getst = nil } end private # Really read from the wire. def _receive(read_socket, connread = false) # p [ "ioscheck", @iosto, connread ] # _dump_callstack() # drdbg = true drdbg = false @read_semaphore.synchronize do line = nil if connread begin Timeout::timeout(@connread_timeout, Stomp::Error::ConnectReadTimeout) do line = _init_line_read(read_socket) end rescue Stomp::Error::ConnectReadTimeout => ex if @reliable _reconn_prep() end raise ex end else p [ "CONR01" ] if drdbg _dump_callstack() if drdbg line = _init_line_read(read_socket) end # p [ "nilcheck", line.nil? ] if drdbg return nil if line.nil? #An extra \n at the beginning of the frame, possibly not caught by is_ready? line = '' if line == "\n" if line == HAND_SHAKE_DATA raise Stomp::Error::HandShakeDetectedError end p [ "wiredatain_01A", line, Time.now ] if drdbg line = _normalize_line_end(line) if @protocol >= Stomp::SPL_12 p [ "wiredatain_01B", line, Time.now ] if drdbg # Reads the beginning of the message until it runs into a empty line message_header = '' begin message_header += line p [ "wiredatain_02A", line, Time.now ] if drdbg unless connread || @ssl raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto) end p [ "wiredatain_02B", line, Time.now ] if drdbg line = _interruptible_gets(read_socket) p [ "wiredatain_02C", line ] if drdbg raise if line.nil? line = _normalize_line_end(line) if @protocol >= Stomp::SPL_12 p [ "wiredatain_02D", line ] if drdbg end until line =~ /^\s?\n$/ p [ "wiredatain_03A" ] if drdbg # Checks if it includes content_length header content_length = message_header.match(/content-length\s?:\s?(\d+)\s?\n/) message_body = '' p [ "wiredatain_03B", content_length ] if drdbg # If content_length is present, read the specified amount of bytes if content_length unless connread || @ssl raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto) end p [ "CL01" ] if drdbg message_body = read_socket.read content_length[1].to_i unless connread || @ssl raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto) end raise Stomp::Error::InvalidMessageLength unless parse_char(read_socket.getc) == "\0" # Else read the rest of the message until the first \0 else unless connread || @ssl raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto) end p [ "NOCL01" ] if drdbg message_body = read_socket.readline("\0") message_body.chop! end p [ "wiredatain_04" ] if drdbg # If the buffer isn't empty, reads trailing new lines. # # Note: experiments with JRuby seem to show that socket.ready? never # returns true. It appears that in cases where Ruby returns true # that JRuby returns a Fixnum. We attempt to adjust for this # in the _is_ready? method. # # Note 2: the draining of new lines must be done _after_ a message # is read. Do _not_ leave them on the wire and attempt to drain them # at the start of the next read. Attempting to do that breaks the # asynchronous nature of the 'poll' method. p [ "wiredatain_05_prep", "isr", _is_ready?(read_socket) ] if drdbg while _is_ready?(read_socket) unless connread || @ssl raise Stomp::Error::ReceiveTimeout unless IO.select([read_socket], nil, nil, @iosto) end p [ "WHIR01" ] if drdbg last_char = read_socket.getc break unless last_char if parse_char(last_char) != "\n" read_socket.ungetc(last_char) break end end p [ "wiredatain_05A" ] if drdbg if @protocol >= Stomp::SPL_11 @lr = Time.now.to_f if @hbr end # Adds the excluded \n and \0 and tries to create a new message with it p [ "wiredatain_05B" ] if drdbg msg = Message.new(message_header + "\n" + message_body + "\0", @protocol >= Stomp::SPL_11) p [ "wiredatain_06", msg.command, msg.headers ] if drdbg # if @protocol >= Stomp::SPL_11 && msg.command != Stomp::CMD_CONNECTED msg.headers = _decodeHeaders(msg.headers) end p [ "wiredatain_99", msg.command, msg.headers ] if drdbg msg end end # # This is a total hack, to try and guess how JRuby will behave today. # def _is_ready?(s) rdy = s.ready? ### p [ "isr?", rdy ] return rdy unless @jruby ### p [ "jrdychk", rdy.class ] if rdy.class == NilClass # rdy = true rdy = false # A test else rdy = (rdy.class == Fixnum || rdy.class == TrueClass) ? true : false end ### p [ "isr?_last", rdy ] rdy end # Normalize line ends because 1.2+ brokers can send 'mixed mode' headers, i.e.: # - Some headers end with '\n' # - Other headers end with '\r\n' def _normalize_line_end(line) return line unless @usecrlf # p [ "nleln", line ] line_len = line.respond_to?(:bytesize) ? line.bytesize : line.length last2 = line[line_len-2...line_len] # p [ "nlel2", last2 ] return line unless last2 == "\r\n" return line[0...line_len-2] + "\n" end # transmit logically puts a Message on the wire. def transmit(command, headers = {}, body = '') # The transmit may fail so we may need to retry. while true begin used_socket = socket() _transmit(used_socket, command, headers, body) return rescue Stomp::Error::MaxReconnectAttempts => e _ = e raise rescue @failure = $! raise unless @reliable errstr = "transmit to #{@host} failed: #{$!}\n" unless slog(:on_miscerr, log_params, "es_trans: " + errstr) $stderr.print errstr end # !!! This loop initiates a re-connect !!! _reconn_prep() end end end # _transmit is the real wire write logic. def _transmit(used_socket, command, headers = {}, body = '') # p [ "wirewrite" ] # _dump_callstack() if @protocol >= Stomp::SPL_11 && command != Stomp::CMD_CONNECT headers = _encodeHeaders(headers) end @transmit_semaphore.synchronize do # Handle nil body body = '' if body.nil? # The content-length should be expressed in bytes. # Ruby 1.8: String#length => # of bytes; Ruby 1.9: String#length => # of characters # With Unicode strings, # of bytes != # of characters. So, use String#bytesize when available. body_length_bytes = body.respond_to?(:bytesize) ? body.bytesize : body.length # ActiveMQ interprets every message as a BinaryMessage # if content_length header is included. # Using :suppress_content_length => true will suppress this behaviour # and ActiveMQ will interpret the message as a TextMessage. # For more information refer to http://juretta.com/log/2009/05/24/activemq-jms-stomp/ # Lets send this header in the message, so it can maintain state when using unreceive headers[:'content-length'] = "#{body_length_bytes}" unless headers[:suppress_content_length] headers[:'content-type'] = "text/plain; charset=UTF-8" unless headers[:'content-type'] || headers[:suppress_content_type] _wire_write(used_socket,command) headers.each do |k,v| if v.is_a?(Array) v.each do |e| _wire_write(used_socket,"#{k}:#{e}") end else _wire_write(used_socket,"#{k}:#{v}") end end _wire_write(used_socket,"") if body != '' if headers[:suppress_content_length] if tz = body.index("\00") used_socket.write body[0..tz-1] else used_socket.write body end else used_socket.write body end end used_socket.write "\0" used_socket.flush if autoflush if @protocol >= Stomp::SPL_11 @ls = Time.now.to_f if @hbs end end end # Use CRLF if protocol is >= 1.2, and the client requested CRLF def _wire_write(sock, data) # p [ "debug_01", @protocol, @usecrlf ] if @protocol >= Stomp::SPL_12 && @usecrlf wiredata = "#{data}#{Stomp::CR}#{Stomp::LF}" # p [ "wiredataout_01:", wiredata ] sock.write(wiredata) else # p [ "wiredataout_02:", "#{data}\n" ] sock.puts data end end # open_tcp_socket opens a TCP socket. def open_tcp_socket() ## $stderr.print("h: #{@host}, p: #{@port}\n") tcp_socket = nil slog(:on_connecting, log_params) Timeout::timeout(@connect_timeout, Stomp::Error::SocketOpenTimeout) do tcp_socket = TCPSocket.open(@host, @port) end tcp_socket end # open_ssl_socket opens an SSL socket. def open_ssl_socket() require 'openssl' unless defined?(OpenSSL) begin # Any raised SSL exceptions ctx = @sslctx_newparm ? OpenSSL::SSL::SSLContext.new(@sslctx_newparm) : OpenSSL::SSL::SSLContext.new ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # Assume for now # # Note: if a client uses :ssl => true this would result in the gem using # the _default_ Ruby ciphers list. This is _known_ to fail in later # Ruby releases. The gem now detects :ssl => true, and replaces that # with: # * :ssl => Stomp::SSLParams.new # # The above results in the use of Stomp default parameters. # # To specifically request Stomp default parameters, use: # * :ssl => Stomp::SSLParams.new(..., :ciphers => Stomp::DEFAULT_CIPHERS) # # If connecting with an SSLParams instance, and the _default_ Ruby # ciphers list is actually required, use: # * :ssl => Stomp::SSLParams.new(..., :use_ruby_ciphers => true) # # If a custom ciphers list is required, connect with: # * :ssl => Stomp::SSLParams.new(..., :ciphers => custom_ciphers_list) # if @ssl != true # # Here @ssl is: # * an instance of Stomp::SSLParams # Control would not be here if @ssl == false or @ssl.nil?. # # Back reference the SSLContext @ssl.ctx = ctx # Server authentication parameters if required if @ssl.ts_files ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER truststores = OpenSSL::X509::Store.new fl = @ssl.ts_files.split(",") fl.each do |fn| # Add next cert file listed raise Stomp::Error::SSLNoTruststoreFileError if !File::exists?(fn) raise Stomp::Error::SSLUnreadableTruststoreFileError if !File::readable?(fn) truststores.add_file(fn) end ctx.cert_store = truststores end # Client authentication parameters. # Both cert file and key file must be present or not, it can not be a mix. raise Stomp::Error::SSLClientParamsError if @ssl.cert_file.nil? && !@ssl.key_file.nil? raise Stomp::Error::SSLClientParamsError if !@ssl.cert_file.nil? && @ssl.key_file.nil? if @ssl.cert_file # Any check will do here raise Stomp::Error::SSLNoCertFileError if !File::exists?(@ssl.cert_file) raise Stomp::Error::SSLUnreadableCertFileError if !File::readable?(@ssl.cert_file) ctx.cert = OpenSSL::X509::Certificate.new(File.read(@ssl.cert_file)) raise Stomp::Error::SSLNoKeyFileError if !File::exists?(@ssl.key_file) raise Stomp::Error::SSLUnreadableKeyFileError if !File::readable?(@ssl.key_file) ctx.key = OpenSSL::PKey::RSA.new(File.read(@ssl.key_file), @ssl.key_password) end # Cipher list # As of this writing, there are numerous problems with supplying # cipher lists to jruby. So we do not attempt to do that here. if !@ssl.use_ruby_ciphers # No Ruby ciphers (the default) if @ssl.ciphers # User ciphers list? ctx.ciphers = @ssl.ciphers # Accept user supplied ciphers else ctx.ciphers = Stomp::DEFAULT_CIPHERS # Just use Stomp defaults end end unless @jruby # Set SSLContext Options if user asks for it in Stomp::SSLParams # and SSL supports it. if @ssl.ssl_ctxopts && ctx.respond_to?(:options=) ctx.options = @ssl.ssl_ctxopts end end # ssl = nil slog(:on_ssl_connecting, log_params) # _dump_ctx(ctx) Timeout::timeout(@connect_timeout, Stomp::Error::SocketOpenTimeout) do tcp_socket = TCPSocket.open(@host, @port) ssl = OpenSSL::SSL::SSLSocket.new(tcp_socket, ctx) ssl.hostname = @host if ssl.respond_to? :hostname= ssl.sync_close = true # Sync ssl close with underlying TCP socket ssl.connect if (ssl.context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && @ssl_post_conn_check ssl.post_connection_check(@host) end end def ssl.ready? ! @rbuffer.empty? || @io.ready? end if @ssl != true # Pass back results if possible if RUBY_VERSION =~ /1\.8\.[56]/ @ssl.verify_result = "N/A for Ruby #{RUBY_VERSION}" else @ssl.verify_result = ssl.verify_result end @ssl.peer_cert = ssl.peer_cert end slog(:on_ssl_connected, log_params) ssl rescue Exception => ex lp = log_params.clone lp[:ssl_exception] = ex slog(:on_ssl_connectfail, lp) if ssl # shut down the TCP socket - we just failed to do the SSL handshake in time ssl.close end # puts ex.backtrace $stdout.flush raise # Reraise end end # close_socket closes the current open socket, and hence the connection. def close_socket() begin # Need to set @closed = true before closing the socket # within the @read_semaphore thread @closed = true @read_semaphore.synchronize do @socket.close end rescue #Ignoring if already closed end @closed end # open_socket opens a TCP or SSL soclet as required. def open_socket() used_socket = @ssl ? open_ssl_socket : open_tcp_socket # try to close the old connection if any close_socket @closed = false if @parameters # nil in some rspec tests unless @reconnect_delay @reconnect_delay = @parameters[:initial_reconnect_delay] || iosto1 end end # Use keepalive used_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) # TCP_NODELAY option (disables Nagle's algorithm) used_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, !!(@parameters && @parameters[:tcp_nodelay])) @iosto = @parse_timeout ? @parse_timeout.to_f : 0.0 used_socket end # connect performs a basic STOMP CONNECT operation. def connect(used_socket) @connect_headers = {} unless @connect_headers # Caller said nil/false headers = @connect_headers.clone headers[:login] = @login unless @login.to_s.empty? headers[:passcode] = @passcode unless @login.to_s.empty? _pre_connect if !@hhas10 && @stompconn _transmit(used_socket, Stomp::CMD_STOMP, headers) else _transmit(used_socket, Stomp::CMD_CONNECT, headers) end @connection_frame = _receive(used_socket, true) _post_connect @disconnect_receipt = nil @session = @connection_frame.headers["session"] if @connection_frame # replay any subscriptions. @subscriptions.each {|k,v| _transmit(used_socket, Stomp::CMD_SUBSCRIBE, v) } end def _init_line_read(read_socket) line = '' if @protocol == Stomp::SPL_10 || (@protocol >= Stomp::SPL_11 && !@hbr) if @jruby # Handle JRuby specific behavior. ### p [ "ilrjr00", _is_ready?(read_socket), RUBY_VERSION ] if RUBY_VERSION < "2" while true ### p [ "ilrjr01A1", _is_ready?(read_socket) ] line = _interruptible_gets(read_socket) # Data from wire break unless line == "\n" line = '' end else # RUBY_VERSION >= "2" while _is_ready?(read_socket) ### p [ "ilrjr01B2", _is_ready?(read_socket) ] line = _interruptible_gets(read_socket) # Data from wire break unless line == "\n" line = '' end end else line = _interruptible_gets(read_socket) # The old way end else # We are >= 1.1 *AND* receiving heartbeats. while true line = _interruptible_gets(read_socket) # Data from wire break unless line == "\n" line = '' @lr = Time.now.to_f end end line end # Used for debugging def _dump_ctx(ctx) p [ "dc01", ctx.inspect ] p [ "dc02ciphers", ctx.ciphers ] end # used for debugging def _dump_callstack() i = 0 caller.each do |c| p [ "csn", i, c ] i += 1 end end # _dump_callstack # used for debugging def _dump_threads() tl = Thread::list tl.each do |at| p [ "THDMPN", at ] end p [ "THDMPMain", @parameters[:client_main] ] end end # class Connection end # module Stomp stomp-1.4.4/lib/connection/utf8.rb0000644000004100000410000002105013120662775017041 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'socket' require 'timeout' require 'io/wait' require 'digest/sha1' module Stomp class Connection private # Ref: # http://unicode.org/mail-arch/unicode-ml/y2003-m02/att-0467/01-The_Algorithm_to_Valide_an_UTF-8_String # # *CONSIDER* replacing this with a dependency on the utf8_validator gem. # This code has been copied from there. # def _valid_utf8?(string) case RUBY_VERSION when /1\.8\.[56]/ bytes = [] 0.upto(string.length-1) {|i| bytes << string[i] } else bytes = string.bytes end # valid = true index = -1 nb_hex = nil ni_hex = nil state = "start" next_byte_save = nil # bytes.each do |next_byte| index += 1 next_byte_save = next_byte ni_hex = sprintf "%x", index nb_hex = sprintf "%x", next_byte # puts "Top: #{next_byte}(0x#{nb_hex}), index: #{index}(0x#{ni_hex})" if DEBUG case state # State: 'start' # The 'start' state: # * handles all occurrences of valid single byte characters i.e., the ASCII character set # * provides state transition logic for start bytes of valid characters with 2-4 bytes # * signals a validation failure for all other single bytes # when "start" # puts "state: start" if DEBUG case next_byte # ASCII # * Input = 0x00-0x7F : change state to START when (0x00..0x7f) # puts "state: start 1" if DEBUG state = "start" # Start byte of two byte characters # * Input = 0xC2-0xDF: change state to A when (0xc2..0xdf) # puts "state: start 2" if DEBUG state = "a" # Start byte of some three byte characters # * Input = 0xE1-0xEC, 0xEE-0xEF: change state to B when (0xe1..0xec) # puts "state: start 3" if DEBUG state = "b" when (0xee..0xef) # puts "state: start 4" if DEBUG state = "b" # Start byte of special three byte characters # * Input = 0xE0: change state to C when 0xe0 # puts "state: start 5" if DEBUG state = "c" # Start byte of the remaining three byte characters # * Input = 0xED: change state to D when 0xed # puts "state: start 6" if DEBUG state = "d" # Start byte of some four byte characters # * Input = 0xF1-0xF3:change state to E when (0xf1..0xf3) # puts "state: start 7" if DEBUG state = "e" # Start byte of special four byte characters # * Input = 0xF0: change state to F when 0xf0 # puts "state: start 8" if DEBUG state = "f" # Start byte of very special four byte characters # * Input = 0xF4: change state to G when 0xf4 # puts "state: start 9" if DEBUG state = "g" # All other single characters are invalid # * Input = Others (0x80-0xBF,0xC0-0xC1, 0xF5-0xFF): ERROR else valid = false break end # of the inner case, the 'start' state # The last continuation byte of a 2, 3, or 4 byte character # State: 'a' # o Input = 0x80-0xBF: change state to START # o Others: ERROR when "a" # puts "state: a" if DEBUG if (0x80..0xbf) === next_byte state = "start" else valid = false break end # The first continuation byte for most 3 byte characters # (those with start bytes in: 0xe1-0xec or 0xee-0xef) # State: 'b' # o Input = 0x80-0xBF: change state to A # o Others: ERROR when "b" # puts "state: b" if DEBUG if (0x80..0xbf) === next_byte state = "a" else valid = false break end # The first continuation byte for some special 3 byte characters # (those with start byte 0xe0) # State: 'c' # o Input = 0xA0-0xBF: change state to A # o Others: ERROR when "c" # puts "state: c" if DEBUG if (0xa0..0xbf) === next_byte state = "a" else valid = false break end # The first continuation byte for the remaining 3 byte characters # (those with start byte 0xed) # State: 'd' # o Input = 0x80-0x9F: change state to A # o Others: ERROR when "d" # puts "state: d" if DEBUG if (0x80..0x9f) === next_byte state = "a" else valid = false break end # The first continuation byte for some 4 byte characters # (those with start bytes in: 0xf1-0xf3) # State: 'e' # o Input = 0x80-0xBF: change state to B # o Others: ERROR when "e" # puts "state: e" if DEBUG if (0x80..0xbf) === next_byte state = "b" else valid = false break end # The first continuation byte for some special 4 byte characters # (those with start byte 0xf0) # State: 'f' # o Input = 0x90-0xBF: change state to B # o Others: ERROR when "f" # puts "state: f" if DEBUG if (0x90..0xbf) === next_byte state = "b" else valid = false break end # The first continuation byte for the remaining 4 byte characters # (those with start byte 0xf4) # State: 'g' # o Input = 0x80-0x8F: change state to B # o Others: ERROR when "g" # puts "state: g" if DEBUG if (0x80..0x8f) === next_byte state = "b" else valid = false break end # else raise RuntimeError, "state: default" end end # # puts "State at end: #{state}" if DEBUG # Catch truncation at end of string if valid and state != 'start' # puts "Resetting valid value" if DEBUG valid = false end # valid end # of _valid_utf8? # Stomp 1.1+ header check for UTF8 validity. Raises Stomp::Error::UTF8ValidationError if header data is not valid UTF8. def _headerCheck(h) return if @protocol == Stomp::SPL_10 # Do nothing for this environment # h.each_pair do |k,v| # Keys here are symbolized ks = k.to_s ks.force_encoding(Stomp::UTF8) if ks.respond_to?(:force_encoding) raise Stomp::Error::UTF8ValidationError unless valid_utf8?(ks) # if v.is_a?(Array) v.each do |e| e.force_encoding(Stomp::UTF8) if e.respond_to?(:force_encoding) raise Stomp::Error::UTF8ValidationError unless valid_utf8?(e) end else vs = v.to_s + "" # Values are usually Strings, but could be TrueClass or Symbol # The + "" above forces an 'unfreeze' if necessary vs.force_encoding(Stomp::UTF8) if vs.respond_to?(:force_encoding) raise Stomp::Error::UTF8ValidationError unless valid_utf8?(vs) end end end # encode returns a Hash of encoded headers per the Stomp 1.1 specification. def _encodeHeaders(h) eh = {} h.each_pair do |k,v| # Keys are symbolized ks = k.to_s if v.is_a?(Array) kenc = Stomp::HeaderCodec::encode(ks) eh[kenc] = [] v.each do |e| eh[kenc] << Stomp::HeaderCodec::encode(e) end else vs = v.to_s eh[Stomp::HeaderCodec::encode(ks).to_sym] = Stomp::HeaderCodec::encode(vs) end end eh end # decode returns a Hash of decoded headers per the Stomp 1.1 specification. def _decodeHeaders(h) dh = {} h.each_pair do |k,v| # Keys here are NOT! symbolized if v.is_a?(Array) kdec = Stomp::HeaderCodec::decode(k) dh[kdec] = [] v.each do |e| dh[kdec] << Stomp::HeaderCodec::decode(e) end else vs = v.to_s dh[Stomp::HeaderCodec::decode(k)] = Stomp::HeaderCodec::decode(vs) end end dh end end # class Connection end # module Stomp stomp-1.4.4/lib/connection/heartbeats.rb0000644000004100000410000002316113120662775020302 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'socket' require 'timeout' require 'io/wait' require 'digest/sha1' module Stomp class Connection private def _validate_hbheader() return if @connect_headers[:"heart-beat"] == "0,0" # Caller does not want heartbeats. OK. parts = @connect_headers[:"heart-beat"].split(",") if (parts.size != 2) || (parts[0] != parts[0].to_i.to_s) || (parts[1] != parts[1].to_i.to_s) raise Stomp::Error::InvalidHeartBeatHeaderError end end def _init_heartbeats() return if @connect_headers[:"heart-beat"] == "0,0" # Caller does not want heartbeats. OK. # Init. # @cx = @cy = @sx = @sy = 0 # Variable names as in spec # @hbsend_interval = @hbrecv_interval = 0.0 # Send/Receive ticker interval. # @hbsend_count = @hbrecv_count = 0 # Send/Receive ticker counts. # @ls = @lr = -1.0 # Last send/receive time (from Time.now.to_f) # @st = @rt = nil # Send/receive ticker thread # Handle current client / server capabilities. # cfh = @connection_frame.headers.symbolize_keys return if cfh[:"heart-beat"] == "0,0" # Server does not want heartbeats # Conect header parts. parts = @connect_headers[:"heart-beat"].split(",") @cx = parts[0].to_i @cy = parts[1].to_i # Connected frame header parts. parts = cfh[:"heart-beat"].split(",") @sx = parts[0].to_i @sy = parts[1].to_i # Catch odd situations like server has used => heart-beat:000,00000 return if (@cx == 0 && @cy == 0) || (@sx == 0 && @sy == 0) # See if we are doing anything at all. @hbs = @hbr = true # Sending/Receiving heartbeats. Assume yes at first. # Check if sending is possible. @hbs = false if @cx == 0 || @sy == 0 # Reset if neither side wants # Check if receiving is possible. @hbr = false if @sx == 0 || @cy == 0 # Reset if neither side wants # Check if we should not do heartbeats at all return if (!@hbs && !@hbr) # If sending if @hbs sm = @cx >= @sy ? @cx : @sy # ticker interval, ms @hbsend_interval = 1000.0 * sm # ticker interval, μs @ls = Time.now.to_f # best guess at start _start_send_ticker() end # If receiving if @hbr rm = @sx >= @cy ? @sx : @cy # ticker interval, ms @hbrecv_interval = 1000.0 * rm # ticker interval, μs @lr = Time.now.to_f # best guess at start _start_receive_ticker() end end # _start_send_ticker starts a thread to send heartbeats when required. def _start_send_ticker() sleeptime = @hbsend_interval / 1000000.0 # Sleep time secs reconn = false adjust = 0.0 @st = Thread.new { first_time = true while true do # slt = sleeptime - adjust - @fast_hbs_adjust sleep(slt) next unless @socket # nil under some circumstances ?? curt = Time.now.to_f slog(:on_hbfire, log_params, "send_fire", :curt => curt, :last_sleep => slt) delta = curt - @ls # Be tolerant (minus), and always do this the first time through. # Reintroduce logic removed in d922fa. compval = (@hbsend_interval - (@hbsend_interval/5.0)) / 1000000.0 if delta > compval || first_time first_time = false slog(:on_hbfire, log_params, "send_heartbeat", {:last_sleep => slt, :curt => curt, :last_send => @ls, :delta => delta, :compval => compval}) # Send a heartbeat @transmit_semaphore.synchronize do begin @socket.puts @socket.flush # Do not buffer heartbeats @ls = Time.now.to_f # Update last send @hb_sent = true # Reset if necessary @hbsend_count += 1 rescue Exception => sendex @hb_sent = false # Set the warning flag slog(:on_hbwrite_fail, log_params, {"ticker_interval" => sleeptime, "exception" => sendex}) if @hbser raise # Re-raise if user requested this, otherwise ignore end if @reliable reconn = true break # exit the synchronize do end end end # of the synchronize if reconn # Attempt a fail over reconnect. This is 'safe' here because # this thread no longer holds the @transmit_semaphore lock. @rt.kill if @rt # Kill the receiver thread if one exists _reconn_prep_hb() # Drive reconnection logic Thread.exit # This sender thread is done end end adjust = Time.now.to_f - curt Thread.pass end } end # _start_receive_ticker starts a thread that receives heartbeats when required. def _start_receive_ticker() sleeptime = @hbrecv_interval / 1000000.0 # Sleep time secs read_fail_count = 0 lock_fail_count = 0 fail_hard = false @rt = Thread.new { # while true do sleep sleeptime next unless @socket # nil under some circumstances ?? rdrdy = _is_ready?(@socket) curt = Time.now.to_f slog(:on_hbfire, log_params, "receive_fire", :curt => curt) # begin delta = curt - @lr if delta > sleeptime slog(:on_hbfire, log_params, "receive_heartbeat", {}) # Client code could be off doing something else (that is, no reading of # the socket has been requested by the caller). Try to handle that case. lock = @read_semaphore.try_lock if lock lock_fail_count = 0 # clear rdrdy = _is_ready?(@socket) if rdrdy read_fail_count = 0 # clear last_char = @socket.getc if last_char.nil? # EOF from broker? fail_hard = true else @lr = Time.now.to_f plc = parse_char(last_char) if plc == "\n" # Server Heartbeat @hbrecv_count += 1 @hb_received = true # Reset if necessary else @socket.ungetc(last_char) end end @read_semaphore.unlock # Release read lock else # Socket is not ready @read_semaphore.unlock # Release read lock @hb_received = false read_fail_count += 1 slog(:on_hbread_fail, log_params, {"ticker_interval" => sleeptime, "read_fail_count" => read_fail_count, "lock_fail" => false, "lock_fail_count" => lock_fail_count, "fail_point" => "not_ready"}) end else # try_lock failed # Shrug. Could not get lock. Client must be actually be reading. @hb_received = false # But notify caller if possible lock_fail_count += 1 slog(:on_hbread_fail, log_params, {"ticker_interval" => sleeptime, "read_fail_count" => read_fail_count, "lock_fail" => true, "lock_fail_count" => lock_fail_count, "fail_point" => "try_lock_fail"}) end # of the try_lock else # delta <= sleeptime @hb_received = true # Reset if necessary read_fail_count = 0 # reset lock_fail_count = 0 # reset end # of the if delta > sleeptime rescue Exception => recvex slog(:on_hbread_fail, log_params, {"ticker_interval" => sleeptime, "exception" => recvex, "read_fail_count" => read_fail_count, "lock_fail_count" => lock_fail_count, "fail_point" => "receive_exception"}) fail_hard = true end # Do we want to attempt a retry? if @reliable # Retry on hard fail or max read fails if fail_hard || (@max_hbread_fails > 0 && read_fail_count >= @max_hbread_fails) # This is an attempt at a connection retry. @st.kill if @st # Kill the sender thread if one exists _reconn_prep_hb() # Drive reconnection logic Thread.exit # This receiver thread is done end # Retry on max lock fails. Different logic in order to avoid a deadlock. if (@max_hbrlck_fails > 0 && lock_fail_count >= @max_hbrlck_fails) # This is an attempt at a connection retry. @gets_semaphore.synchronize do @getst.raise(Errno::EBADF.new) if @getst # kill the socket reading thread if exists @socket.close rescue nil # Attempt a forced close end @st.kill if @st # Kill the sender thread if one exists Thread.exit # This receiver thread is done end end Thread.pass # Prior to next receive loop # end # of the "while true" } # end of the Thread.new end # _reconn_prep_hb prepares for a reconnect retry def _reconn_prep_hb() if @parameters change_host() end @socket = nil used_socket = socket() return used_socket end end # class Connection end # module Stomp stomp-1.4.4/test/0000755000004100000410000000000013120662775013702 5ustar www-datawww-datastomp-1.4.4/test/test_urlogin.rb0000644000004100000410000000675513120662775016762 0ustar www-datawww-data# -*- encoding: utf-8 -*- if Kernel.respond_to?(:require_relative) require_relative("test_helper") else $:.unshift(File.dirname(__FILE__)) require 'test_helper' end =begin Main class for testing Stomp::Client URL based Logins. =end class TestURLLogins < Test::Unit::TestCase include TestBase def setup hostname = host() portnum = port() sslpn = ssl_port() @tdstomp = [ "stomp://guestl:guestp@#{hostname}:#{portnum}", "stomp://#{hostname}:#{portnum}", "stomp://@#{hostname}:#{portnum}", "stomp://f@#$$%^&*()_+=o.o:@#{hostname}:#{portnum}", 'stomp://f@#$$%^&*()_+=o.o::b~!@#$%^&*()+-_=?:<>,.@@' + hostname + ":#{portnum}", ] @tdfailover = [ "failover://(stomp://#{hostname}:#{portnum})", "failover://(stomp://#{hostname}:#{portnum})", "failover://(stomp://#{hostname}:#{portnum})?whatup=doc&coyote=kaboom", "failover://(stomp://#{hostname}:#{portnum})?whatup=doc", "failover://(stomp://#{hostname}:#{portnum})?whatup=doc&coyote=kaboom&randomize=true", 'failover://(stomp://f@#$$%^&*()_+=o.o::b~!@#$%^&*()+-_=?:<>,.@@' + "#{hostname}" + ":#{portnum}" + ")", 'failover://(stomp://f@#$$%^&*()_+=o.o::b~!@#$%^&*()+-_=:<>,.@@' + "#{hostname}" + ":#{portnum}" + ")", 'failover://(stomp://f@#$$%^&*()_+=o.o::b~!@#$%^&*()+-_=?:<>,.@@' + "#{hostname}" + ":#{portnum}" + ")?a=b", 'failover://(stomp://f@#$$%^&*()_+=o.o::b~!@#$%^&*()+-_=:<>,.@@' + "#{hostname}" + ":#{portnum}" + ")?c=d&e=f", "failover://(stomp://usera:passa@#{hostname}:#{portnum})", "failover://(stomp://usera:@#{hostname}:#{portnum})", "failover://(stomp://#{hostname}:#{portnum},stomp://#{hostname}:#{portnum})", "failover://(stomp://usera:passa@#{hostname}:#{portnum},stomp://#{hostname}:#{portnum})", "failover://(stomp://usera:@#{hostname}:#{portnum},stomp://#{hostname}:#{portnum})", "failover://(stomp://#{hostname}:#{portnum},stomp://#{hostname}:#{portnum})?a=b&c=d", "failover://(stomp://#{hostname}:#{portnum},stomp://#{hostname}:#{portnum})?a=b&c=d&connect_timeout=2020", ] @sslfailover = [ "failover://(stomp+ssl://#{hostname}:#{sslpn})", "failover://(stomp+ssl://usera:passa@#{hostname}:#{sslpn})", "failover://(stomp://usera:@#{hostname}:#{portnum},stomp+ssl://#{hostname}:#{sslpn})", ] @badparms = "failover://(stomp://#{hostname}:#{portnum})?a=b&noequal" @client = nil end def teardown @client.close if @client && @client.open? # allow tests to close end # test stomp:// URLs def test_0010_stomp_urls() @tdstomp.each_with_index do |url, ndx| c = Stomp::Client.new(url) assert !c.nil?, url assert c.open?, url c.close end end # test failover:// urls - tcp def test_0020_failover_urls_tcp() @tdfailover.each_with_index do |url, ndx| # p [ "xurl", url, "xndx", ndx ] c = Stomp::Client.new(url) assert !c.nil?, url assert c.open?, url c.close end end # test failover:// urls - ssl def test_0020_failover_urls_ssl() @sslfailover.each_with_index do |url, ndx| # p [ "sslxurl", url, "sslxndx", ndx ] c = Stomp::Client.new(url) assert !c.nil?, url assert c.open?, url c.close end end if ENV['STOMP_TESTSSL'] # test failover:// with bad parameters def test_0020_failover_badparms() assert_raise(Stomp::Error::MalformedFailoverOptionsError) { _ = Stomp::Client.new(@badparms) } end end unless ENV['STOMP_RABBIT'] stomp-1.4.4/test/unitst.sh0000755000004100000410000000076713120662775015601 0ustar www-datawww-data#!/bin/bash # eval $DeBug # ------------------------------------------------------------------------------ cmd_base=$(dirname $0) source $cmd_base/funcs.sh # ------------------------------------------------------------------------------ pushd $cmd_base RUN=${RUBY:-ruby} slist=$(ls -1 *.rb | grep -v helper | grep -v 1method) for rbf in $slist do echo "${RUN} -I ../lib ${rbf}" $RUN -I ../lib $rbf done # ------------------------------------------------------------------------------ popd set +x exit stomp-1.4.4/test/MultiBrokerUnitTestExample.sh0000755000004100000410000000474713120662775021530 0ustar www-datawww-data#!/bin/sh # set -x # # An example of running unit tests against multiple message brokers. # pref="STOMP_PORT=61613 rake test --trace" echo =============== AMQ Stomp 1.0 =============== eval $pref pref="STOMP_PORT=61613 STOMP_TEST11p=1.1 STOMP_AMQ11=y STOMP_NOWILD=y rake test --trace" echo =============== AMQ Stomp 1.1 =============== eval $pref pref="STOMP_PORT=61613 STOMP_TEST11p=1.2 STOMP_AMQ11=y STOMP_NOWILD=y rake test --trace" echo =============== AMQ Stomp 1.2 =============== eval $pref pref="STOMP_TESTSSL=y STOMP_PORT=62613 STOMP_SSLPORT=62614 STOMP_DOTQUEUE=y STOMP_NOWILD=y rake test --trace" echo =============== Apollo Stomp 1.0 =============== eval $pref pref="STOMP_TESTSSL=y STOMP_PORT=62613 STOMP_SSLPORT=62614 STOMP_DOTQUEUE=y STOMP_TEST11p=1.1 STOMP_NOWILD=y STOMP_APOLLO=y rake test --trace" echo =============== Apollo Stomp 1.1 =============== eval $pref pref="STOMP_TESTSSL=y STOMP_PORT=62613 STOMP_SSLPORT=62614 STOMP_DOTQUEUE=y STOMP_TEST11p=1.2 STOMP_NOWILD=y STOMP_APOLLO=y rake test --trace" echo =============== Apollo Stomp 1.2 =============== eval $pref pref="STOMP_TESTSSL=y STOMP_PORT=62613 STOMP_SSLPORT=62614 STOMP_DOTQUEUE=y STOMP_TEST11p=1.2 STOMP_NOWILD=y STOMP_CRLF=y STOMP_APOLLO=y rake test --trace" echo =============== Apollo Stomp 1.2 - CRLF=y =============== eval $pref pref="STOMP_TESTSSL=y STOMP_PORT=62613 STOMP_SSLPORT=62614 STOMP_DOTQUEUE=y STOMP_TEST11p=1.2 STOMP_NOWILD=y STOMP_CONN=y STOMP_APOLLO=y rake test --trace" echo =============== Apollo Stomp 1.2 - CONN=y =============== eval $pref pref="STOMP_RABBIT=y STOMP_PORT=41613 STOMP_DOTQUEUE=y STOMP_NOWILD=y rake test --trace" echo =============== RabbitMQ Stomp 1.0 =============== eval $pref pref="STOMP_RABBIT=y STOMP_PORT=41613 STOMP_DOTQUEUE=y STOMP_NOWILD=y STOMP_TEST11p=1.1 rake test --trace" echo =============== RabbitMQ Stomp 1.1 =============== eval $pref pref="STOMP_RABBIT=y STOMP_PORT=41613 STOMP_DOTQUEUE=y STOMP_NOWILD=y STOMP_TEST11p=1.2 rake test --trace" echo =============== RabbitMQ Stomp 1.2 =============== eval $pref pref="STOMP_ARTEMIS=y STOMP_PORT=31613 STOMP_NOWILD=y rake test --trace" echo =============== Artemis Stomp 1.0 =============== eval $pref pref="STOMP_ARTEMIS=y STOMP_PORT=31613 STOMP_NOWILD=y STOMP_TEST11p=1.1 rake test --trace" echo =============== Artemis Stomp 1.1 =============== eval $pref pref="STOMP_ARTEMIS=y STOMP_PORT=31613 STOMP_NOWILD=y STOMP_TEST11p=1.2 rake test --trace" echo =============== Artemis Stomp 1.2 =============== eval $pref set +x exit 0 stomp-1.4.4/test/test_ssl.rb0000644000004100000410000000360213120662775016070 0ustar www-datawww-data# -*- encoding: utf-8 -*- if Kernel.respond_to?(:require_relative) require_relative("test_helper") else $:.unshift(File.dirname(__FILE__)) require 'test_helper' end =begin Main class for testing Stomp::SSLParams. =end class TestSSL < Test::Unit::TestCase include TestBase def setup @conn = get_ssl_connection() end def teardown @conn.disconnect if @conn && @conn.open? # allow tests to disconnect end # def test_ssl_0000 assert @conn.open? end # Test SSLParams basic. def test_ssl_0010_parms ssl_params = Stomp::SSLParams.new assert ssl_params.ts_files.nil? assert ssl_params.cert_file.nil? assert ssl_params.key_file.nil? assert ssl_params.fsck.nil? end # Test using correct parameters. def test_ssl_0020_noraise _ = Stomp::SSLParams.new(:cert_file => "dummy1", :key_file => "dummy2") _ = Stomp::SSLParams.new(:ts_files => "dummyts1") _ = Stomp::SSLParams.new(:ts_files => "dummyts1", :cert_file => "dummy1", :key_file => "dummy2") end # Test using incorrect / incomplete parameters. def test_ssl_0030_raise assert_raise(Stomp::Error::SSLClientParamsError) { _ = Stomp::SSLParams.new(:cert_file => "dummy1") } assert_raise(Stomp::Error::SSLClientParamsError) { _ = Stomp::SSLParams.new(:key_file => "dummy2") } end # Test that :fsck works. def test_ssl_0040_fsck assert_raise(Stomp::Error::SSLNoCertFileError) { _ = Stomp::SSLParams.new(:cert_file => "dummy1", :key_file => "dummy2", :fsck => true) } assert_raise(Stomp::Error::SSLNoKeyFileError) { _ = Stomp::SSLParams.new(:cert_file => __FILE__, :key_file => "dummy2", :fsck => true) } assert_raise(Stomp::Error::SSLNoTruststoreFileError) { _ = Stomp::SSLParams.new(:ts_files => "/tmp/not-likely-here.txt", :fsck => true) } end # end if ENV['STOMP_TESTSSL'] stomp-1.4.4/test/test_client.rb0000644000004100000410000005011113120662775016542 0ustar www-datawww-data# -*- encoding: utf-8 -*- if Kernel.respond_to?(:require_relative) require_relative("test_helper") else $:.unshift(File.dirname(__FILE__)) require 'test_helper' end =begin Main class for testing Stomp::Client instances. =end class TestClient < Test::Unit::TestCase include TestBase def setup @client = get_client() # Multi_thread test data @max_threads = 20 @max_msgs = 50 end def teardown @client.close if @client && @client.open? # allow tests to close end # Test poll works. def test_poll_async # If the test 'hangs' here, Connection#poll is broken. m = @client.poll assert m.nil? end # Test ACKs. def test_ack_api_works @client.publish make_destination, message_text, {:suppress_content_length => true} received = nil @client.subscribe(make_destination, {:ack => 'client'}) {|msg| received = msg} sleep 0.01 until received assert_equal message_text, received.body receipt = nil ack_headers = {} if @client.protocol == Stomp::SPL_11 # 1.1 only ack_headers["subscription"] = received.headers["subscription"] end @client.acknowledge(received, ack_headers) {|r| receipt = r} sleep 0.01 until receipt assert_not_nil receipt.headers['receipt-id'] checkEmsg(@client) end # Test Client subscribe def test_asynch_subscribe received = false @client.subscribe(make_destination) {|msg| received = msg} @client.publish make_destination, message_text sleep 0.01 until received assert_equal message_text, received.body checkEmsg(@client) end # Test not ACKing messages. def test_noack @client.publish make_destination, message_text received = nil @client.subscribe(make_destination, :ack => :client) {|msg| received = msg} sleep 0.01 until received assert_equal message_text, received.body @client.close # was never acked so should be resent to next client @client = get_client() received2 = nil @client.subscribe(make_destination) {|msg| received2 = msg} sleep 0.01 until received2 assert_equal message_text, received2.body assert_equal received.body, received2.body assert_equal received.headers['message-id'], received2.headers['message-id'] unless ENV['STOMP_RABBIT'] checkEmsg(@client) end unless RUBY_ENGINE =~ /jruby/ # Test obtaining a RECEIPT via a listener. def test_receipts receipt = false @client.publish(make_destination, message_text) {|r| receipt = r} sleep 0.1 until receipt assert_equal receipt.command, Stomp::CMD_RECEIPT message = nil @client.subscribe(make_destination) {|m| message = m} sleep 0.1 until message assert_equal message_text, message.body checkEmsg(@client) end # Test requesting a receipt on disconnect. def test_disconnect_receipt @client.close :receipt => "xyz789" assert_not_nil(@client.disconnect_receipt, "should have a receipt") assert_equal(@client.disconnect_receipt.headers['receipt-id'], "xyz789", "receipt sent and received should match") end # Test publish and immediate subscribe. def test_publish_then_sub @client.publish make_destination, message_text message = nil @client.subscribe(make_destination) {|m| message = m} sleep 0.01 until message assert_equal message_text, message.body checkEmsg(@client) end # Test that Client subscribe requires a block. def test_subscribe_requires_block assert_raise(Stomp::Error::NoListenerGiven) do @client.subscribe make_destination end checkEmsg(@client) end unless RUBY_ENGINE =~ /jruby/ # Test transaction publish. def test_transactional_publish @client.begin 'tx1' @client.publish make_destination, message_text, :transaction => 'tx1' @client.commit 'tx1' message = nil @client.subscribe(make_destination) {|m| message = m} sleep 0.01 until message assert_equal message_text, message.body checkEmsg(@client) end # Test transaction publish and abort. def test_transaction_publish_then_rollback @client.begin 'tx1' @client.publish make_destination, "first_message", :transaction => 'tx1' @client.abort 'tx1' @client.begin 'tx1' @client.publish make_destination, "second_message", :transaction => 'tx1' @client.commit 'tx1' message = nil @client.subscribe(make_destination) {|m| message = m} sleep 0.01 until message assert_equal "second_message", message.body checkEmsg(@client) end unless RUBY_ENGINE =~ /jruby/ # Test transaction publish and abort, receive with new client. # New client uses ack => client. def test_tran_ack_abrt_newcli_cli @client.close if @client && @client.open? # allow tests to close @client = get_client() q = make_destination data = message_text @client.publish q, data @client.begin 'tx1' message = nil sid = nil if @client.protocol() == Stomp::SPL_10 @client.subscribe(q, :ack => 'client') {|m| message = m} else # 1.1 and 1.2 are the same for this sid = @client.uuid() @client.subscribe(q, :ack => 'client', :id => sid) {|m| message = m} end sleep 0.01 until message assert_equal data, message.body case @client.protocol() when Stomp::SPL_10 @client.acknowledge message, :transaction => 'tx1' checkEmsg(@client) when Stomp::SPL_11 @client.acknowledge message, :transaction => 'tx1', :subscription => message.headers['subscription'] checkEmsg(@client) else # 1.2+ @client.acknowledge message, :transaction => 'tx1', :id => message.headers['ack'] checkEmsg(@client) end message = nil # reset @client.abort 'tx1' # now abort checkEmsg(@client) # lets recreate the connection @client.close @client = get_client() sid = nil message2 = nil @client.begin 'tx2' if @client.protocol() == Stomp::SPL_10 @client.subscribe(q, :ack => 'client') {|m| message2 = m} else # 1.1 and 1.2 are the same for this sid = @client.uuid() @client.subscribe(q, :ack => 'client', :id => sid) {|m| message2 = m} end sleep 0.01 until message2 assert_not_nil message assert_equal data, message2.body case @client.protocol() when Stomp::SPL_10 @client.acknowledge message2, :transaction => 'tx2' checkEmsg(@client) when Stomp::SPL_11 @client.acknowledge message2, :transaction => 'tx2', :subscription => message2.headers['subscription'] checkEmsg(@client) else # 1.2+ @client.acknowledge message2, :transaction => 'tx2', :id => message2.headers['ack'] checkEmsg(@client) end @client.commit 'tx2' checkEmsg(@client) @client.close end unless ENV['STOMP_ARTEMIS'] # See Artemis docs for 1.3, page 222 # Test transaction publish and abort, receive with new client. # New client uses ack => auto. def test_tran_ack_abrt_newcli_auto @client.close if @client && @client.open? # allow tests to close @client = get_client() q = make_destination data = message_text @client.publish q, data @client.begin 'tx1' message = nil sid = nil if @client.protocol() == Stomp::SPL_10 @client.subscribe(q, :ack => 'client') {|m| message = m} else # 1.1 and 1.2 are the same for this sid = @client.uuid() @client.subscribe(q, :ack => 'client', :id => sid) {|m| message = m} end sleep 0.01 until message assert_equal data, message.body case @client.protocol() when Stomp::SPL_10 @client.acknowledge message, :transaction => 'tx1' checkEmsg(@client) when Stomp::SPL_11 @client.acknowledge message, :transaction => 'tx1', :subscription => message.headers['subscription'] checkEmsg(@client) else # 1.2+ @client.acknowledge message, :transaction => 'tx1', :id => message.headers['ack'] checkEmsg(@client) end message = nil # reset @client.abort 'tx1' # now abort checkEmsg(@client) # lets recreate the connection @client.close @client = get_client() sid = nil message2 = nil @client.begin 'tx2' if @client.protocol() == Stomp::SPL_10 @client.subscribe(q, :ack => 'auto') {|m| message2 = m} else # 1.1 and 1.2 are the same for this sid = @client.uuid() @client.subscribe(q, :ack => 'auto', :id => sid) {|m| message2 = m} end sleep 0.01 until message2 assert_not_nil message2 assert_equal data, message2.body @client.commit 'tx2' checkEmsg(@client) @client.close end unless ENV['STOMP_ARTEMIS'] # See Artemis docs for 1.3, page 222 # Test that subscription destinations must be unique for a Client. def test_raise_on_multiple_subscriptions_to_same_make_destination subscribe_dest = make_destination @client.subscribe(subscribe_dest) {|m| nil } assert_raise(Stomp::Error::DuplicateSubscription) do @client.subscribe(subscribe_dest) {|m| nil } end checkEmsg(@client) end # Test that subscription IDs must be unique for a Client. def test_raise_on_multiple_subscriptions_to_same_id subscribe_dest = make_destination @client.subscribe(subscribe_dest, {'id' => 'myid'}) {|m| nil } assert_raise(Stomp::Error::DuplicateSubscription) do @client.subscribe(subscribe_dest, {'id' => 'myid'}) {|m| nil } end checkEmsg(@client) end # Test that subscription IDs must be unique for a Client, mixed id specification. def test_raise_on_multiple_subscriptions_to_same_id_mixed subscribe_dest = make_destination @client.subscribe(subscribe_dest, {'id' => 'myid'}) {|m| nil } assert_raise(Stomp::Error::DuplicateSubscription) do @client.subscribe(subscribe_dest, {:id => 'myid'}) {|m| nil } end checkEmsg(@client) end # Test wildcard subscribe. Primarily for AMQ. def test_asterisk_wildcard_subscribe queue_base_name = make_destination queue1 = queue_base_name + ".a" queue2 = queue_base_name + ".b" send_message = message_text @client.publish queue1, send_message @client.publish queue2, send_message messages = [] @client.subscribe(queue_base_name + ".*", :ack => 'client') do |m| messages << m @client.acknowledge(m) end Timeout::timeout(4) do sleep 0.1 while messages.size < 2 end messages.each do |message| assert_not_nil message assert_equal send_message, message.body end results = [queue1, queue2].collect do |queue| messages.any? do |message| message_source = message.headers['destination'] message_source == queue end end assert results.all?{|a| a == true } checkEmsg(@client) end unless ENV['STOMP_NOWILD'] # Test wildcard subscribe with >. Primarily for AMQ. def test_greater_than_wildcard_subscribe queue_base_name = make_destination + "." queue1 = queue_base_name + "foo.a" queue2 = queue_base_name + "bar.a" queue3 = queue_base_name + "foo.b" send_message = message_text @client.publish queue1, send_message @client.publish queue2, send_message @client.publish queue3, send_message messages = [] # should subscribe to all three queues @client.subscribe(queue_base_name + ">", :ack => 'client') do |m| messages << m @client.acknowledge(m) end Timeout::timeout(4) do sleep 0.1 while messages.size < 3 end messages.each do |message| assert_not_nil message assert_equal send_message, message.body end # make sure that the messages received came from the expected queues results = [queue1, queue2, queue3].collect do |queue| messages.any? do |message| message_source = message.headers['destination'] message_source == queue end end assert results.all?{|a| a == true } checkEmsg(@client) end unless ENV['STOMP_NOWILD'] || ENV['STOMP_DOTQUEUE'] # Test transaction with client side reacknowledge. def test_transaction_with_client_side_reack @client.close if @client && @client.open? # allow tests to close @client = get_client() q = make_destination data = message_text @client.publish q, data @client.begin 'tx1' message = nil sid = nil if @client.protocol() == Stomp::SPL_10 @client.subscribe(q, :ack => 'client') { |m| message = m } else sid = @client.uuid() @client.subscribe(q, :ack => 'client', :id => sid) { |m| message = m } end sleep 0.1 while message.nil? assert_equal data, message.body case @client.protocol() when Stomp::SPL_10 @client.acknowledge message, :transaction => 'tx1' checkEmsg(@client) when Stomp::SPL_11 @client.acknowledge message, :transaction => 'tx1', :subscription => message.headers['subscription'] checkEmsg(@client) else # 1.2+ @client.acknowledge message, :transaction => 'tx1', :id => message.headers['ack'] checkEmsg(@client) end message = nil @client.abort 'tx1' # Wait for redlivery (Client logic) sleep 0.1 while message.nil? assert_not_nil message assert_equal data, message.body @client.begin 'tx2' case @client.protocol() when Stomp::SPL_10 @client.acknowledge message, :transaction => 'tx2' checkEmsg(@client) when Stomp::SPL_11 @client.acknowledge message, :transaction => 'tx2', :subscription => message.headers['subscription'] checkEmsg(@client) else # 1.2+ @client.acknowledge message, :transaction => 'tx2', :id => message.headers['ack'] checkEmsg(@client) end @client.commit 'tx2' checkEmsg(@client) @client.close @client = nil end # Test that a connection frame is received. def test_connection_frame assert_not_nil @client.connection_frame checkEmsg(@client) end unless RUBY_ENGINE =~ /jruby/ # Test basic unsubscribe. def test_unsubscribe @client.close if @client && @client.open? # close setup work @client = nil message = nil dest = make_destination to_send = message_text client = get_client() sid = nil if client.protocol() == Stomp::SPL_10 client.subscribe(dest, :ack => 'client') { |m| message = m } else sid = client.uuid() client.subscribe(dest, :ack => 'client', :id => sid) { |m| message = m } end client.publish dest, to_send Timeout::timeout(4) do sleep 0.01 until message end assert_equal to_send, message.body, "first body check" if client.protocol() == Stomp::SPL_10 client.unsubscribe dest else client.unsubscribe dest, :id => sid end client.close # Same message should remain on the queue. Receive it again with ack=>auto. message_copy = nil client = get_client() if client.protocol() == Stomp::SPL_10 client.subscribe(dest, :ack => 'auto') { |m| message_copy = m } else sid = client.uuid() client.subscribe(dest, :ack => 'auto', :id => sid) { |m| message_copy = m } end Timeout::timeout(4) do sleep 0.01 until message_copy end assert_equal to_send, message_copy.body, "second body check" assert_equal message.headers['message-id'], message_copy.headers['message-id'], "header check" unless ENV['STOMP_RABBIT'] checkEmsg(client) client.close end # Test subscribe from a worker thread. def test_thread_one_subscribe msg = nil dest = make_destination Thread.new(@client) do |acli| if acli.protocol() == Stomp::SPL_10 acli.subscribe(dest) { |m| msg = m } else acli.subscribe(dest, :id => acli.uuid()) { |m| msg = m } end Timeout::timeout(4) do sleep 0.01 until msg end end # @client.publish(dest, message_text) sleep 1 assert_not_nil msg checkEmsg(@client) end unless RUBY_ENGINE =~ /jruby/ # Test subscribe from multiple worker threads. def test_thread_multi_subscribe # lock = Mutex.new msg_ctr = 0 dest = make_destination 1.upto(@max_threads) do |tnum| # Threads within threads ..... Thread.new(@client) do |acli| # this is ugly ..... if acli.protocol() == Stomp::SPL_10 acli.subscribe(dest) { |m| _ = m lock.synchronize do msg_ctr += 1 end # Simulate message processing sleep 0.05 } else acli.subscribe(dest, :id => acli.uuid()) { |m| _ = m lock.synchronize do msg_ctr += 1 end # Simulate message processing sleep 0.05 } end end end # 1.upto(@max_msgs) do |mnum| msg = Time.now.to_s + " #{mnum}" @client.publish(dest, msg) end # max_sleep = (RUBY_VERSION =~ /1\.8\.6/) ? 30 : 5 sleep_incr = 0.10 total_slept = 0 while true break if @max_msgs == msg_ctr total_slept += sleep_incr break if total_slept > max_sleep sleep sleep_incr end assert_equal @max_msgs, msg_ctr checkEmsg(@client) end # Test that methods detect no client connection is present. def test_closed_checks_client @client.close # assert_raise Stomp::Error::NoCurrentConnection do m = Stomp::Message.new("") @client.acknowledge(m) {|r| _ = r} end # assert_raise Stomp::Error::NoCurrentConnection do @client.begin("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @client.commit("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @client.abort("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @client.subscribe("dummy_data", {:ack => 'auto'}) {|msg| _ = msg} end # assert_raise Stomp::Error::NoCurrentConnection do @client.unsubscribe("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @client.publish("dummy_data","dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @client.unreceive("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @client.close("dummy_data") end end # test JRuby detection def test_jruby_presence if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/ assert @client.jruby? else assert !@client.jruby? end end # test max redeliveries is not broken (6c2c1c1) def test_max_redeliveries @client.close 2.upto(2) do |max_re| rdmsg = "To Be Redelivered: #{max_re.to_s}" dest = make_destination + ".#{max_re.to_s}" @client = get_client() sid = "sid_max_redeliveries_#{max_re.to_s}" received = nil rm_actual = 0 sh = @client.protocol() == Stomp::SPL_10 ? {} : {:id => sid} @client.subscribe(dest, sh) {|msg| rm_actual += 1 @client.unreceive(msg, :max_redeliveries => max_re, :dead_letter_queue => make_dlq()) received = msg if rm_actual - 1 == max_re } @client.publish(dest, rdmsg) sleep 0.01 until received assert_equal rdmsg, received.body sleep 0.5 @client.unsubscribe dest, sh assert_equal max_re, rm_actual - 1 @client.close end end unless ENV['STOMP_ARTEMIS'] # need to investigate this, but skip # Artemis for now # test issue99, OK values def test_cli_iss99_ok return unless host() == "localhost" && port() == 61613 @client.close # ok_vals = dflt_data_ok() ok_vals.each do |hsv| cli = Stomp::Client.open(hsv) cli.close end end # test issue99, exception values def test_cli_iss99_ex return unless host() == "localhost" && port() == 61613 @client.close # ex_vals = dflt_data_ex() ex_vals.each do |hsv| assert_raise ArgumentError do _ = Stomp::Client.open(hsv) end end end def test_cli_nodest_sub assert_raise Stomp::Error::DestinationRequired do @client.subscribe(nil) {|msg| puts msg} end end def test_cli_nodest_unsub assert_raise Stomp::Error::DestinationRequired do @client.unsubscribe(nil) end end def test_cli_nodest_pub assert_raise Stomp::Error::DestinationRequired do @client.publish(nil, "msg") end end private def message_text name = caller_method_name unless name "test_client#" + name end end stomp-1.4.4/test/test_message.rb0000644000004100000410000001241713120662775016717 0ustar www-datawww-data# -*- encoding: us-ascii -*- # # !!!! # # This can *NOT* currently be marked as UTF-8 encoded. It uses invalid UTF-8 # sequences for testing. Tests will fail under 1.9.x+ if this file is marked # as UTF-8 encoded. Tests will fail under 2.0.0 if this file is *NOT* marked # as US-ASCII. # if Kernel.respond_to?(:require_relative) require_relative("test_helper") else $:.unshift(File.dirname(__FILE__)) require 'test_helper' end # # Test Ruby 1.8 with $KCODE='U' # =begin Main class for testing Stomp::Message. =end class TestMessage < Test::Unit::TestCase include TestBase # def setup $KCODE = 'U' if RUBY_VERSION =~ /1\.8/ @conn = get_connection() # Message body data @messages = [ "normal text message", "bad byte: \372", "\004\b{\f:\tbody\"\001\207\004\b{\b:\016statusmsg\"\aOK:\017statuscodei\000:\tdata{\t:\voutput\"3Enabled, not running, last run 693 seconds ago:\frunningi\000:\fenabledi\006:\flastrunl+\aE\021\022M:\rsenderid\"\032xx.xx.xx.xx:\016requestid\"%849d647bbe3e421ea19ac9f947bbdde4:\020senderagent\"\fpuppetd:\016msgtarget\"%/topic/mcollective.puppetd.reply:\thash\"\001\257ZdQqtaDmmdD0jZinnEcpN+YbkxQDn8uuCnwsQdvGHau6d+gxnnfPLUddWRSb\nZNMs+sQUXgJNfcV1eVBn1H+Z8QQmzYXVDMqz7J43jmgloz5PsLVbN9K3PmX/\ngszqV/WpvIyAqm98ennWqSzpwMuiCC4q2Jr3s3Gm6bUJ6UkKXnY=\n:\fmsgtimel+\a\372\023\022M" ] # end def teardown @conn.disconnect if @conn # allow tests to disconnect end # Various message bodies, including the failing test case reported def test_0010_kcode # dest = make_destination if @conn.protocol == Stomp::SPL_10 @conn.subscribe dest else sh = {} @conn.subscribe dest, sh, @conn.uuid() end @messages.each do |abody| @conn.publish dest, abody msg = @conn.receive assert_instance_of Stomp::Message , msg, "type check for #{abody}" assert_equal abody, msg.body, "equal check for #{abody}" end end # All possible byte values def test_0020_kcode # abody = "" "\000".upto("\377") {|abyte| abody << abyte } # dest = make_destination if @conn.protocol == Stomp::SPL_10 @conn.subscribe dest else sh = {} @conn.subscribe dest, sh, @conn.uuid() end @conn.publish dest, abody msg = @conn.receive assert_instance_of Stomp::Message , msg, "type check for #{abody}" assert_equal abody, msg.body, "equal check for #{abody}" end # A single byte at a time def test_0030_kcode # dest = make_destination if @conn.protocol == Stomp::SPL_10 @conn.subscribe dest else sh = {:id => @conn.uuid()} @conn.subscribe dest, sh end # "\000".upto("\377") do |abody| @conn.publish dest, abody msg = @conn.receive assert_instance_of Stomp::Message , msg, "type check for #{abody}" assert_equal abody, msg.body, "equal check for #{abody}" end end # Test various valid and invalid frames. def test_0040_msg_create # assert_raise(Stomp::Error::InvalidFormat) { _ = Stomp::Message.new("junk", false) } # assert_raise(Stomp::Error::InvalidFormat) { _ = Stomp::Message.new("command\njunk", false) } # assert_raise(Stomp::Error::InvalidFormat) { _ = Stomp::Message.new("command\nheaders\n\njunk", false) } # assert_raise(Stomp::Error::InvalidServerCommand) { _ = Stomp::Message.new("junkcommand\nheaders\n\njunk\0\n\n", false) } # assert_raise(Stomp::Error::InvalidFormat) { _ = Stomp::Message.new("ERROR\nbadheaders\n\njunk\0\n\n", false) } # _ = Stomp::Message.new("CONNECTED\nh1:val1\n\njunk\0\n", false) _ = Stomp::Message.new("MESSAGE\nh1:val1\n\njunk\0\n", false) _ = Stomp::Message.new("MESSAGE\nh2:val2\n\n\0", false) _ = Stomp::Message.new("RECEIPT\nh1:val1\n\njunk\0\n", false) _ = Stomp::Message.new("ERROR\nh1:val1\n\njunk\0\n", false) end # Test multiple headers with the same key def test_0050_mh_msg_create aframe = bframe = nil amsg = "MESSAGE\n" + "h1:val1\n" + "h2:val3\n" + "h2:val2\n" + "h2:val1\n" + "h3:val1\n" + "\n" + "payload" + "\0\n" aframe = Stomp::Message.new(amsg, false) bframe = Stomp::Message.new(amsg, true) # assert aframe.headers["h2"].is_a?(String), "Expected a String" assert_equal "val3", aframe.headers["h2"], "Expected 1st value" # assert bframe.headers["h2"].is_a?(Array), "Expected an Array" assert_equal 3, bframe.headers["h2"].length, "Expected 3 values" assert_equal "val3", bframe.headers["h2"][0], "Expected val3" assert_equal "val2", bframe.headers["h2"][1], "Expected val2" assert_equal "val1", bframe.headers["h2"][2], "Expected val1" end # Test headers with empty key / value def test_0060_hdr_ekv # amsg = "MESSAGE\n" + "h1:val1\n" + ":val3\n" + "h2:val2\n" + "\n" + "payload" + "\0\n" assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do _ = Stomp::Message.new(amsg, false) end assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do _ = Stomp::Message.new(amsg, true) end # amsg = "MESSAGE\n" + "h1:val1\n" + "h2:val3\n" + "h3:\n" + "\n" + "payload" + "\0\n" assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do _ = Stomp::Message.new(amsg, false) end _ = Stomp::Message.new(amsg, true) end end stomp-1.4.4/test/test_codec.rb0000644000004100000410000000732013120662775016345 0ustar www-datawww-data# -*- encoding: utf-8 -*- if Kernel.respond_to?(:require_relative) require_relative("test_helper") else $:.unshift(File.dirname(__FILE__)) require 'test_helper' end =begin Main class for testing Stomp::HeadreCodec methods. =end class TestCodec < Test::Unit::TestCase include TestBase def setup @conn = get_connection() # Data for multi_thread tests @max_threads = 20 @max_msgs = 100 end def teardown @conn.disconnect if @conn.open? # allow tests to disconnect end # Test that the codec does nothing to strings that do not need encoding. def test_1000_check_notneeded test_data = [ "a", "abcdefghijklmnopqrstuvwxyz", "ªºÀÁ", "AÇBØCꞇDẼ", "ªºÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ" + "ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġDŽDždžLJLjǼǽǾǿȀȁȂȃȌȍȒɰɵɲɮᴘᴤᴭᴥᵻᶅ" + "ᶑṆṌṕṽẄẂỚỘⅱⅲꜨꝐꞂ", ] # test_data.each do |s| # s_decoded_a = Stomp::HeaderCodec::decode(s) assert_equal s, s_decoded_a, "Sanity check decode: #{s} | #{s_decoded_a}" s_reencoded = Stomp::HeaderCodec::encode(s_decoded_a) assert_equal s_decoded_a, s_reencoded, "Sanity check reencode: #{s_decoded_a} | #{s_reencoded}" # end end # Test the basic encoding / decoding requirements. def test_1010_basic_encode_decode test_data = [ [ "\\\\\\\\", "\\\\" ], # [encoded, decoded] [ "\\\\", "\\" ], # [encoded, decoded] ["\\n", "\n"], ["\\r", "\r"], ["\\c", ":"], ["\\\\\\n\\c", "\\\n:"], ["\\\\\\r\\c", "\\\r:"], ["\\c\\n\\\\", ":\n\\"], ["\\c\\r\\\\", ":\r\\"], ["\\\\\\c", "\\:"], ["c\\cc", "c:c"], ["n\\nn", "n\nn"], ["r\\rr", "r\rr"], ] # test_data.each do |s| encoded_orig = s[0] decoded_orig = s[1] # Part 1 s_decoded_a = Stomp::HeaderCodec::decode(encoded_orig) assert_equal decoded_orig, s_decoded_a, "Sanity check decode: #{decoded_orig} | #{s_decoded_a}" # s_encoded_a = Stomp::HeaderCodec::encode(decoded_orig) assert_equal encoded_orig, s_encoded_a, "Sanity check encode: #{encoded_orig} | #{s_encoded_a}" # Part 2 s_decoded_b = Stomp::HeaderCodec::decode(s_encoded_a) assert_equal decoded_orig, s_decoded_b, "Sanity check 2 decode: #{decoded_orig} | #{s_decoded_b}" # s_encoded_b = Stomp::HeaderCodec::encode(s_decoded_a) assert_equal encoded_orig, s_encoded_b, "Sanity check 2 encode: #{encoded_orig} | #{s_encoded_b}" end end # Test more complex strings with the codec. def test_1020_fancier test_data = [ [ "a\\\\b", "a\\b" ], # [encoded, decoded] [ "\\\\\\n\\c", "\\\n:" ], [ "\\\\\\r\\c", "\\\r:" ], [ "\\rr\\\\\\n\\c", "\rr\\\n:" ], ] # test_data.each do |s| encoded_orig = s[0] decoded_orig = s[1] # Part 1 s_decoded_a = Stomp::HeaderCodec::decode(encoded_orig) assert_equal decoded_orig, s_decoded_a, "Sanity check decode: #{decoded_orig} | #{s_decoded_a}" # s_encoded_a = Stomp::HeaderCodec::encode(decoded_orig) assert_equal encoded_orig, s_encoded_a, "Sanity check encode: #{encoded_orig} | #{s_encoded_a}" # Part 2 s_decoded_b = Stomp::HeaderCodec::decode(s_encoded_a) assert_equal decoded_orig, s_decoded_b, "Sanity check 2 decode: #{decoded_orig} | #{s_decoded_b}" # s_encoded_b = Stomp::HeaderCodec::encode(s_decoded_a) assert_equal encoded_orig, s_encoded_b, "Sanity check 2 encode: #{encoded_orig} | #{s_encoded_b}" end end end # of class stomp-1.4.4/test/funcs.sh0000755000004100000410000000012213120662775015352 0ustar www-datawww-data# ------------------------------------------------------------------------------ stomp-1.4.4/test/test_anonymous.rb0000644000004100000410000003265413120662775017330 0ustar www-datawww-data# -*- encoding: utf-8 -*- if Kernel.respond_to?(:require_relative) require_relative("test_helper") else $:.unshift(File.dirname(__FILE__)) require 'test_helper' end =begin Main class for testing Stomp::Connection instances with anonymous logins. =end class TestAnonymous < Test::Unit::TestCase include TestBase def setup @conn = get_anonymous_connection() # Data for multi_thread tests @max_threads = 20 @max_msgs = 100 end def teardown @conn.disconnect if @conn.open? # allow tests to disconnect end # Test basic connection creation. def test_connection_exists assert_not_nil @conn end # Test asynchronous polling. def test_poll_async sq = ENV['STOMP_ARTEMIS'] ? "jms.queue.queue.do.not.put.messages.on.this.queue" : "/queue/do.not.put.messages.on.this.queue" @conn.subscribe(sq, :id => "a.no.messages.queue") # If the test 'hangs' here, Connection#poll is broken. m = @conn.poll assert m.nil? end # Test suppression of content length header. def test_no_length conn_subscribe make_destination # @conn.publish make_destination, "test_stomp#test_no_length", { :suppress_content_length => true } msg = @conn.receive assert_equal "test_stomp#test_no_length", msg.body # @conn.publish make_destination, "test_stomp#test_\000_length", { :suppress_content_length => true } msg2 = @conn.receive assert_equal "test_stomp#test_", msg2.body checkEmsg(@conn) end unless ENV['STOMP_RABBIT'] # Test direct / explicit receive. def test_explicit_receive conn_subscribe make_destination @conn.publish make_destination, "test_stomp#test_explicit_receive" msg = @conn.receive assert_equal "test_stomp#test_explicit_receive", msg.body end # Test asking for a receipt. def test_receipt conn_subscribe make_destination, :receipt => "abc" msg = @conn.receive assert_equal "abc", msg.headers['receipt-id'] checkEmsg(@conn) end # Test asking for a receipt on disconnect. def test_disconnect_receipt @conn.disconnect :receipt => "abc123" assert_not_nil(@conn.disconnect_receipt, "should have a receipt") assert_equal(@conn.disconnect_receipt.headers['receipt-id'], "abc123", "receipt sent and received should match") end # Test ACKs for Stomp 1.0 def test_client_ack_with_symbol_10 if @conn.protocol != Stomp::SPL_10 assert true return end queue = make_destination() @conn.subscribe queue, :ack => :client @conn.publish queue, "test_stomp#test_client_ack_with_symbol_10" msg = @conn.receive # ACK has one required header, message-id, which must contain a value # matching the message-id for the MESSAGE being acknowledged. @conn.ack msg.headers['message-id'] checkEmsg(@conn) end # Test ACKs for Stomp 1.1 def test_client_ack_with_symbol_11 if @conn.protocol != Stomp::SPL_11 assert true return end sid = @conn.uuid() queue = make_destination() @conn.subscribe queue, :ack => :client, :id => sid @conn.publish queue, "test_stomp#test_client_ack_with_symbol_11" msg = @conn.receive # ACK has two REQUIRED headers: message-id, which MUST contain a value # matching the message-id for the MESSAGE being acknowledged and # subscription, which MUST be set to match the value of the subscription's # id header. @conn.ack msg.headers['message-id'], :subscription => msg.headers['subscription'] checkEmsg(@conn) end # Test ACKs for Stomp 1.2 def test_client_ack_with_symbol_12 if @conn.protocol != Stomp::SPL_12 assert true return end sid = @conn.uuid() queue = make_destination() @conn.subscribe queue, :ack => :client, :id => sid @conn.publish queue, "test_stomp#test_client_ack_with_symbol_11" msg = @conn.receive # The ACK frame MUST include an id header matching the ack header # of the MESSAGE being acknowledged. @conn.ack msg.headers['ack'] checkEmsg(@conn) end # Test a message with 0x00 embedded in the body. def test_embedded_null conn_subscribe make_destination @conn.publish make_destination, "a\0" msg = @conn.receive assert_equal "a\0" , msg.body checkEmsg(@conn) end # Test connection open checking. def test_connection_open? assert_equal true , @conn.open? @conn.disconnect assert_equal false, @conn.open? end # Test connection closed checking. def test_connection_closed? assert_equal false, @conn.closed? @conn.disconnect assert_equal true, @conn.closed? end # Test that methods detect a closed connection. def test_closed_checks_conn @conn.disconnect # assert_raise Stomp::Error::NoCurrentConnection do @conn.ack("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.begin("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.commit("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.abort("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do conn_subscribe("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.unsubscribe("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.publish("dummy_data","dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.unreceive("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.disconnect("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do _ = @conn.receive end # assert_raise Stomp::Error::NoCurrentConnection do _ = @conn.poll end end # Test that we receive a Stomp::Message. def test_response_is_instance_of_message_class conn_subscribe make_destination @conn.publish make_destination, "a\0" msg = @conn.receive assert_instance_of Stomp::Message , msg checkEmsg(@conn) end # Test converting a Message to a string. def test_message_to_s conn_subscribe make_destination @conn.publish make_destination, "a\0" msg = @conn.receive assert_match(/^ max_sleep sleep sleep_incr end assert_equal @max_msgs, msg_ctr checkEmsg(@conn) end unless RUBY_ENGINE =~ /jruby/ # Test polling with multiple threads. def test_multi_thread_poll # lock = Mutex.new msg_ctr = 0 dest = make_destination # 1.upto(@max_threads) do |tnum| Thread.new(@conn) do |amq| while true received = amq.poll if received lock.synchronize do msg_ctr += 1 end # Simulate message processing sleep 0.05 else # Wait a bit for more work sleep 0.05 end end end end # conn_subscribe( dest ) 1.upto(@max_msgs) do |mnum| msg = Time.now.to_s + " #{mnum}" @conn.publish(dest, msg) end # max_sleep = (RUBY_VERSION =~ /1\.8\.6/) ? 30 : 5 max_sleep = 30 if RUBY_ENGINE =~ /mingw/ sleep_incr = 0.10 total_slept = 0 while true break if @max_msgs == msg_ctr total_slept += sleep_incr break if total_slept > max_sleep sleep sleep_incr end assert_equal @max_msgs, msg_ctr checkEmsg(@conn) end unless RUBY_ENGINE =~ /jruby/ # Test using a nil body. def test_nil_body dest = make_destination @conn.publish dest, nil conn_subscribe dest msg = @conn.receive assert_equal "", msg.body checkEmsg(@conn) end # Test transaction message sequencing. def test_transaction conn_subscribe make_destination @conn.begin "txA" @conn.publish make_destination, "txn message", 'transaction' => "txA" @conn.publish make_destination, "first message" msg = @conn.receive assert_equal "first message", msg.body @conn.commit "txA" msg = @conn.receive assert_equal "txn message", msg.body checkEmsg(@conn) end unless ENV['STOMP_ARTEMIS'] # See Artemis docs for 1.3, page 222 # Test duplicate subscriptions. def test_duplicate_subscription @conn.disconnect # not reliable @conn = Stomp::Connection.open(nil, nil, host, port, true, nil, nil) # reliable dest = make_destination conn_subscribe dest # assert_raise Stomp::Error::DuplicateSubscription do conn_subscribe dest end checkEmsg(@conn) end # Test nil 1.1 connection parameters. def test_nil_connparms @conn.disconnect # @conn = Stomp::Connection.open(nil, nil, host, port, false, 5, nil) checkEmsg(@conn) end # Basic NAK test. def test_nack11p_0010 if @conn.protocol == Stomp::SPL_10 assert_raise Stomp::Error::UnsupportedProtocolError do @conn.nack "dummy msg-id" end else dest = make_destination smsg = "test_stomp#test_nack01: #{Time.now.to_f}" @conn.publish dest, smsg # sid = @conn.uuid() @conn.subscribe dest, :ack => :client, :id => sid msg = @conn.receive assert_equal smsg, msg.body case @conn.protocol when Stomp::SPL_12 @conn.nack msg.headers["ack"] sleep 0.05 # Give racy brokers a chance to handle the last nack before unsubscribe @conn.unsubscribe dest, :id => sid else # Stomp::SPL_11 @conn.nack msg.headers["message-id"], :subscription => sid sleep 0.05 # Give racy brokers a chance to handle the last nack before unsubscribe @conn.unsubscribe dest, :id => sid end # phase 2 teardown() setup() sid = @conn.uuid() @conn.subscribe dest, :ack => :auto, :id => sid msg2 = @conn.receive assert_equal smsg, msg2.body checkEmsg(@conn) end end unless (ENV['STOMP_AMQ11'] || ENV['STOMP_ARTEMIS']) # Test to illustrate Issue #44. Prior to a fix for #44, these tests would # fail only when connecting to a pure STOMP 1.0 server that does not # return a 'version' header at all. def test_conn10_simple @conn.disconnect # vhost = ENV['STOMP_RABBIT'] ? "/" : host hash = { :hosts => [ {:host => host, :port => port, :ssl => false}, ], :connect_headers => {"accept-version" => "1.0", "host" => vhost}, :reliable => false, } c = nil c = Stomp::Connection.new(hash) c.disconnect if c # hash = { :hosts => [ {:host => host, :port => port, :ssl => false}, ], :connect_headers => {"accept-version" => "3.14159,1.0,12.0", "host" => vhost}, :reliable => false, } c = nil c = Stomp::Connection.new(hash) c.disconnect if c end # test JRuby detection def test_jruby_presence if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/ assert @conn.jruby else assert !@conn.jruby end end end stomp-1.4.4/test/test_connection1p.rb0000644000004100000410000002644113120662775017675 0ustar www-datawww-data# -*- encoding: utf-8 -*- if Kernel.respond_to?(:require_relative) require_relative("test_helper") else $:.unshift(File.dirname(__FILE__)) require 'test_helper' end =begin Main class for testing Stomp::Connection instances, protocol levels 1.1+. =end class TestConnection1P < Test::Unit::TestCase include TestBase def setup @conn = get_connection() end def teardown @conn.disconnect if @conn.open? # allow tests to disconnect end # Test basic connection open. def test_conn_1p_0000 assert @conn.open? end # Test missing connect headers - part 1. def test_conn_1p_0010 @conn.disconnect # cha = {:host => "localhost"} assert_raise Stomp::Error::ProtocolErrorConnect do _ = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) end # chb = {"accept-version" => "1.0"} assert_raise Stomp::Error::ProtocolErrorConnect do _ = Stomp::Connection.open(user, passcode, host, port, false, 5, chb) end end # Test missing connect headers - part 2. def test_conn_1p_0015 @conn.disconnect # cha = {:host => "localhost"} hash = { :hosts => [ {:login => user, :passcode => passcode, :host => host, :port => port, :ssl => nil}, ], :reliable => true, # Note, issue #57 discussion :connect_headers => cha, :stompconn => get_stomp_conn(), :usecrlf => get_crlf(), :initial_reconnect_delay => 0.1, :max_reconnect_delay => 30, :use_exponential_back_off => true, :back_off_multiplier => 2, :max_reconnect_attempts => 10, } assert_raise Stomp::Error::ProtocolErrorConnect do _ = Stomp::Connection.open(hash) end hash[:connect_headers] = {"accept-version" => "1.1"} assert_raise Stomp::Error::ProtocolErrorConnect do _ = Stomp::Connection.open(hash) end end # Test requesting only a 1.0 connection. def test_conn_1p_0020 @conn.disconnect # cha = {:host => "localhost", "accept-version" => "1.0"} cha[:host] = "/" if ENV['STOMP_RABBIT'] conn = nil conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) conn.disconnect assert_equal conn.protocol, Stomp::SPL_10 end # Test requesting only a 1.1+ connection. def test_conn_1p_0030 @conn.disconnect # cha = get_conn_headers() conn = nil conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) conn.disconnect assert conn.protocol >= Stomp::SPL_11 end # Test basic request for no heartbeats. def test_conn_1p_0040 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "0,0" # No heartbeats conn = nil conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) conn.disconnect assert conn.protocol >= Stomp::SPL_11 end # Test malformed heartbeat header. def test_conn_1p_0050 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "10,10,20" # Bad header Heartbeats conn = nil assert_raise Stomp::Error::InvalidHeartBeatHeaderError do conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) end end # Test malformed heartbeat header. def test_conn_11h_0060 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "a,10" # Bad header Heartbeats conn = nil assert_raise Stomp::Error::InvalidHeartBeatHeaderError do conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) end end # Test a valid heartbeat header. def test_conn_1p_0070 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "500,1000" # Valid heart beat headers conn = nil conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) conn.disconnect assert conn.hbsend_interval > 0 assert conn.hbrecv_interval > 0 end # Test only sending heartbeats. def test_conn_1p_0080 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "10000,0" # Valid heart beat headers, send only conn = nil logger = Tlogger.new conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) conn.set_logger(logger) sleep 65 conn.set_logger(nil) conn.disconnect hb_asserts_send(conn) end if ENV['STOMP_HB11LONG'] # Test only receiving heartbeats. def test_conn_1p_0090 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "0,6000" # Valid heart beat headers, receive only conn = nil logger = Tlogger.new conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) # m = conn.receive # This will hang forever ..... conn.set_logger(logger) sleep 65 conn.set_logger(nil) conn.disconnect hb_asserts_recv(conn) end if ENV['STOMP_HB11LONG'] # Test sending and receiving heartbeats. def test_conn_1p_0100 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "5000,10000" # Valid heart beat headers, send and receive conn = nil logger = Tlogger.new conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) # m = conn.receive # This will hang forever ..... conn.set_logger(logger) sleep 65 conn.set_logger(nil) conn.disconnect hb_asserts_both(conn) end if ENV['STOMP_HB11LONG'] # Test valid UTF8 data. def test_conn_1p_0110 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "0,0" # No heartbeats conn = nil conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) good_data = [ "\x41\xc3\xb1\x42", "\xc2\x80", # 2 byte characters "\xc2\xbf", "\xdf\x80", "\xdf\xbf", "\xe0\xa0\x80", # 3 byte characters "\xe0\xbf\x80", "\xe0\xa0\xbf", "\xe0\xbf\xbf", "\xf1\x80\x80\x80", # 4 byte characters "\xf1\xbf\xbf\xbf", "\xf2\x80\x80\x80", "\xf2\xbf\xbf\xbf", "\xf3\x80\x80\x80", "\xf3\xbf\xbf\xbf", ] good_data.each do |string| assert conn.valid_utf8?(string), "good unicode specs 01: #{string}" end conn.disconnect end # Test invalid UTF8 data. def test_conn_1p_0120 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "0,0" # No heartbeats conn = nil conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) bad_data = [ "\x41\xc2\xc3\xb1\x42", "\xed\xa0\x80", # UTF-16 surrogate halves "\xed\xad\xbf", "\xed\xae\x80", "\xed\xaf\xbf", "\xed\xb0\x80", "\xed\xbe\x80", "\xed\xbf\xbf", "\xc0", # Single bytes "\xc1", "\xf5","\xf6","\xf7","\xf8","\xf9","\xfa","\xfb","\xfc", "\xfd","\xfe","\xff", "\xc0\x80", # Not shortest representation "\xc1\x80", "\xc0\x30", "\xc1\x30", "\xe0\x80\x80", "\xf0\x80\x80\x80", ] bad_data.each do |string| assert !conn.valid_utf8?(string), "bad unicode specs 01: #{string}" end conn.disconnect end # Repeated headers test. Brokers have a lot of freedom given the verbiage # in the specs. def test_conn_1p_0124 dest = make_destination msg = "payload: #{Time.now.to_f}" shdrs = { "key1" => "val1", "key2" => "val2", "key3" => ["kv3", "kv2", "kv1"] } @conn.publish dest, msg, shdrs # sid = @conn.uuid() @conn.subscribe dest, :id => sid # received = @conn.receive assert_equal msg, received.body if @conn.protocol != Stomp::SPL_10 assert_equal shdrs["key3"], received.headers["key3"] unless ENV['STOMP_RABBIT'] || ENV['STOMP_AMQ11'] || ENV['STOMP_ARTEMIS'] else assert_equal "kv3", received.headers["key3"] end # @conn.unsubscribe dest, :id => sid end # Test frozen headers. def test_conn_1p_0127 dest = make_destination sid = @conn.uuid() sid.freeze @conn.subscribe dest, :id => sid end # Test heartbeats with send and receive. def test_conn_1p_0130 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "10000,6000" # Valid heart beat headers, send and receive conn = nil logger = Tlogger.new conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) # m = conn.receive # This will hang forever ..... conn.set_logger(logger) sleep 65 conn.set_logger(nil) conn.disconnect hb_asserts_both(conn) end if ENV['STOMP_HB11LONG'] # Test heartbeats with send and receive. def test_conn_1p_0135 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "10000,1000" # Valid heart beat headers, send and receive conn = nil logger = Tlogger.new conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) # m = conn.receive # This will hang forever ..... conn.set_logger(logger) sleep 65 conn.set_logger(nil) conn.disconnect hb_asserts_both(conn) end if ENV['STOMP_HB11LONG'] # Test heartbeats with send and receive. def test_conn_1p_0140 @conn.disconnect # cha = get_conn_headers() cha["heart-beat"] = "1000,10000" # Valid heart beat headers, send and receive conn = nil logger = Tlogger.new conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) # m = conn.receive # This will hang forever ..... conn.set_logger(logger) sleep 65 conn.set_logger(nil) conn.disconnect hb_asserts_both(conn) end if ENV['STOMP_HB11LONG'] # Test very basic encoding / decoding of headers def test_conn_1p_0200 @conn.disconnect # cha = get_conn_headers() conn = Stomp::Connection.open(user, passcode, host, port, false, 5, cha) msg = "payload: #{Time.now.to_f}" dest = make_destination shdrs = { "ab:cd" => "ef:gh", "a\nb" => "c\nd", "x\\y" => "z\\s" } if conn.protocol >= Stomp::SPL_12 shdrs["bb\rcc"] = "dd\ree" end conn.publish dest, msg, shdrs # sid = conn.uuid() conn.subscribe dest, :id => sid # received = conn.receive assert_equal msg, received.body # shdrs.each_pair {|k,v| assert received.headers.has_key?(k), "Key not found: #{k}" assert received.headers.has_value?(v), "Value not found: #{v}" assert received.headers[k] == v, "Mismatch: #{k},#{v}" } conn.disconnect end unless ENV['STOMP_RABBIT'] # Test that 1.1+ connections do not break suppress_content_length # (Issue #52) def test_conn_1p_0210 msg = "payload: #{Time.now.to_f}" dest = make_destination shdrs = { :suppress_content_length => true } @conn.publish dest, msg, shdrs # sid = @conn.uuid() @conn.subscribe dest, :id => sid # received = @conn.receive assert_equal msg, received.body assert_nil received.headers["content-length"], "No content length expected." end if ENV['STOMP_AMQ11'] private def hb_asserts_both(conn) assert conn.hbsend_interval > 0 assert conn.hbrecv_interval > 0 assert conn.hbsend_count > 0 assert conn.hbrecv_count > 0 end def hb_asserts_send(conn) assert conn.hbsend_interval > 0 assert conn.hbrecv_interval == 0 assert conn.hbsend_count > 0 assert conn.hbrecv_count == 0 end def hb_asserts_recv(conn) assert conn.hbsend_interval == 0 assert conn.hbrecv_interval > 0 assert conn.hbsend_count == 0 assert conn.hbrecv_count > 0 end end if ENV['STOMP_TEST11p'] stomp-1.4.4/test/test_helper.rb0000644000004100000410000001363413120662775016554 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'test/unit' require 'timeout' if Kernel.respond_to?(:require_relative) require_relative("../lib/stomp") require_relative("tlogger") else $:.unshift(File.join(File.dirname(__FILE__), "..", "lib")) require 'stomp' require 'tlogger' end begin _ = RUBY_ENGINE rescue NameError RUBY_ENGINE = "unknown" end =begin Test helper methods. =end module TestBase # Get user def user ENV['STOMP_USER'] || "guest" end # Gete passcode def passcode ENV['STOMP_PASSCODE'] || "guest" end # Get host def host ENV['STOMP_HOST'] || "localhost" end # Get port def port (ENV['STOMP_PORT'] || 61613).to_i end # Get SSL port def ssl_port (ENV['STOMP_SSLPORT'] || 61612).to_i end # Helper for minitest on 1.9 def caller_method_name parse_caller(caller(2).first).last end # Helper for minitest on 1.9 def parse_caller(at) if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at file = Regexp.last_match[1] line = Regexp.last_match[2].to_i method = Regexp.last_match[3] method.gsub!(" ","_") [file, line, method] end end # Get a Stomp Connection. def get_connection() ch = get_conn_headers() hash = { :hosts => [ {:login => user, :passcode => passcode, :host => host, :port => port, :ssl => nil}, ], :reliable => false, :connect_headers => ch, :stompconn => get_stomp_conn(), :usecrlf => get_crlf(), } conn = Stomp::Connection.open(hash) conn end # Get a Stomp Anonymous Connection. def get_anonymous_connection() ch = get_conn_headers() hash = { :hosts => [ {:host => host, :port => port, :ssl => nil}, ], :reliable => false, :connect_headers => ch, :stompconn => get_stomp_conn(), :usecrlf => get_crlf(), } conn = Stomp::Connection.open(hash) conn end # Get a Stomp SSL Connection. def get_ssl_connection() ch = get_conn_headers() ssl_params = Stomp::SSLParams.new(:use_ruby_ciphers => jruby?()) hash = { :hosts => [ {:login => user, :passcode => passcode, :host => host, :port => ssl_port, :ssl => ssl_params}, ], :connect_headers => ch, :stompconn => get_stomp_conn(), :usecrlf => get_crlf(), } conn = Stomp::Connection.new(hash) conn end # Get a Stomp Client. def get_client() hash = { :hosts => [ {:login => user, :passcode => passcode, :host => host, :port => port}, ], :connect_headers => get_conn_headers(), :stompconn => get_stomp_conn(), :usecrlf => get_crlf(), } client = Stomp::Client.new(hash) client end # Get a connection headers hash. def get_conn_headers() ch = {} if ENV['STOMP_TEST11p'] # raise "Invalid 1.1 plus test protocol" if ENV['STOMP_TEST11p'] == Stomp::SPL_10 # if Stomp::SUPPORTED.index(ENV['STOMP_TEST11p']) ch['accept-version'] = ENV['STOMP_TEST11p'] else ch['accept-version'] = Stomp::SPL_11 # Just use 1.1 end # ch['host'] = ENV['STOMP_RABBIT'] ? "/" : host end ch end # Determine if tests should use STOMP instead of CONNECT def get_stomp_conn() usc = false usc = true if ENV['STOMP_TEST11p'] && Stomp::SUPPORTED.index(ENV['STOMP_TEST11p']) && ENV['STOMP_TEST11p'] >= Stomp::SPL_11 && ENV['STOMP_CONN'] usc end # Determine if tests should \r\n as line ends def get_crlf() ucr = false ucr = true if ENV['STOMP_TEST11p'] && Stomp::SUPPORTED.index(ENV['STOMP_TEST11p']) && ENV['STOMP_TEST11p'] >= Stomp::SPL_12 && ENV['STOMP_CRLF'] ucr end # Subscribe to a destination. def conn_subscribe(dest, headers = {}) if @conn.protocol >= Stomp::SPL_11 headers[:id] = @conn.uuid() unless headers[:id] end @conn.subscribe dest, headers end # Get a dynamic destination name. def make_destination name = caller_method_name unless name case when ENV['STOMP_DOTQUEUE'] qname = "/queue/test.ruby.stomp." + name when ENV['STOMP_ARTEMIS'] qname = "jms.queue.queue.test.ruby.stomp." + name else qname = "/queue/test/ruby/stomp/" + name end return qname end # get DLQ name def make_dlq return "jms.queue.DLQ" if ENV['STOMP_ARTEMIS'] "/queue/DLQ" end # def checkEmsg(cc) m = cc.poll if m assert m.command != Stomp::CMD_ERROR, "checkEmsg" end end # Check for JRuby before a connection exists def jruby?() jr = defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/ ? true : false return jr end # OK Data For Default Tests def dflt_data_ok() [ # { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => "localhost", :port => 61613, :ssl => false}, ], :reliable => false, }, # { :hosts => [ {:login => 'guest', :passcode => 'guest', :ssl => false}, ], :reliable => false, }, # { :hosts => [ {:login => 'guest', :passcode => 'guest', :port => 61613, :ssl => false}, ], :reliable => false, }, # { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => "localhost" , :ssl => false}, ], :reliable => false, }, # { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => '' , :ssl => false}, ], :reliable => false, }, ] end # Exception Data For Default Tests def dflt_data_ex() [ {}, {:hosts => 123}, { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => "localhost", :port => '' , :ssl => false}, ], :reliable => false, }, { :hosts => [ {:login => 'guest', :passcode => 'guest', :host => "localhost", :port => -1 , :ssl => false}, ], :reliable => false, }, ] end end stomp-1.4.4/test/.gitignore0000644000004100000410000000001613120662775015667 0ustar www-datawww-data# 1method.rb stomp-1.4.4/test/tlogger.rb0000644000004100000410000000373713120662775015704 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'logger' # use the standard Ruby logger ..... =begin Callback logger for Stomp 1.1+ heartbeat tests. See the examples directory for a more robust logger example. =end class Tlogger # Initialize a callback logger class. def initialize(init_parms = nil) puts @log = Logger::new(STDOUT) # User preference @log.level = Logger::DEBUG # User preference @log.info("Logger initialization complete.") end # Log miscellaneous errors. def on_miscerr(parms, errstr) begin @log.debug "Miscellaneous Error #{info(parms)}" @log.debug "Miscellaneous Error String #{errstr}" rescue @log.debug "Miscellaneous Error oops" end end # Stomp 1.1+ - heart beat send (transmit) failed def on_hbwrite_fail(parms, ticker_data) begin @log.debug "Hbwritef Parms #{info(parms)}" @log.debug "Hbwritef Result #{ticker_data.inspect}" rescue @log.debug "Hbwritef oops" end end # Stomp 1.1+ - heart beat read (receive) failed def on_hbread_fail(parms, ticker_data) begin @log.debug "Hbreadf Parms #{info(parms)}" @log.debug "Hbreadf Result #{ticker_data.inspect}" rescue @log.debug "Hbreadf oops" end end # Stomp 1.1+ - heart beat thread fires def on_hbfire(parms, type, firedata) begin @log.debug "HBfire #{type} " + "=" * 30 @log.debug "HBfire #{type} Parms #{info(parms)}" @log.debug "HBfire #{type} Firedata #{firedata.inspect}" rescue @log.debug "HBfire #{type} oops" end end private def info(parms) # # Available in the parms Hash: # parms[:cur_host] # parms[:cur_port] # parms[:cur_login] # parms[:cur_passcode] # parms[:cur_ssl] # parms[:cur_recondelay] # parms[:cur_parseto] # parms[:cur_conattempts] # parms[:openstat] # "Host: #{parms[:cur_host]}, Port: #{parms[:cur_port]}, Login: #{parms[:cur_login]}, Passcode: #{parms[:cur_passcode]}" end end # of class stomp-1.4.4/test/test_connection.rb0000644000004100000410000004125713120662775017436 0ustar www-datawww-data# -*- encoding: utf-8 -*- if Kernel.respond_to?(:require_relative) require_relative("test_helper") else $:.unshift(File.dirname(__FILE__)) require 'test_helper' end =begin Main class for testing Stomp::Connection instances. =end class TestConnection < Test::Unit::TestCase include TestBase def setup @conn = get_connection() # Data for multi_thread tests @max_threads = 20 @max_msgs = 100 end def teardown @conn.disconnect if @conn.open? # allow tests to disconnect end # Test basic connection creation. def test_connection_exists assert_not_nil @conn end # Test asynchronous polling. def test_poll_async sq = ENV['STOMP_ARTEMIS'] ? "jms.queue.queue.do.not.put.messages.on.this.queue" : "/queue/do.not.put.messages.on.this.queue" @conn.subscribe(sq, :id => "a.no.messages.queue") # If the test 'hangs' here, Connection#poll is broken. m = @conn.poll assert m.nil? end # Test suppression of content length header. def test_no_length conn_subscribe make_destination # @conn.publish make_destination, "test_stomp#test_no_length", { :suppress_content_length => true } msg = @conn.receive assert_equal "test_stomp#test_no_length", msg.body # @conn.publish make_destination, "test_stomp#test_\000_length", { :suppress_content_length => true } msg2 = @conn.receive assert_equal "test_stomp#test_", msg2.body checkEmsg(@conn) end unless ENV['STOMP_RABBIT'] # Test direct / explicit receive. def test_explicit_receive conn_subscribe make_destination @conn.publish make_destination, "test_stomp#test_explicit_receive" msg = @conn.receive assert_equal "test_stomp#test_explicit_receive", msg.body end # Test asking for a receipt. def test_receipt conn_subscribe make_destination, :receipt => "abc" msg = @conn.receive assert_equal "abc", msg.headers['receipt-id'] checkEmsg(@conn) end # Test asking for a receipt on disconnect. def test_disconnect_receipt @conn.disconnect :receipt => "abc123" assert_not_nil(@conn.disconnect_receipt, "should have a receipt") assert_equal(@conn.disconnect_receipt.headers['receipt-id'], "abc123", "receipt sent and received should match") end # Test ACKs for Stomp 1.0 def test_client_ack_with_symbol_10 if @conn.protocol != Stomp::SPL_10 assert true return end queue = make_destination() @conn.subscribe queue, :ack => :client @conn.publish queue, "test_stomp#test_client_ack_with_symbol_10" msg = @conn.receive # ACK has one required header, message-id, which must contain a value # matching the message-id for the MESSAGE being acknowledged. @conn.ack msg.headers['message-id'] checkEmsg(@conn) end # Test ACKs for Stomp 1.1 def test_client_ack_with_symbol_11 if @conn.protocol != Stomp::SPL_11 assert true return end sid = @conn.uuid() queue = make_destination() @conn.subscribe queue, :ack => :client, :id => sid @conn.publish queue, "test_stomp#test_client_ack_with_symbol_11" msg = @conn.receive # ACK has two REQUIRED headers: message-id, which MUST contain a value # matching the message-id for the MESSAGE being acknowledged and # subscription, which MUST be set to match the value of the subscription's # id header. @conn.ack msg.headers['message-id'], :subscription => msg.headers['subscription'] checkEmsg(@conn) end # Test ACKs for Stomp 1.2 def test_client_ack_with_symbol_12 if @conn.protocol != Stomp::SPL_12 assert true return end sid = @conn.uuid() queue = make_destination() @conn.subscribe queue, :ack => :client, :id => sid @conn.publish queue, "test_stomp#test_client_ack_with_symbol_11" msg = @conn.receive # The ACK frame MUST include an id header matching the ack header # of the MESSAGE being acknowledged. @conn.ack msg.headers['ack'] checkEmsg(@conn) end # Test a message with 0x00 embedded in the body. def test_embedded_null conn_subscribe make_destination @conn.publish make_destination, "a\0" msg = @conn.receive assert_equal "a\0" , msg.body checkEmsg(@conn) end # Test connection open checking. def test_connection_open? assert_equal true , @conn.open? @conn.disconnect assert_equal false, @conn.open? end # Test connection closed checking. def test_connection_closed? assert_equal false, @conn.closed? @conn.disconnect assert_equal true, @conn.closed? end # Test that methods detect a closed connection. def test_closed_checks_conn @conn.disconnect # assert_raise Stomp::Error::NoCurrentConnection do @conn.ack("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.begin("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.commit("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.abort("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do conn_subscribe("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.unsubscribe("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.publish("dummy_data","dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.unreceive("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do @conn.disconnect("dummy_data") end # assert_raise Stomp::Error::NoCurrentConnection do _ = @conn.receive end # assert_raise Stomp::Error::NoCurrentConnection do _ = @conn.poll end end # Test that we receive a Stomp::Message. def test_response_is_instance_of_message_class conn_subscribe make_destination @conn.publish make_destination, "a\0" msg = @conn.receive assert_instance_of Stomp::Message , msg checkEmsg(@conn) end # Test converting a Message to a string. def test_message_to_s conn_subscribe make_destination @conn.publish make_destination, "a\0" msg = @conn.receive assert_match(/^ max_sleep sleep sleep_incr end assert_equal @max_msgs, msg_ctr checkEmsg(@conn) end unless RUBY_ENGINE =~ /jruby/ # Test polling with multiple threads. def test_multi_thread_poll # lock = Mutex.new msg_ctr = 0 dest = make_destination # 1.upto(@max_threads) do |tnum| Thread.new(@conn) do |amq| while true received = amq.poll if received lock.synchronize do msg_ctr += 1 end # Simulate message processing sleep 0.05 else # Wait a bit for more work sleep 0.05 end end end end # conn_subscribe( dest ) 1.upto(@max_msgs) do |mnum| msg = Time.now.to_s + " #{mnum}" @conn.publish(dest, msg) end # max_sleep = (RUBY_VERSION =~ /1\.8\.6/) ? 30 : 5 max_sleep = 30 if RUBY_ENGINE =~ /mingw/ sleep_incr = 0.10 total_slept = 0 while true break if @max_msgs == msg_ctr total_slept += sleep_incr break if total_slept > max_sleep sleep sleep_incr end assert_equal @max_msgs, msg_ctr checkEmsg(@conn) end unless RUBY_ENGINE =~ /jruby/ # Test using a nil body. def test_nil_body dest = make_destination @conn.publish dest, nil conn_subscribe dest msg = @conn.receive assert_equal "", msg.body checkEmsg(@conn) end # Test transaction message sequencing. def test_transaction conn_subscribe make_destination @conn.begin "txA" @conn.publish make_destination, "txn message", 'transaction' => "txA" @conn.publish make_destination, "first message" msg = @conn.receive assert_equal "first message", msg.body @conn.commit "txA" msg = @conn.receive assert_equal "txn message", msg.body checkEmsg(@conn) end unless ENV['STOMP_ARTEMIS'] # See Artemis docs for 1.3, page 222 # Test duplicate subscriptions. def test_duplicate_subscription @conn.disconnect # not reliable @conn = Stomp::Connection.open(user, passcode, host, port, true) # reliable dest = make_destination conn_subscribe dest # assert_raise Stomp::Error::DuplicateSubscription do conn_subscribe dest end checkEmsg(@conn) end # Test nil 1.1 connection parameters. def test_nil_connparms @conn.disconnect # @conn = Stomp::Connection.open(user, passcode, host, port, false, 5, nil) checkEmsg(@conn) end # Basic NAK test. def test_nack11p_0010 if @conn.protocol == Stomp::SPL_10 assert_raise Stomp::Error::UnsupportedProtocolError do @conn.nack "dummy msg-id" end else dest = make_destination smsg = "test_stomp#test_nack01: #{Time.now.to_f}" @conn.publish dest, smsg # sid = @conn.uuid() @conn.subscribe dest, :ack => :client, :id => sid msg = @conn.receive assert_equal smsg, msg.body case @conn.protocol when Stomp::SPL_12 @conn.nack msg.headers["ack"] sleep 0.05 # Give racy brokers a chance to handle the last nack before unsubscribe @conn.unsubscribe dest, :id => sid else # Stomp::SPL_11 @conn.nack msg.headers["message-id"], :subscription => sid sleep 0.05 # Give racy brokers a chance to handle the last nack before unsubscribe @conn.unsubscribe dest, :id => sid end # phase 2 teardown() setup() sid = @conn.uuid() @conn.subscribe dest, :ack => :auto, :id => sid msg2 = @conn.receive assert_equal smsg, msg2.body checkEmsg(@conn) end end unless (ENV['STOMP_AMQ11'] || ENV['STOMP_ARTEMIS']) # Test to illustrate Issue #44. Prior to a fix for #44, these tests would # fail only when connecting to a pure STOMP 1.0 server that does not # return a 'version' header at all. def test_conn10_simple @conn.disconnect # vhost = ENV['STOMP_RABBIT'] ? "/" : host hash = { :hosts => [ {:login => user, :passcode => passcode, :host => host, :port => port, :ssl => false}, ], :connect_headers => {"accept-version" => "1.0", "host" => vhost}, :reliable => false, } c = nil c = Stomp::Connection.new(hash) c.disconnect if c # hash = { :hosts => [ {:login => user, :passcode => passcode, :host => host, :port => port, :ssl => false}, ], :connect_headers => {"accept-version" => "3.14159,1.0,12.0", "host" => vhost}, :reliable => false, } c = nil c = Stomp::Connection.new(hash) c.disconnect if c end # test JRuby detection def test_jruby_presence if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/ assert @conn.jruby else assert !@conn.jruby end end # Test that methods detect an empty header key. def test_empty_header_key # bad_headers = {"a" => "11", "" => "emptykey", :c => "ccc"} # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do @conn.ack("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do @conn.nack("dummy_data", bad_headers) end if @conn.protocol != Stomp::SPL_10 # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do @conn.begin("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do @conn.commit("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do @conn.abort("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do @conn.subscribe("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do @conn.unsubscribe("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do @conn.publish("dummy_data","dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderKey do @conn.disconnect(bad_headers) end end # Test that methods detect an empty header value. # STOMP 1.0 only. def test_empty_header_value if @conn.protocol != Stomp::SPL_10 assert true return end # bad_headers = {"a" => "11", "hdra" => "", :c => "ccc"} # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do @conn.ack("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do @conn.nack("dummy_data", bad_headers) end if @conn.protocol != Stomp::SPL_10 # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do @conn.begin("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do @conn.commit("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do @conn.abort("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do @conn.subscribe("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do @conn.unsubscribe("dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do @conn.publish("dummy_data","dummy_data", bad_headers) end # assert_raise Stomp::Error::ProtocolErrorEmptyHeaderValue do @conn.disconnect(bad_headers) end end # test issue99, OK values def test_con_iss99_ok return unless host() == "localhost" && port() == 61613 # ok_vals = dflt_data_ok() ok_vals.each do |hsv| conn = Stomp::Connection.new(hsv) conn.disconnect end end def test_conn_nodest_sub assert_raise Stomp::Error::DestinationRequired do @conn.subscribe(nil) end end def test_conn_nodest_unsub assert_raise Stomp::Error::DestinationRequired do @conn.unsubscribe(nil) end end def test_conn_nodest_pub assert_raise Stomp::Error::DestinationRequired do @conn.publish(nil, "msg") end end end stomp-1.4.4/stomp.gemspec0000644000004100000410000001030113120662775015425 0ustar www-datawww-data# Generated by jeweler # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- # stub: stomp 1.4.4 ruby lib Gem::Specification.new do |s| s.name = "stomp".freeze s.version = "1.4.4" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Brian McCallister".freeze, "Marius Mathiesen".freeze, "Thiago Morello".freeze, "Guy M. Allard".freeze] s.date = "2017-06-11" s.description = "Ruby client for the Stomp messaging protocol. Note that this gem is no longer supported on rubyforge.".freeze s.email = ["brianm@apache.org".freeze, "marius@stones.com".freeze, "morellon@gmail.com".freeze, "allard.guy.m@gmail.com".freeze] s.executables = ["catstomp".freeze, "stompcat".freeze] s.extra_rdoc_files = [ "LICENSE", "README.md" ] s.files = [ "CHANGELOG.md", "LICENSE", "README.md", "Rakefile", "adhoc/.gitignore", "adhoc/README.md", "adhoc/issue121_01.rb", "adhoc/issue121_01_conn.rb", "adhoc/issue121_02.rb", "adhoc/issue121_03.rb", "adhoc/payload_generator.rb", "adhoc/payload_generator_adhoctest.rb", "adhoc/stomp_adhoc_common.rb", "bin/catstomp", "bin/stompcat", "examples/amqdurasub.rb", "examples/artemis/artlogger.rb", "examples/artemis/cliwaiter_not_reliable.rb", "examples/artemis/cliwaiter_reliable.rb", "examples/client11_ex1.rb", "examples/client11_putget1.rb", "examples/conn11_ex1.rb", "examples/conn11_ex2.rb", "examples/conn11_hb1.rb", "examples/consume_file.rb", "examples/consumer.rb", "examples/contrib.sh", "examples/contributors.rb", "examples/examplogger.rb", "examples/get11conn_ex1.rb", "examples/get11conn_ex2.rb", "examples/lflogger.rb", "examples/logexamp.rb", "examples/logexamp_ssl.rb", "examples/publish_file.rb", "examples/publish_file_conn.rb", "examples/publisher.rb", "examples/put11conn_ex1.rb", "examples/putget11_rh1.rb", "examples/ssl_common.rb", "examples/ssl_ctxoptions.rb", "examples/ssl_newparm.rb", "examples/ssl_uc1.rb", "examples/ssl_uc1_ciphers.rb", "examples/ssl_uc2.rb", "examples/ssl_uc2_ciphers.rb", "examples/ssl_uc3.rb", "examples/ssl_uc3_ciphers.rb", "examples/ssl_uc4.rb", "examples/ssl_uc4_ciphers.rb", "examples/ssl_ucx_default_ciphers.rb", "examples/stomp11_common.rb", "examples/topic_consumer.rb", "examples/topic_publisher.rb", "lib/client/utils.rb", "lib/connection/heartbeats.rb", "lib/connection/netio.rb", "lib/connection/utf8.rb", "lib/connection/utils.rb", "lib/stomp.rb", "lib/stomp/client.rb", "lib/stomp/codec.rb", "lib/stomp/connection.rb", "lib/stomp/constants.rb", "lib/stomp/errors.rb", "lib/stomp/ext/hash.rb", "lib/stomp/message.rb", "lib/stomp/null_logger.rb", "lib/stomp/slogger.rb", "lib/stomp/sslparams.rb", "lib/stomp/version.rb", "notes/heartbeat_readme.txt", "spec/client_shared_examples.rb", "spec/client_spec.rb", "spec/connection_spec.rb", "spec/message_spec.rb", "spec/spec_helper.rb", "stomp.gemspec", "test/.gitignore", "test/MultiBrokerUnitTestExample.sh", "test/funcs.sh", "test/test_anonymous.rb", "test/test_client.rb", "test/test_codec.rb", "test/test_connection.rb", "test/test_connection1p.rb", "test/test_helper.rb", "test/test_message.rb", "test/test_ssl.rb", "test/test_urlogin.rb", "test/tlogger.rb", "test/unitst.sh" ] s.homepage = "https://github.com/stompgem/stomp".freeze s.licenses = ["Apache-2.0".freeze] s.rubygems_version = "2.6.11".freeze s.summary = "Ruby client for the Stomp messaging protocol".freeze if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q.freeze, [">= 2.14.1", "~> 2.14"]) else s.add_dependency(%q.freeze, [">= 2.14.1", "~> 2.14"]) end else s.add_dependency(%q.freeze, [">= 2.14.1", "~> 2.14"]) end end stomp-1.4.4/LICENSE0000644000004100000410000002613613120662775013740 0ustar www-datawww-data Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. stomp-1.4.4/CHANGELOG.md0000644000004100000410000003056613120662775014546 0ustar www-datawww-data# Stomp Gem Change Log ## 1.4.4 20170611 * Merge fix for issue 141. * Fix broken MatReconnectAttempts * Attempt to detect mismatched SSL port and params. * Issue 139, add ssl checks to receive timeout logic. * Eliminate assert_nothing_raised from tests. * Add Artemis suport to standard test bed. * Run JRuby tests more easily. ## 1.4.3 20160821 * Quick fix of install failures. Do not try to use install 1.4.2. ## 1.4.2 20160820 * Refine SSL examples. * Address issue #124 with additional RDOC. * spec for Stomp::Client - check that headers passed to connection contain required values as well as given custom and that given hash is not modified. * Stomp::Client now does not modify given headers hash * spec description enhancement. * fix build_subscription_id - symbol and string were mixed up. * STOMP_TESTSSL flag should enable all SSL tests. * Add a basic Gemfile. * Fix a memory leak in receipt implementation. * Add unit test helper script. ## 1.4.1 20160623 * Add call to #post_connection_check to normal SSL processing. This change further validates the name of the broker connected to. This change adds to the current SSL connection processing logic, and is **highly recommended**. In the case a client cannot tolerate this logic, it can be disabled by adding :ssl_post_conn_check => false to the connection hash. * Fix typo in SSL failure recovery processing. ## 1.4.0 20160608 * Connection parameter :parse_timeout now means IO:select wait time for socket reads. Consumer clients should see a significantly reduced memory footprint. If the default (5 seconds) is not used by your client, we suggest you test carefully. * Add example programs for sending / receiving large messages. * Changelog format is changed from .rdoc to .md. * README format is changed from .rdoc to .md. * README format change of contributor's list. * Add example utilities for generating the contributor's list. * Eliminate many warnings when using 'rake test', mostly from the 2.x Ruby series. * Get rakefile up to date. * Add the 'adhoc' subdirectory, an area for experiments and issue recreation code. ## 1.3.5 20160302 * Add AMQ specific durable topic example. * Output error to stderr only in logger is undefined. * Move README changelog lower. * Handle newline at start of receive buffer. * Use Timeout::timeout instead of deprecated kernel version. * If socket open on reconnect, close it before new open. * On misc_err, make error messages more readable. * Attempt to support both Rspec 2.14.1+ and 3.x. ## 1.3.4 20141202 * Change :start_timeout default to 0 (might break some clients) (#98). * Allow user set of SSLContext options (#105). * Allow user set of parm in SSLContext.new(parm) (#105). ## 1.3.3 20140810 * Do not attempt to write empty message bodies. * Explicity close ssl socket on connection timeout. * Fix incorrect behavior for empty header keys (#93) * Do not override explicit :reliable => false. * Fix client fail-over fails (#98) ## 1.3.2 20131208 * Anon tests assigned unique class name. * Fix TypeError on connect timeout with 1.8.x, 2.x. * Complete revert to previous logger logic. * start_timeout and tcp_nodelay parameters * SSL Fix, revert not setting default ciphers. * Copy hash params at init. * Fix ssl => true for Ruby 1.9.x and 2.x. * Expanded list of STOMP default SSL ciphers: * Do not change caller's :hosts array * Issue #78, again. * Clean up logger interfacing. * Fixes from RSpec testing ## 1.3.1 20131002 * Method calls to the logger object should check for that method first (#83) ## 1.3.0 20130930 * ERROR frames now raise an exception in the Stomp::Client thread(#73, #81) * Allow anonymous connections (#75) * Fix for subscription id handling in STOMP 1.1 (#78) * Added a NullLogger (#77) * Added :tcp_nodelay option (disable Nagle's algorithm) (#76) * Read receipt ids are now UUIDs * Added a :start_timeout parameter ## 1.2.16 20130812 * Stomp::Client's should expose connection's host params ## 1.2.15 20130809 * Add user-specified timeout for initial CONNECTED/ERROR frame read. * Eliminate dup Timeout::timeout in ssl connect * Add license information to gemspec (#69) ## 1.2.14 20130819 * Version bump (1.2.13 release had Stomp::Version of 1.1.12.) * Prevent dup subscription header on re-receive ## 1.2.13 20130817 * Issue #68, Stomp::Client#unreceive max_redeliveries off-by-one error ## 1.2.12 20130811 * Fix infinite loop when max reconn attempts is reached * Enhance JRuby support in tests * Issue #63, nil message on rapid AMQ restarts * Issue #63, fast spurious failovers with JRuby and AMQ * Issue #67, SSL SNI support (thanks Hiram) * Proper cleanup when not reliable adn EOF from broker * Remove extraneous privte declarations * Issue #65, allow non-word characters in login and passcode using stomp:// * Issue #66, allow a single broker in a failover URL ## 1.2.11 20130728 * Issue #60, timeout/hang under JRuby * More generally support JRuby use and testing * Issue #58, nil message in Client on AMQ shutdown * More robust RabbitMQ tests ## 1.2.10 20130708 * Issue #57, reconnect delays not honored if erroneous headers * Support fail overs when heartbeat send/receive fails * Update callback logger example ## 1.2.9 20130328 * Refactoring and documentation updates (glennr) * Fix test encoding for Ruby 2.0+ * Fixes to tests due to :suppress_content_length fix * Issue #50 Stomp::Client reconnects fail * Correctly honor :suppress_content_length with 1.1 (JP Hastings-Spital) * Fix reference to client.publish rather than client.send (JP Hastings-Spital) ## 1.2.8 20121228 * Fix inverted encode / decode logic (fairly major 1.1+ bug) * Enhance codec tests * Enhance Stomp 1.1+ tests ## 1.2.7 20121102 * Stomp 1.2 support (see http://stomp.github.com) * Reset reconnect_delay to default value upon successful reconnect * Enhance tests for Stomp 1.2 ## 1.2.6 20120913 * Provide ability to eliminate checks for closed in protocol methods * Cover ssl.connect with connection timeout parameter * If heartbeat send raises, do not reraise unless client requests that * Remove methods that invoke __send__ * Move internal methods to 'private' ## 1.2.5 20120804 * Issue #48 any forks with modifications will be affected! * Source code restructured into individual files * Common indentation used throughout the source * Many method comments have been added * See notes in source regarding making methods private in the next release * See notes in source regarding removal of methods in the next release * Include examples and tests in rdoc generated during install * Issue #47 socket is open during retries ## 1.2.4 20120625 * Add ability for client to request flush on write to the connection (Issue #45) * Add ability for client to retrieve heartbeat intervals and counters * Fix I/O errors with heartbeats and multithreaded clients (Issue #46) * Enhance tests for heartbeats * Correct typos and clarify comments in many examples ## 1.2.3 20120616 * Fix UnsupportedProtocol on connect to a 1.0 broker * Add Client#poll method * Add help to stompcat and catstomp * Allow password to be set for private SSL key * Update comments to reflect new repository URL * Reformat changelog dates to ISO8601 * Fix SSL connection failures using JRuby * Use symbols, not strings for all header keys * Add IPV6 to IPV4 failover for dual homed systems when requested ## 1.2.2 20120324 * Major performance improvement for read of messages without content-length header * Correct Stomp 1.1 failing test * Update sample code to reflect removal of 'send' * Add on_ssl_connectfail callback and allow clients to signal quit from the callback * Ensure that SSL certificates and SSL related files exist and are readable * Allow SSL file checks before connect using SSLParams.new(:fsck => true, ...) * Correct a test for Windows compatibility ## 1.2.1 20120313 * Robust SSL certificate support. See examples and: https://github.com/stompgem/stomp/wiki/extended-ssl-overview * Really remove the deprecated #send methods * Fix exception in Stomp 1.1 code when headers are frozen * Revert 245e734a0. See ce8335fb2f for details. Fixes broken Connection#poll. * Add reconnection attempts to callback logging. * Add SSL specific connection information to callback logging. ## 1.2.0 20111214 * Stomp 1.1 protocol support. A significant change. Please test existing 1.0 code well. See the examples directory for 1.1 examples. * Accept :reliable in a Stomp::Client connection hash * Add connect timeout with hashed parameters * Do not allow calls after close/disconnect * Enhance supported logger callbacks * Fix subscription id in find_listener * Start to bootstrap STOMP 1.1 support ## 1.1.10 20111107 * Fixes for JRuby support * Fix EOF error on disconnect * Refactoring and additional test * Set up tests for use of RabbitMQ ## 1.1.9 20110615 * Support wildcard destinations * Handle subscribe with string or symbol ID * Check for duplicate subscriptions in spec tests * Support AMQ and Apollo servers in uinit tests * Correct UTF-8 (Unicode) content-length calcualtion in Ruby 1.9 * Send of a nil body causes exception * Add optional callback logging. See the examples install directory, files logexamp.rb and slogger.rb * Correct date stamps in this file ## 1.1.8 20110316 * Set KEEPALIVE on connection socket options * Attempt to support JRuby more robustly (poll remains broken) * Switch to ruby supplied IO#ready? * Test enhancements for suppress_content_length header * Miscellaneous small documentation updates * Add parse_timeout parameter for use with hashed logins * Allow connection to hosts with a - (dash) in the host name * Add limit parameter to thread joins ## 1.1.7 20110109 * Binary parse of raw STOMP frame * Fix broken tests on Ruby 1.9.2 ## 1.1.6 20100610 * Fixed multi-thread app hanging ## 1.1.5 20100317 * Added publish method (send is now deprecated) * Changes on Rake File * Added original_destination header to unreceive * suppress content length header is send on the message for future handling (like unreceive) ## 1.1.4 20100121 * Added unreceive message method that sends the message back to its queue or to the dead letter queue, depending on the :max_redeliveries option, similar to a13m one. * Added environment variable option for running 'rake test' on any stomp server, using any port with any user. * Added suppress_content_length header option for ActiveMQ knowing it is a text message (see: http://juretta.com/log/2009/05/24/activemq-jms-stomp/) * Fixed some bugs with Ruby 1.9 (concatenate string + exception) * Major changes on message parsing feature * Fixed bug with old socket not being closed when using failover * Fixed broken poll method on Connection * Fixed broken close method on Client * Added connection_frame accessor * Added disconnect receipt ## 1.1.3 20091124 * Failover support * SSL support * Stomp::Connection and Stomp::Client accept a hash on their constructor ## 1.1 20090227 * Ruby 1.9 Support * Add support for connect_headers, to control the CONNECT command. * Refactored lib dir to separate concerns. * Better test coverage * General code cleanup. ## 1.0.6 20080805 * Whitespace cleanup * Refactored Rakefile and added stomp.gemspec for GitHub friendliness. * Added .gitignore file * Refactored layout of lib dir to separate concerns * Cleanup of initializers, and provide Client accessors for reading values (for testing) * Removed test/test_url_* files as they only differed from the test_client.rb in their setup. Super UnDry. Added URL tests to cover stomp URL as param. * Created initial RSpec specs which stub/mock objects and should not require a running Stomp server instance. ## v1.0.5 20070201 * better url parsing * git-svn-id: http://svn.codehaus.org/stomp/ruby/trunk@48 fd4e7336-3dff-0310-b68a-b6615a75f13b ## v1.0.4 20070115 * Allow URL style connections descriptors * git-svn-id: http://svn.codehaus.org/stomp/ruby/trunk@44 fd4e7336-3dff-0310-b68a-b6615a75f13b ## v1.0.3 20070114 * Additional fixes for reliable by Andrew Kuklewicz * git-svn-id: http://svn.codehaus.org/stomp/ruby/trunk@42 fd4e7336-3dff-0310-b68a-b6615a75f13b ## v1.0.2 20060922 * Moving ruby so we can tag it ;-) * git-svn-id: http://svn.codehaus.org/stomp/ruby/trunk@37 fd4e7336-3dff-0310-b68a-b6615a75f13b ## v1.0.1 20051217 * Increment version * git-svn-id: http://svn.codehaus.org/stomp/trunk/ruby@24 fd4e7336-3dff-0310-b68a-b6615a75f13b ## v1.0.0 20051015 * works in repl, getting messages in weird order or dupes in test, but unable to isolate so far #( * git-svn-id: http://svn.codehaus.org/stomp/trunk/ruby@20 fd4e7336-3dff-0310-b68a-b6615a75f13b stomp-1.4.4/README.md0000644000004100000410000004767513120662775014225 0ustar www-datawww-data# README * [Project Information](https://github.com/stompgem/stomp) ## Overview An implementation of the Stomp protocol for Ruby. See: * [STOMP 1.0, 1.1, and 1.2] (http://stomp.github.com/index.html) ## Hash Login Example Usage (**this is the recommended login technique**): ``` hash = { :hosts => [ # First connect is to remotehost1 {:login => "login1", :passcode => "passcode1", :host => "remotehost1", :port => 61612, :ssl => true}, # First failover connect is to remotehost2 {:login => "login2", :passcode => "passcode2", :host => "remotehost2", :port => 61613, :ssl => false}, ], # These are the default parameters and do not need to be set :reliable => true, # reliable (use failover) :initial_reconnect_delay => 0.01, # initial delay before reconnect (secs) :max_reconnect_delay => 30.0, # max delay before reconnect :use_exponential_back_off => true, # increase delay between reconnect attpempts :back_off_multiplier => 2, # next delay multiplier :max_reconnect_attempts => 0, # retry forever, use # for maximum attempts :randomize => false, # do not radomize hosts hash before reconnect :connect_timeout => 0, # Timeout for TCP/TLS connects, use # for max seconds :connect_headers => {}, # user supplied CONNECT headers (req'd for Stomp 1.1+) :parse_timeout => 5, # IO::select wait time on socket reads :logger => nil, # user suplied callback logger instance :dmh => false, # do not support multihomed IPV4 / IPV6 hosts during failover :closed_check => true, # check first if closed in each protocol method :hbser => false, # raise on heartbeat send exception :stompconn => false, # Use STOMP instead of CONNECT :usecrlf => false, # Use CRLF command and header line ends (1.2+) :max_hbread_fails => 0, # Max HB read fails before retry. 0 => never retry :max_hbrlck_fails => 0, # Max HB read lock obtain fails before retry. 0 => never retry :fast_hbs_adjust => 0.0, # Fast heartbeat senders sleep adjustment, seconds, needed ... # For fast heartbeat senders. 'fast' == YMMV. If not # correct for your environment, expect unnecessary fail overs :connread_timeout => 0, # Timeout during CONNECT for read of CONNECTED/ERROR, secs :tcp_nodelay => true, # Turns on the TCP_NODELAY socket option; disables Nagle's algorithm :start_timeout => 0, # Timeout around Stomp::Client initialization :sslctx_newparm => nil, # Param for SSLContext.new :ssl_post_conn_check => true, # Further verify broker identity } # for a client client = Stomp::Client.new(hash) # for a connection connection = Stomp::Connection.new(hash) ``` ### Positional Parameter Usage: ``` client = Stomp::Client.new("user", "pass", "localhost", 61613) client.publish("/queue/mine", "hello world!") client.subscribe("/queue/mine") do |msg| p msg end ``` ### Stomp URL Usage: A Stomp URL must begin with 'stomp://' and can be in one of the following forms: ``` stomp://host:port stomp://host.domain.tld:port stomp://login:passcode@host:port stomp://login:passcode@host.domain.tld:port # e.g. c = Stomp::Client.new(urlstring) ``` ### Failover + SSL Example URL Usage: ``` options = "initialReconnectDelay=5000&randomize=false&useExponentialBackOff=false" # remotehost1 uses SSL, remotehost2 doesn't client = Stomp::Client.new("failover:(stomp+ssl://login1:passcode1@remotehost1:61612,stomp://login2:passcode2@remotehost2:61613)?#{options}") client.publish("/queue/mine", "hello world!") client.subscribe("/queue/mine") do |msg| p msg end ``` ### New: See _CHANGELOG.rdoc_ for details. * Gem version 1.4.4. Miscellaneous fixes, see CHANGELOG.rdoc for details. * Gem version 1.4.3. Fix broken install. Do not try to install 1.4.2. * Gem version 1.4.2. Fix memory leak, and others !: see CHANGELOG.md for details. * Gem version 1.4.1. Important SSL changes !: see CHANGELOG.md for details. * Gem version 1.4.0. Note: Change sementics of :parse_timeout, see CHANGELOG.md for details. * Gem version 1.3.5. Miscellaneous fixes, see CHANGELOG.rdoc for details. * Gem version 1.3.4. Miscellaneous fixes, see CHANGELOG.rdoc for details. * Gem version 1.3.3. Miscellaneous fixes, see CHANGELOG.rdoc for details. * Gem version 1.3.2. Miscellaneous fixes, see changelog for details. * Gem version 1.3.1. Bugfix for logging. * Gem version 1.3.0. Added ERROR frame raising as exception, added anonymous connections, miscellaneous other fixes. For changes in older versions see CHANGELOG.rdoc for details. ### Historical Information: Up until March 2009 the project was maintained and primarily developed by Brian McCallister. ### Source Code and Project URLs: [Source Code and Project](https://github.com/stompgem/stomp) ### Stomp Protocol Information: [Protocol Information](http://stomp.github.com/index.html) #### Contributors (by first author date) #### Contribution information:
First Author Date (Commit Count) Name / E-mail
2005-08-26 (0023) brianm / brianm@fd4e7336-3dff-0310-b68a-b6615a75f13b
2006-03-16 (0005) jstrachan / jstrachan@fd4e7336-3dff-0310-b68a-b6615a75f13b
2006-04-19 (0001) chirino / chirino@fd4e7336-3dff-0310-b68a-b6615a75f13b
2007-05-09 (0003) kookster / kookster@fd4e7336-3dff-0310-b68a-b6615a75f13b
2008-05-08 (0016) Glenn Rempe / glenn@rempe.us
2009-02-03 (0001) Tony Garnock-Jones / tonyg@lshift.net
2009-02-09 (0003) Marius Mathiesen / marius.mathiesen@gmail.com
2009-02-13 (0004) Johan Sørensen / johan@johansorensen.com
2009-11-17 (0022) Thiago Morello / thiago.morello@locaweb.com.br
2009-11-22 (0001) unknown / katy@.(none)
2009-12-18 (0052) Thiago Morello / morello@queroinfra32.fabrica.locaweb.com.br
2009-12-25 (0387) gmallard / allard.guy.m@gmail.com
2010-01-07 (0007) Rafael Rosa / rafael.rosa@locaweb.com.br
2010-03-23 (0042) Guy M. Allard / allard.guy.m@gmail.com
2010-04-01 (0001) Dmytro Shteflyuk / kpumuk@kpumuk.info
2010-10-22 (0001) Neil Wilson / neil@aldur.co.uk
2011-02-09 (0001) Dinesh Majrekar / dinesh.majrekar@advantage-interactive.com
2011-04-15 (0002) Kiall Mac Innes / kiall@managedit.ie
2011-04-29 (0002) Rob Skaggs / rob@pivotal-it.com
2011-08-23 (0003) Tom May / tom@gist.com
2011-08-24 (0002) Thiago Morello / morellon@gmail.com
2011-09-11 (0003) Lucas Hills / info@lucashills.com
2011-11-20 (0002) Chris Needham / chrisn303@gmail.com
2011-12-11 (0003) R.I.Pienaar / rip@devco.net
2011-12-13 (0001) tworker / tworker@onyx.ove.com
2012-03-16 (0001) James Pearson / james@fearmediocrity.co.uk
2012-05-10 (0001) Tommy Bishop / bishop.thomas@gmail.com
2012-06-18 (0002) Jeremy Gailor / jeremy@infinitecube.com
2013-02-20 (0002) JP Hastings-Spital / jphastings@gmail.com
2013-03-14 (0003) glennr / glenn@siyelo.com
2013-07-29 (0021) Ian Smith / ian.smith@mylookout.com
2013-08-07 (0001) Hiram Chirino / hiram@hiramchirino.com
2013-08-15 (0005) Ian Smith / ian.smith@lookout.com
2013-08-22 (0007) Ian Smith / ismith@mit.edu
2013-09-26 (0001) Orazio Cotroneo / orazio@we7.com
2013-10-22 (0001) OrazioWE7 / orazio@we7.com
2014-03-13 (0001) Richard Clamp / richardc@unixbeard.net
2014-12-08 (0001) m4rCsi / m4rCsi@gmail.com
2015-09-05 (0001) Michael Klishin / michael@novemberain.com
2015-11-10 (0002) Patrick Sharp / psharp@numerex.com
2016-02-03 (0001) Wayne Robinson / wayne.robinson@gmail.com
2016-07-12 (0006) Nikolay Khasanov / nkhasanov@groupon.com
2016-07-16 (0006) GitHub / noreply@github.com
2016-06-02 (0001) Ryan Rosenblum / ryan.rosenblum@gmail.com
2016-08-17 (0002) Alexandre Moutot / a.moutot@alphalink.fr
2016-10-25 (0001) Raducu Deaconu / raducu.deaconu@visma.com
2017-03-23 (0001) Steve Traylen / steve.traylen@cern.ch
2017-06-01 (0001) Michael Smith / michael.smith@puppet.com