forked from wingify/lua-resty-rabbitmqstomp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rabbitmqstomp.lua
236 lines (174 loc) · 4.72 KB
/
rabbitmqstomp.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
-- lua-resty-rabbitmqstomp: Opinionated RabbitMQ (STOMP) client lib
-- Copyright (C) 2013 Rohit 'bhaisaab' Yadav, Wingify
-- Opensourced at Wingify in New Delhi under the MIT License
local byte = string.byte
local concat = table.concat
local error = error
local find = string.find
local gsub = string.gsub
local insert = table.insert
local len = string.len
local pairs = pairs
local setmetatable = setmetatable
local sub = string.sub
local tcp = ngx.socket.tcp
module(...)
_VERSION = "0.1"
local mt = { __index = _M }
local EOL = "\x0d\x0a"
local NULL_BYTE = "\x00"
local STATE_CONNECTED = 1
local STATE_COMMAND_SENT = 2
function new(self, opts)
local sock, err = tcp()
if not sock then
return nil, err
end
if opts == nil then
opts = {username = "guest", password = "guest", vhost = "/"}
end
return setmetatable({ sock = sock, opts = opts}, mt)
end
function set_timeout(self, timeout)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
return sock:settimeout(timeout)
end
function _build_frame(self, command, headers, body)
local frame = {command, EOL}
if body then
headers["content-length"] = len(body) + 4
end
for key, value in pairs(headers) do
insert(frame, key)
insert(frame, ":")
insert(frame, value)
insert(frame, EOL)
end
insert(frame, EOL)
if body then
insert(frame, body)
insert(frame, EOL)
insert(frame, EOL)
end
insert(frame, NULL_BYTE)
insert(frame, EOL)
return concat(frame, "")
end
function _send_frame(self, frame)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
return sock:send(frame)
end
function _receive_frame(self)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
local resp = sock:receiveuntil(NULL_BYTE, {inclusive = true})
local data, err, partial = resp()
return data, err
end
function _login(self)
local headers = {}
headers["accept-version"] = "1.2"
headers["login"] = self.opts.user
headers["passcode"] = self.opts.password
headers["host"] = self.opts.vhost
local ok, err = _send_frame(self, _build_frame(self, "CONNECT", headers, nil))
if not ok then
return nil, err
end
self.state = STATE_CONNECTED
return _receive_frame(self)
end
function _logout(self)
local sock = self.sock
if not sock then
self.state = nil
return nil, "not initialized"
end
if self.state == STATE_CONNECTED then
-- Graceful shutdown
local headers = {}
headers["receipt"] = "disconnect"
sock:send(_build_frame(self, "DISCONNECT", headers, nil))
sock:receive("*a")
end
self.state = nil
return sock:close()
end
function connect(self, ...)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
local ok, err = sock:connect(...)
if not ok then
return nil, "failed to connect: " .. err
end
local reused = sock:getreusedtimes()
if reused and reused > 0 then
self.state = STATE_CONNECTED
return 1
end
return _login(self)
end
function send(self, msg, headers)
local ok, err = _send_frame(self, _build_frame(self, "SEND", headers, msg))
if not ok then
return nil, err
end
if headers["receipt"] ~= nil then
return _receive_frame(self)
end
return ok, err
end
function subscribe(self, headers)
return _send_frame(self, _build_frame(self, "SUBSCRIBE", headers))
end
function unsubscribe(self, headers)
return _send_frame(self, _build_frame(self, "UNSUBSCRIBE", headers))
end
function receive(self)
local data, err = _receive_frame(self)
if not data then
return nil, err
end
data = gsub(data, EOL..EOL, "")
local idx = find(data, "\n\n", 1)
return sub(data, idx + 2)
end
function set_keepalive(self, ...)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
if self.state ~= STATE_CONNECTED then
return nil, "cannot be reused in the current connection state: "
.. (self.state or "nil")
end
self.state = nil
return sock:setkeepalive(...)
end
function get_reused_times(self)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
return sock:getreusedtimes()
end
function close(self)
return _logout(self)
end
local class_mt = {
-- to prevent use of casual module global variables
__newindex = function (table, key, val)
error('attempt to write to undeclared variable "' .. key .. '"')
end
}
setmetatable(_M, class_mt)