You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
5.5 KiB
Lua
210 lines
5.5 KiB
Lua
local socket = ngx and ngx.socket.tcp or require("socket").tcp
|
|
local cbson = require("cbson")
|
|
|
|
local opcodes = {
|
|
OP_REPLY = 1;
|
|
OP_MSG = 1000;
|
|
OP_UPDATE = 2001;
|
|
OP_INSERT = 2002;
|
|
RESERVED = 2003;
|
|
OP_QUERY = 2004;
|
|
OP_GET_MORE = 2005;
|
|
OP_DELETE = 2006;
|
|
OP_KILL_CURSORS = 2007;
|
|
}
|
|
|
|
local _M = {}
|
|
|
|
local mt = { __index = _M }
|
|
|
|
function _M.new(host, port, timeout)
|
|
local sock = socket()
|
|
if timeout then
|
|
sock:settimeout(timeout)
|
|
end
|
|
|
|
return setmetatable({
|
|
sock = sock;
|
|
host = host;
|
|
port = port;
|
|
_id = 0;
|
|
}, mt)
|
|
end
|
|
|
|
function _M.connect(self, host, port)
|
|
self.host = host or self.host
|
|
self.port = port or self.port
|
|
return self.sock:connect(self.host, self.port)
|
|
end
|
|
|
|
function _M.handshake(self)
|
|
if ngx then
|
|
self.sock:sslhandshake()
|
|
else
|
|
local ssl = require("ssl")
|
|
self.sock = ssl.wrap(self.sock, {mode = "client", protocol = "tlsv1_2"})
|
|
assert(self.sock)
|
|
self.sock:dohandshake()
|
|
end
|
|
end
|
|
|
|
function _M.close(self)
|
|
if ngx then
|
|
self.sock:setkeepalive()
|
|
else
|
|
self.sock:close()
|
|
end
|
|
end
|
|
|
|
function _M.get_reused_times(self)
|
|
if not self.sock then
|
|
return nil, "not initialized"
|
|
end
|
|
|
|
return self.sock:getreusedtimes()
|
|
end
|
|
|
|
function _M.settimeout(self, ms)
|
|
self.sock:settimeout(ms)
|
|
end
|
|
|
|
function _M.send(self, data)
|
|
return self.sock:send(data)
|
|
end
|
|
|
|
function _M.receive(self, pat)
|
|
return self.sock:receive(pat)
|
|
end
|
|
|
|
function _M._handle_reply(self)
|
|
local header = assert ( self.sock:receive ( 16 ) )
|
|
|
|
local length = cbson.raw_to_uint( string.sub(header , 1 , 4 ))
|
|
local r_id = cbson.raw_to_uint( string.sub(header , 5 , 8 ))
|
|
local r_to = cbson.raw_to_uint( string.sub(header , 9 , 12 ))
|
|
local opcode = cbson.raw_to_uint( string.sub(header , 13 , 16 ))
|
|
|
|
assert ( opcode == cbson.uint(opcodes.OP_REPLY ) )
|
|
assert ( r_to == cbson.uint(self._id) )
|
|
|
|
local data = assert ( self.sock:receive ( tostring(length-16 ) ) )
|
|
|
|
local flags = cbson.raw_to_uint( string.sub(data , 1 , 4 ))
|
|
local cursor_id = cbson.raw_to_uint( string.sub(data , 5 , 12 ))
|
|
local from = cbson.raw_to_uint( string.sub(data , 13 , 16 ))
|
|
local number = tonumber(tostring(cbson.raw_to_uint( string.sub(data , 17 , 20 ))))
|
|
|
|
local docs = string.sub(data , 21)
|
|
|
|
local pos = 1
|
|
local index = 0
|
|
local r_docs = {}
|
|
while index < number do
|
|
local bson_size = tonumber(tostring(cbson.raw_to_uint(docs:sub(pos, pos+3))))
|
|
|
|
local dt = docs:sub(pos,pos+bson_size-1) -- get bson data according to size
|
|
|
|
table.insert(r_docs, cbson.decode(dt))
|
|
|
|
pos = pos + bson_size
|
|
index = index + 1
|
|
end
|
|
|
|
return flags, cursor_id, from, number, r_docs
|
|
end
|
|
|
|
function _M._build_header(self, op, payload_size)
|
|
local size = cbson.uint_to_raw(cbson.uint(payload_size+16), 4)
|
|
local op = cbson.uint_to_raw(cbson.uint(op), 4)
|
|
self._id = self._id+1
|
|
local id = cbson.uint_to_raw(cbson.uint(self._id), 4)
|
|
local reply_to = "\0\0\0\0"
|
|
return size .. id .. reply_to .. op
|
|
end
|
|
|
|
function _M._query(self, collection, query, to_skip, to_return, selector, flags)
|
|
local flags = {
|
|
tailable = flags and flags.tailable and 1 or 0,
|
|
slaveok = flags and flags.slaveok and 1 or 0,
|
|
notimeout = flags and flags.notimeout and 1 or 0,
|
|
await = flags and flags.await and 1 or 0,
|
|
exhaust = flags and flags.exhaust and 1 or 0,
|
|
partial = flags and flags.partial and 1 or 0
|
|
}
|
|
|
|
local flagset = cbson.int_to_raw(
|
|
cbson.int(
|
|
2 * flags["tailable"] +
|
|
2^2 * flags["slaveok"] +
|
|
2^4 * flags["notimeout"] +
|
|
2^5 * flags["await"] +
|
|
2^6 * flags["exhaust"] +
|
|
2^7 * flags["partial"]
|
|
),
|
|
4)
|
|
|
|
local selector = selector and #selector and cbson.encode(selector) or ""
|
|
|
|
local to_skip = cbson.int_to_raw(cbson.int(to_skip), 4)
|
|
local to_return = cbson.int_to_raw(cbson.int(to_return), 4)
|
|
|
|
local size = 4 + #collection + 1 + 4 + 4 + #query + #selector
|
|
|
|
local header = self:_build_header(opcodes["OP_QUERY"], size)
|
|
|
|
local data = header .. flagset .. collection .. "\0" .. to_skip .. to_return .. query .. selector
|
|
|
|
assert(self:send(data))
|
|
return self:_handle_reply()
|
|
end
|
|
|
|
function _M._insert(self, collection, docs, flags)
|
|
local encoded_docs = {}
|
|
for k, doc in ipairs(docs) do
|
|
encoded_docs[k] = cbson.encode(doc)
|
|
end
|
|
string_docs = table.concat(encoded_docs)
|
|
|
|
local flags = {
|
|
continue_on_error = flags and flags.continue_on_error and 1 or 0
|
|
}
|
|
|
|
local flagset = cbson.int_to_raw(
|
|
cbson.int(
|
|
2 * flags["continue_on_error"]
|
|
),
|
|
4)
|
|
|
|
local size = 4 + 1 + #collection + #string_docs
|
|
local header = self:_build_header(opcodes["OP_INSERT"], size)
|
|
|
|
local data = header .. flagset .. collection .. "\0" .. string_docs
|
|
|
|
assert(self:send(data))
|
|
|
|
return true -- Mongo doesn't send a reply
|
|
end
|
|
|
|
function _M._kill_cursors(self, id)
|
|
local id = cbson.uint_to_raw(id, 8)
|
|
local num = cbson.int_to_raw(cbson.int(1), 4)
|
|
local zero = cbson.int_to_raw(cbson.int(0), 4)
|
|
local size = 8+4+4
|
|
local header = self:_build_header(opcodes["OP_KILL_CURSORS"], size)
|
|
local data = header .. zero .. num .. id
|
|
assert(self:send(data))
|
|
return true -- Mongo doesn't send a reply
|
|
end
|
|
|
|
function _M._get_more(self, collection, number, cursor)
|
|
local num = cbson.int_to_raw(cbson.int(number), 4)
|
|
local zero = cbson.int_to_raw(cbson.int(0), 4)
|
|
local cursor = cbson.uint_to_raw(cursor, 8)
|
|
local size = 4+#collection+1+4+8
|
|
local header = self:_build_header(opcodes["OP_GET_MORE"], size)
|
|
local data = header .. zero .. collection .. '\0' .. num .. cursor
|
|
assert(self:send(data))
|
|
return self:_handle_reply()
|
|
end
|
|
|
|
return _M |