stompmq 0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README 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
+