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
|
--- A minimalistic Redis (in-memory key-value data store) library.
--
-- @author "Patrik Karlsson <patrik@cqure.net>"
module(... or "redis", package.seeall)
local match = require 'match'
Request = {
new = function(self, cmd, ...)
local o = { cmd = cmd, args = {...} }
setmetatable (o,self)
self.__index = self
return o
end,
__tostring = function(self)
local output = ("*%s\r\n$%d\r\n%s\r\n"):format(#self.args + 1, #self.cmd, self.cmd)
for _, arg in ipairs(self.args) do
arg = tostring(arg)
output = output .. ("$%s\r\n%s\r\n"):format(#arg, arg)
end
return output
end
}
Response = {
Type = {
STATUS = 0,
ERROR = 1,
INTEGER = 2,
BULK = 3,
MULTIBULK = 4,
},
new = function(self, socket)
local o = { socket = socket }
setmetatable (o,self)
self.__index = self
return o
end,
receive = function(self)
local status, data = self.socket:receive_buf("\r\n")
if ( not(status) ) then
return false, "Failed to receive data from server"
end
-- if we have a status, integer or error message
if ( data:match("^[%-%+%:]") ) then
local response = { data = data }
local t = data:match("^([-+:])")
if ( t == "-" ) then
response.type = Response.Type.ERROR
elseif ( t == "+" ) then
response.type = Response.Type.STATUS
elseif ( t == ":" ) then
response.type = Response.Type.INTEGER
end
return true, response
end
-- process bulk reply
if ( data:match("^%$") ) then
-- non existing key
if ( data == "$-1" ) then
return true, nil
end
local len = tonumber(data:match("^%$(%d*)"))
-- we should only have a single line, so we can just peel of the length
status, data = self.socket:receive_buf(match.numbytes(len))
if( not(status) ) then
return false, "Failed to receive data from server"
end
return true, { data = data, type = Response.Type.BULK }
end
-- process multi-bulk reply
if ( data:match("^%*%d*") ) then
local count = data:match("^%*(%d*)")
local results = {}
for i=1, count do
-- peel of the length
local status = self.socket:receive_buf("\r\n")
if( not(status) ) then
return false, "Failed to receive data from server"
end
status, data = self.socket:receive_buf("\r\n")
if( not(status) ) then
return false, "Failed to receive data from server"
end
table.insert(results, data)
end
return true, { data = results, type = Response.Type.MULTIBULK }
end
return false, "Unsupported response"
end,
}
Helper = {
new = function(self, host, port)
local o = { host = host, port = port }
setmetatable (o,self)
self.__index = self
return o
end,
connect = function(self)
self.socket = nmap.new_socket()
return self.socket:connect(self.host, self.port)
end,
reqCmd = function(self, cmd, ...)
local req = Request:new(cmd, ...)
local status, err = self.socket:send(tostring(req))
if (not(status)) then
return false, "Failed to send command to server"
end
return Response:new(self.socket):receive()
end,
close = function(self)
return self.socket:close()
end
}
|