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 +0 -0
- data/lib/stompmq.rb +2 -0
- data/lib/stompmq/connection.rb +139 -0
- data/lib/stompmq/frame.rb +12 -0
- data/lib/stompmq/message.rb +11 -0
- metadata +58 -0
data/README
ADDED
File without changes
|
data/lib/stompmq.rb
ADDED
@@ -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
|
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
|
+
|