stompmq 0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
File without changes
data/lib/stompmq.rb ADDED
@@ -0,0 +1,2 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+ require 'stompmq/connection'
@@ -0,0 +1,139 @@
1
+ require 'socket'
2
+ require 'stompmq/frame'
3
+ require 'stompmq/message'
4
+
5
+ module StompMq
6
+ class InvalidFrameType < StandardError; end
7
+ class CorruptFrame < StandardError; end
8
+ class CorruptHeader < StandardError; end
9
+ class ProtocolSequenceViolation < StandardError; end
10
+ class StompErrorReceived < StandardError; end
11
+
12
+ # Represents a connection to a STOMP message broker.
13
+ class Connection
14
+ # Connect to a STOMP broker and log in.
15
+ def initialize(opts = { :broker => 'localhost', :port => 61613, :username => nil, :passcode => nil })
16
+ @buffer = ""
17
+ @broker = opts[:broker] || 'localhost'
18
+ @port = opts[:port] || 61613
19
+ @username = opts[:username]
20
+ @passcode = opts[:passcode]
21
+ @sock = TCPSocket.new @broker, @port
22
+ send_frame :connect, {:login => @username, :passcode => @passcode}
23
+ frame = receive_frame(15)
24
+ # some brokers add a \n after null-terminators, including after CONNECTED frames.
25
+ @buffer = ""
26
+ raise ProtocolSequenceViolation unless frame.cmd == 'CONNECTED'
27
+ self
28
+ end
29
+
30
+ # Subscribe to a STOMP queue.
31
+ def subscribe(opts = { :queue => nil, :selector => nil })
32
+ if opts[:selector]
33
+ send_frame :subscribe, {:destination => opts[:queue], :selector => opts[:selector]}
34
+ else
35
+ send_frame :subscribe, {:destination => opts[:queue]}
36
+ end
37
+ end
38
+
39
+ # Send a STOMP message to a queue.
40
+ def send_message(opts = { :queue => nil, :header => {}, :content => {} })
41
+ queue = opts[:queue]
42
+ content = opts[:content] || {}
43
+ header = opts[:header] || {}
44
+
45
+ header = {:destination => queue}.merge(header)
46
+ send_frame :send, header, content
47
+ end
48
+
49
+ # Receive a STOMP message.
50
+ def receive_message(timeout = nil)
51
+ frame = receive_frame(timeout)
52
+ return nil if !frame
53
+ raise StompErrorReceived.new(frame.content) if frame.cmd == 'ERROR'
54
+ raise ProtocolSequenceViolation unless frame.cmd == 'MESSAGE'
55
+ StompMq::Message.new(frame.header, frame.content)
56
+ end
57
+
58
+ private
59
+
60
+ # Transmit a standard STOMP frame.
61
+ def send_frame(function, header = {}, content = '')
62
+ frame = "#{function.to_s.upcase}\n"
63
+ header['content-length'] = content.to_s.length if content.to_s.include?("\000")
64
+ header.each do |key, val|
65
+ frame += "#{key.to_s}:#{val.to_s}\n"
66
+ end
67
+ frame += "\n"
68
+ frame += "#{content.to_s}\000"
69
+ @sock.write(frame)
70
+ end
71
+
72
+ # Receive a standard STOMP frame.
73
+ def receive_frame(timeout = nil)
74
+ frame = parse_frame
75
+ return frame unless frame.nil?
76
+ # FIXME: we'll violate the timeout given if half a packet comes in at first, then more later.
77
+ until frame do
78
+ return nil if receive_data(timeout) == nil
79
+ frame = parse_frame
80
+ end
81
+ frame
82
+ end
83
+
84
+ # Receive data waiting on a socket. If timeout is passed, block for this many seconds,
85
+ # otherwise don't block, return nil if no frame is waiting.
86
+ def receive_data(timeout = nil)
87
+ sock_ready = select([@sock], nil, nil, timeout)
88
+ return nil if !sock_ready
89
+ @buffer += @sock.recv(1024)
90
+ end
91
+
92
+ # Parse the instance buffer and pick out one frame. If one entire frame is not available
93
+ # in the buffer, returns nil.
94
+ def parse_frame
95
+ return nil if !@buffer["\000"]
96
+
97
+ cmd = header_text = nil
98
+ @buffer.lstrip!
99
+ raise CorruptFrame.new("No end to header found.") unless @buffer.include?("\n\n")
100
+ begin
101
+ cmd = @buffer.slice!(0..@buffer.index("\n")).chomp
102
+ header_text = @buffer.slice!(0..@buffer.index("\n\n")+1).chomp
103
+ rescue ArgumentError => e
104
+ raise CorruptFrame.new(e.message)
105
+ end
106
+ raise CorruptHeader.new("Found null in header.") if header_text.include?("\000")
107
+
108
+ # split the header text up into a proper hash
109
+ header = {}
110
+ header_text.split("\n").each do |keyval|
111
+ key,val = keyval.split(':', 2)
112
+ header[key] = val
113
+ end
114
+
115
+ # content runs until the first null, or per the content-length header if present.
116
+ content_length = @buffer.index("\000")
117
+ content_length = header['content-length'].to_i if header['content-length']
118
+ content = ''
119
+ if(@buffer.length) <= content_length then
120
+ # turns out we don't have a full frame after all! push the cmd/header back in.
121
+ @buffer = "#{cmd}\n#{header_text}\n#{@buffer}"
122
+ return nil
123
+ end
124
+ if(content_length > 0) then
125
+ content = @buffer[0..content_length-1]
126
+ @buffer[0..content_length] = ''
127
+ else
128
+ content = nil
129
+ @buffer[0] = ''
130
+ end
131
+
132
+ raise InvalidFrameType.new("Invalid command: '#{cmd}'") unless ['CONNECTED', 'RECEIPT', 'MESSAGE', 'ERROR'].include?(cmd)
133
+
134
+ return StompMq::Frame.new(cmd, header, content)
135
+ end
136
+
137
+ attr_accessor :buffer
138
+ end
139
+ end
@@ -0,0 +1,12 @@
1
+ module StompMq
2
+ # Holds a STOMP frame of any type.
3
+ class Frame
4
+ attr_reader :cmd, :header, :content
5
+
6
+ def initialize(cmd, header, content)
7
+ @cmd = cmd
8
+ @header = header
9
+ @content = content
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module StompMq
2
+ # Holds a STOMP message.
3
+ class Message
4
+ attr_reader :header, :content
5
+
6
+ def initialize(header, content)
7
+ @header = header
8
+ @content = content
9
+ end
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stompmq
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.5"
5
+ platform: ruby
6
+ authors:
7
+ - Sarah Nordstrom
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-09-13 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: sarahemm@sarahemm.net
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - lib/stompmq
26
+ - lib/stompmq/connection.rb
27
+ - lib/stompmq/frame.rb
28
+ - lib/stompmq/message.rb
29
+ - lib/stompmq.rb
30
+ - README
31
+ has_rdoc: true
32
+ homepage: http://www.rubyforge.org/stompmq/
33
+ post_install_message:
34
+ rdoc_options: []
35
+
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: "0"
43
+ version:
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ requirements: []
51
+
52
+ rubyforge_project: stompmq
53
+ rubygems_version: 1.0.1
54
+ signing_key:
55
+ specification_version: 2
56
+ summary: Support for the STOMP messaging protocol in Ruby.
57
+ test_files: []
58
+