diff --git a/Changes.md b/Changes.md index ce9ca3c..bfbe74e 100755 --- a/Changes.md +++ b/Changes.md @@ -1,3 +1,13 @@ +### v0.3.2 2017.06.10 + +- 关于内置session插件的更改 + - 修复session过期时间bug + - 移除lua-resty-session依赖 + - 内置session插件替换为基于cookie的简单实现 + - 接口仍然保持与之前版本兼容 + - 关于session处理,仍然建议根据具体业务需求和安全考量自行实现 +- 支持URI中含有字符'-' + ### v0.3.1 2017.04.16 - 支持路由中包含`~`字符(from [@XadillaX](https://github.com/XadillaX)) diff --git a/bin/scaffold/generator.lua b/bin/scaffold/generator.lua index 5b5f59d..e09c1f6 100755 --- a/bin/scaffold/generator.lua +++ b/bin/scaffold/generator.lua @@ -259,6 +259,7 @@ local mw_cookie = require("lor.lib.middleware.cookie") local mw_session = require("lor.lib.middleware.session") app:use(mw_cookie()) app:use(mw_session({ + secret = "session aes secret which you should set", timeout = 3600 -- default session timeout is 3600 seconds })) @@ -291,10 +292,10 @@ app:erroruse(function(err, req, res, next) if string_find(req.headers["Accept"], "application/json") then res:status(500):json({ success = false, - msg = "500! unknown error." + msg = "500! internal error, please check the log." }) else - res:status(500):send("unknown error") + res:status(500):send("internal error, please check the log.") end end end) diff --git a/dist.ini b/dist.ini index 7fdf79b..2468355 100755 --- a/dist.ini +++ b/dist.ini @@ -1,10 +1,10 @@ name = lor abstract = A fast and minimalist web framework based on OpenResty. -version = 0.3.1 +version = 0.3.2 author = Sumory Wu (@sumory) is_original = yes license = mit repo_link = https://github.com/sumory/lor main_module = lib/lor/index.lua exclude_files = .travis, docker, docs, .travis.yml -requires = bungle/lua-resty-session >= 2.13, bungle/lua-resty-template >= 1.9, p0pr0ck5/lua-resty-cookie >= 0.01 +requires = bungle/lua-resty-template >= 1.9, p0pr0ck5/lua-resty-cookie >= 0.01 diff --git a/lib/lor/lib/middleware/init.lua b/lib/lor/lib/middleware/init.lua index 2c34239..3c4d1f4 100755 --- a/lib/lor/lib/middleware/init.lua +++ b/lib/lor/lib/middleware/init.lua @@ -2,19 +2,8 @@ local init_middleware = function(req, res, next) req.res = res req.next = next res.req = req - - -- if app and app:getconf("x-powered-by") then - -- if app.version then - -- res:setHeader('X-Powered-By', 'Lor Framework ' .. app.version) - -- else - -- - -- end - -- end - -- res:set_header('X-Powered-By', 'Lor Framework') res.locals = res.locals or {} - -- setmetatable(req, {__index = app.request}) - -- setmetatable(res, {__index = app.response}) next() end diff --git a/lib/lor/lib/middleware/params.lua b/lib/lor/lib/middleware/params.lua deleted file mode 100755 index a3613a7..0000000 --- a/lib/lor/lib/middleware/params.lua +++ /dev/null @@ -1,10 +0,0 @@ --- do something before handling every request -local params_middleware = function(req, res, next) - req.query = req.query - req.params = req.params - req.body = req.body - next() -end - - -return params_middleware \ No newline at end of file diff --git a/lib/lor/lib/middleware/session.lua b/lib/lor/lib/middleware/session.lua index 1558658..d9295d2 100755 --- a/lib/lor/lib/middleware/session.lua +++ b/lib/lor/lib/middleware/session.lua @@ -1,111 +1,172 @@ -local xpcall = xpcall +local type, xpcall = type, xpcall local traceback = debug.traceback +local http_time = ngx.http_time local ngx_time = ngx.time -local Session = require("resty.session") - --- Mind: --- base on 'lua-resty-session' --- this is the default `session` middleware which uses storage `cookie` --- you're recommended to define your own `session` middleware. --- you're strongly recommended to set your own session.secret - --- usage example: --- app:get("/session/set", function(req, res, next) --- local k = req.query.k --- local v = req.query.v --- if k then --- req.session.set(k,v) --- res:send("session saved: " .. k .. "->" .. v) --- else --- res:send("null session key") --- end --- end) --- --- app:get("/session/get/:key", function(req, res, next) --- local k = req.params.key --- if not k then --- res:send("please input session key") --- else --- res:send("session data: " .. req.session.get(k)) --- end --- end) --- --- app:get("/session/destroy", function(req, res, next) --- req.session.destroy() --- end) +local ck = require("resty.cookie") +local utils = require("lor.lib.utils.utils") +local aes = require("lor.lib.utils.aes") +local base64 = require("lor.lib.utils.base64") + + +local function decode_data(field, aes_key, ase_secret) + if not field or field == "" then return {} end + local payload = base64.decode(field) + local data = {} + local cipher = aes.new() + local decrypt_str = cipher:decrypt(payload, aes_key, ase_secret) + local decode_obj = utils.json_decode(decrypt_str) + return decode_obj or data +end + +local function encode_data(obj, aes_key, ase_secret) + local default = "{}" + local str = utils.json_encode(obj) or default + local cipher = aes.new() + local encrypt_str = cipher:encrypt(str, aes_key, ase_secret) + local encode_encrypt_str = base64.encode(encrypt_str) + return encode_encrypt_str +end + +local function parse_session(field, aes_key, ase_secret) + if not field then return end + return decode_data(field, aes_key, ase_secret) +end + +--- no much secure & performance consideration +--- TODO: optimization & security issues local session_middleware = function(config) config = config or {} + config.session_key = config.session_key or "_app_" if config.refresh_cookie ~= false then config.refresh_cookie = true end - if not config.timeout or type(config.timeout) ~= "number" then config.timeout = 3600 -- default session timeout is 3600 seconds end + + -- backward compatibility for lor < v0.3.2 + config.session_aes_key = config.session_aes_key or "custom_session_aes_key" + config.session_aes_secret = config.session_aes_secret or config.secret - if not config.secret then - config.secret = "7su3k78hjqw90fvj480fsdi934j7ery3n59ljf295d" + local err_tip = "session_aes_key and session_aes_secret should be set for session middleware" + if not config.session_aes_key or config.session_aes_key == "" + or not config.session_aes_secret or config.session_aes_secret == "" then + ngx.log(ngx.ERR, err_tip) end + local session_key = config.session_key + local session_aes_key = config.session_aes_key + local session_aes_secret = config.session_aes_secret + local refresh_cookie = config.refresh_cookie + local timeout = config.timeout + ngx.log(ngx.INFO, "session middleware initialized") + return function(req, res, next) - -- local config = config or {} - -- config.storage = config.storage or "cookie" -- default is “cookie” - -- local session = Session.new(config) + if not session_aes_key or not session_aes_secret then + return next(err_tip) + end + + local cookie, err = ck:new() + if not cookie then + ngx.log(ngx.ERR, "cookie is nil:", err) + end + + local current_session + local session_data, err = cookie:get(session_key) + if err then + ngx.log(ngx.ERR, "cannot get session_data:", err) + else + if session_data then + current_session = parse_session(session_data, session_aes_key, session_aes_secret) + end + end + current_session = current_session or {} + req.session = { - set = function(key, value) - local s = Session:open({ - secret = config.secret + set = function(...) + local p = ... + if type(p) == "table" then + for i, v in pairs(p) do + current_session[i] = v + end + else + local params = { ... } + if type(params[2]) == "table" then -- set("k", {1, 2, 3}) + current_session[params[1]] = params[2] + else -- set("k", "123") + current_session[params[1]] = params[2] or "" + end + end + + local value = encode_data(current_session, session_aes_key, session_aes_secret) + local expires = http_time(ngx_time() + timeout) + local max_age = timeout + local ok, err = cookie:set({ + key = session_key, + value = value or "", + expires = expires, + max_age = max_age, + path = "/" }) - s.data[key] = value + ngx.log(ngx.INFO, "session.set: ", value) - s.cookie.persistent = true - s.cookie.lifetime = config.timeout - s.expires = ngx_time() + config.timeout - s:save() + if err or not ok then + return ngx.log(ngx.ERR, "session.set error:", err) + end end, - update = function() - local s = Session:start({ - secret = config.secret - }) - - s.cookie.persistent = true - s.expires = ngx_time() + config.timeout - s.cookie.lifetime = config.timeout - s:save() + refresh = function() + if session_data and session_data ~= "" then + local expires = http_time(ngx_time() + timeout) + local max_age = timeout + local ok, err = cookie:set({ + key = session_key, + value = session_data or "", + expires = expires, + max_age = max_age, + path = "/" + }) + if err or not ok then + return ngx.log(ngx.ERR, "session.refresh error:", err) + end + end end, get = function(key) - local s = Session:open({ - secret = config.secret - }) - - s.cookie.persistent = true - s.cookie.lifetime = config.timeout - s.expires = ngx_time() + config.timeout - return s.data[key] + return current_session[key] end, destroy = function() - local s = Session.start({ - secret = config.secret + local expires = "Thu, 01 Jan 1970 00:00:01 GMT" + local max_age = 0 + local ok, err = cookie:set({ + key = session_key, + value = "", + expires = expires, + max_age = max_age, + path = "/" }) - s:destroy() + if err or not ok then + ngx.log(ngx.ERR, "session.destroy error:", err) + return false + end + + return true end } - local e, ok - ok = xpcall(function() - if config and config.refresh_cookie == true then - req.session.update() - end - end, function() - e = traceback() - end) + if refresh_cookie then + local e, ok + ok = xpcall(function() + req.session.refresh() + end, function() + e = traceback() + end) - if not ok then - ngx.log(ngx.ERR, "[session middleware]refresh cookie error, ", e) + if not ok then + ngx.log(ngx.ERR, "refresh cookie error:", e) + end end next() diff --git a/lib/lor/lib/response.lua b/lib/lor/lib/response.lua index 880ce9e..c69fac7 100755 --- a/lib/lor/lib/response.lua +++ b/lib/lor/lib/response.lua @@ -9,7 +9,6 @@ local Response = {} function Response:new() --ngx.status = 200 - local instance = { http_status = nil, headers = {}, diff --git a/resty/session/ciphers/aes.lua b/lib/lor/lib/utils/aes.lua old mode 100755 new mode 100644 similarity index 93% rename from resty/session/ciphers/aes.lua rename to lib/lor/lib/utils/aes.lua index f92b465..98456a1 --- a/resty/session/ciphers/aes.lua +++ b/lib/lor/lib/utils/aes.lua @@ -1,3 +1,4 @@ +-- from lua-resty-session local setmetatable = setmetatable local tonumber = tonumber local aes = require "resty.aes" @@ -33,7 +34,7 @@ local cipher = {} cipher.__index = cipher function cipher.new(config) - local a = config.aes or defaults + local a = config and config.aes or defaults return setmetatable({ size = CIPHER_SIZES[a.size or defaults.size] or 256, mode = CIPHER_MODES[a.mode or defaults.mode] or "cbc", @@ -50,4 +51,4 @@ function cipher:decrypt(d, k, s) return aes:new(k, s, cip(self.size, self.mode), self.hash, self.rounds):decrypt(d) end -return cipher \ No newline at end of file +return cipher diff --git a/resty/session/encoders/base64.lua b/lib/lor/lib/utils/base64.lua old mode 100755 new mode 100644 similarity index 96% rename from resty/session/encoders/base64.lua rename to lib/lor/lib/utils/base64.lua index c9e6098..d1872aa --- a/resty/session/encoders/base64.lua +++ b/lib/lor/lib/utils/base64.lua @@ -24,4 +24,4 @@ function base64.decode(value) return base64dec((value:gsub("[-_.]", DECODE_CHARS))) end -return base64 \ No newline at end of file +return base64 diff --git a/lib/lor/lib/utils/utils.lua b/lib/lor/lib/utils/utils.lua index a92a725..52ca1d5 100755 --- a/lib/lor/lib/utils/utils.lua +++ b/lib/lor/lib/utils/utils.lua @@ -68,7 +68,8 @@ end function _M.json_encode(data, empty_table_as_object) local json_value if json.encode_empty_table_as_object then - json.encode_empty_table_as_object(empty_table_as_object or false) -- empty table encoded as array default + -- empty table encoded as array default + json.encode_empty_table_as_object(empty_table_as_object or false) end if require("ffi").os ~= "Windows" then json.encode_sparse_array(true) diff --git a/lib/lor/lib/view.lua b/lib/lor/lib/view.lua index 45b5862..0090c4f 100755 --- a/lib/lor/lib/view.lua +++ b/lib/lor/lib/view.lua @@ -51,5 +51,4 @@ function View:render(view_file, data) end end - -return View +return View \ No newline at end of file diff --git a/lib/lor/lib/wrap.lua b/lib/lor/lib/wrap.lua index 70d10f0..255893d 100755 --- a/lib/lor/lib/wrap.lua +++ b/lib/lor/lib/wrap.lua @@ -19,7 +19,7 @@ function _M:new(create_app, Router, Group, Request, Response) return instance end --- Generally, this shouled only be used by `lor` framework itself. +-- Generally, this should only be used by `lor` framework itself. function _M:create_app(options) self.app = self.fn(options) return self.app diff --git a/lib/lor/version.lua b/lib/lor/version.lua index 0903b7b..edeacee 100755 --- a/lib/lor/version.lua +++ b/lib/lor/version.lua @@ -1 +1 @@ -return "0.3.1" +return "0.3.2" diff --git a/resty/session.lua b/resty/session.lua deleted file mode 100755 index 1691ac5..0000000 --- a/resty/session.lua +++ /dev/null @@ -1,313 +0,0 @@ -local require = require -local var = ngx.var -local header = ngx.header -local concat = table.concat -local hmac = ngx.hmac_sha1 -local time = ngx.time -local http_time = ngx.http_time -local find = string.find -local type = type -local pcall = pcall -local tonumber = tonumber -local setmetatable = setmetatable -local getmetatable = getmetatable -local random = require "resty.random".bytes - -local function enabled(val) - if val == nil then return nil end - return val == true or (val == "1" or val == "true" or val == "on") -end - -local function setcookie(session, value, expires) - if ngx.headers_sent then return nil, "Attempt to set session cookie after sending out response headers." end - local c = session.cookie - local i = 3 - local n = session.name .. "=" - local k = { n, value or "" } - local d = c.domain - local x = c.samesite - if expires then - k[i] = "; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0" - i=i+1 - elseif c.persistent then - k[i] = "; Expires=" - k[i+1] = http_time(session.expires) - k[i+2] = "; Max-Age=" - k[i+3] = c.lifetime - i=i+4 - end - if d and d ~= "localhost" and d ~= "" then - k[i] = "; Domain=" - k[i+1] = d - i=i+2 - end - k[i] = "; Path=" - k[i+1] = c.path or "/" - i=i+2 - if x == "Lax" or x == "Strict" then - k[i] = "; SameSite=" - k[i+1] = x - i=i+2 - end - if c.secure then - k[i] = "; Secure" - i=i+1 - end - if c.httponly then - k[i] = "; HttpOnly" - end - k = concat(k) - local s = header["Set-Cookie"] - local t = type(s) - if t == "table" then - local f = false - local z = #s - for i=1, z do - if find(s[i], n, 1, true) == 1 then - s[i] = k - f = true - break - end - end - if not f then - s[z+1] = k - end - elseif t == "string" and find(s, n, 1, true) ~= 1 then - s = { s, k } - else - s = k - end - header["Set-Cookie"] = s - return true -end - -local function save(session, close) - session.expires = time() + session.cookie.lifetime - local i, e, s = session.id, session.expires, session.storage - local k = hmac(session.secret, i .. e) - local d = session.serializer.serialize(session.data) - local h = hmac(k, concat{ i, e, d, session.key }) - local cookie, err = s:save(i, e, session.cipher:encrypt(d, k, i, session.key), h, close) - if cookie then - return setcookie(session, cookie) - end - return nil, err -end - -local function regenerate(session, flush) - local i = session.present and session.id - session.id = session:identifier() - if flush then - if i and session.storage.destroy then - session.storage:destroy(i); - end - session.data = {} - end -end - -local defaults = { - name = var.session_name or "session", - identifier = var.session_identifier or "random", - storage = var.session_storage or "cookie", - serializer = var.session_serializer or "json", - encoder = var.session_encoder or "base64", - cipher = var.session_cipher or "aes", - cookie = { - persistent = enabled(var.session_cookie_persistent or false), - renew = tonumber(var.session_cookie_renew) or 600, - lifetime = tonumber(var.session_cookie_lifetime) or 3600, - path = var.session_cookie_path or "/", - domain = var.session_cookie_domain, - samesite = var.session_cookie_samesite or "Lax", - secure = enabled(var.session_cookie_secure), - httponly = enabled(var.session_cookie_httponly or true), - delimiter = var.session_cookie_delimiter or "|" - }, check = { - ssi = enabled(var.session_check_ssi or false), - ua = enabled(var.session_check_ua or true), - scheme = enabled(var.session_check_scheme or true), - addr = enabled(var.session_check_addr or false) - }, - -} -defaults.secret = var.session_secret or random(32, true) or random(32) - -local session = { - _VERSION = "2.13" -} - -session.__index = session - -function session.new(opts) - if getmetatable(opts) == session then - return opts - end - local z = defaults - local y = opts or z - local a, b = y.cookie or z.cookie, z.cookie - local c, d = y.check or z.check, z.check - local e, f = y.cipher or z.cipher, z.cipher - local o, g = pcall(require, "resty.session.identifiers." .. (y.identifier or z.identifier)) - if not o then - g = require "resty.session.identifiers.random" - end - local o, h = pcall(require, "resty.session.storage." .. (y.storage or z.storage)) - if not o then - h = require "resty.session.storage.cookie" - end - local o, i = pcall(require, "resty.session.serializers." .. (y.serializer or z.serializer)) - if not o then - i = require "resty.session.serializers.json" - end - local o, j = pcall(require, "resty.session.encoders." .. (y.encoder or z.encoder)) - if not o then - j = require "resty.session.encoders.base64" - end - local o, k = pcall(require, "resty.session.ciphers." .. (e or f)) - if not o then - k = require "resty.session.ciphers.aes" - end - local self = { - name = y.name or z.name, - identifier = g, - serializer = i, - encoder = j, - data = y.data or {}, - secret = y.secret or z.secret, - cookie = { - persistent = a.persistent or b.persistent, - renew = a.renew or b.renew, - lifetime = a.lifetime or b.lifetime, - path = a.path or b.path, - domain = a.domain or b.domain, - samesite = a.samesite or b.samesite, - secure = a.secure or b.secure, - httponly = a.httponly or b.httponly, - delimiter = a.delimiter or b.delimiter - }, check = { - ssi = c.ssi or d.ssi, - ua = c.ua or d.ua, - scheme = c.scheme or d.scheme, - addr = c.addr or d.addr - } - } - self.storage = h.new(self) - self.cipher = k.new(self) - return setmetatable(self, session) -end - -function session.open(opts) - local self = opts - if getmetatable(self) == session then - if self.opened then - return self, self.present - end - else - self = session.new(opts) - end - local scheme = header["X-Forwarded-Proto"] - if self.cookie.secure == nil then - if scheme then - self.cookie.secure = scheme == "https" - else - self.cookie.secure = var.https == "on" - end - end - scheme = self.check.scheme and (scheme or var.scheme or "") or "" - local addr = "" - if self.check.addr then - addr = header["CF-Connecting-IP"] or - header["Fastly-Client-IP"] or - header["Incap-Client-IP"] or - header["X-Real-IP"] - if not addr then - addr = header["X-Forwarded-For"] - if addr then - -- We shouldn't really get the left-most address, because of spoofing, - -- but this is better handled with a module, like nginx realip module, - -- anyway (see also: http://goo.gl/Z6u2oR). - local s = find(addr, ',', 1, true) - if s then - addr = addr:sub(1, s - 1) - end - else - addr = var.remote_addr - end - end - end - self.key = concat{ - self.check.ssi and (var.ssl_session_id or "") or "", - self.check.ua and (var.http_user_agent or "") or "", - addr, - scheme - } - self.opened = true - local cookie = var["cookie_" .. self.name] - if cookie then - local i, e, d, h = self.storage:open(cookie, self.cookie.lifetime) - if i and e and e > time() and d and h then - local k = hmac(self.secret, i .. e) - d = self.cipher:decrypt(d, k, i, self.key) - if d and hmac(k, concat{ i, e, d, self.key }) == h then - d = self.serializer.deserialize(d) - self.id = i - self.expires = e - self.data = type(d) == "table" and d or {} - self.present = true - return self, true - end - end - end - regenerate(self, true) - return self, false -end - -function session.start(opts) - if getmetatable(opts) == session and opts.started then - return opts, opts.present - end - local self, present = session.open(opts) - if present then - if self.storage.start then - local ok, err = self.storage:start(self.id) - if not ok then return nil, err end - end - local now = time() - if self.expires - now < self.cookie.renew or - self.expires > now + self.cookie.lifetime then - local ok, err = save(self) - if not ok then return nil, err end - end - else - local ok, err = save(self) - if not ok then return nil, err end - end - self.started = true - return self, present -end - -function session:regenerate(flush) - regenerate(self, flush) - return save(self) -end - -function session:save(close) - if not self.id then - self.id = self:identifier() - end - return save(self, close ~= false) -end - -function session:destroy() - if self.storage.destroy then - self.storage:destroy(self.id) - end - self.data = {} - self.present = nil - self.opened = nil - self.started = nil - self.destroyed = true - return setcookie(self, "", true) -end - -return session \ No newline at end of file diff --git a/resty/session/ciphers/none.lua b/resty/session/ciphers/none.lua deleted file mode 100755 index ca6a07e..0000000 --- a/resty/session/ciphers/none.lua +++ /dev/null @@ -1,22 +0,0 @@ -local setmetatable = setmetatable -local singleton -local cipher = {} - -cipher.__index = cipher - -function cipher.new() - if singleton == nil then - singleton = setmetatable({}, cipher) - end - return singleton -end - -function cipher:encrypt(d) - return d -end - -function cipher:decrypt(d) - return d -end - -return cipher \ No newline at end of file diff --git a/resty/session/encoders/base16.lua b/resty/session/encoders/base16.lua deleted file mode 100755 index 789179d..0000000 --- a/resty/session/encoders/base16.lua +++ /dev/null @@ -1,25 +0,0 @@ -local tonumber = tonumber -local format = string.format -local gsub = string.gsub -local char = string.char -local byte = string.byte - -local function byt(c) - return format('%02x', byte(c or "")) -end - -local function chr(c) - return char(tonumber(c, 16) or 0) -end - -local base16 = {} - -function base16.encode(v) - return (gsub(v, ".", byt)) -end - -function base16.decode(v) - return (gsub(v, "..", chr)) -end - -return base16 \ No newline at end of file diff --git a/resty/session/encoders/hex.lua b/resty/session/encoders/hex.lua deleted file mode 100755 index 1b94a5a..0000000 --- a/resty/session/encoders/hex.lua +++ /dev/null @@ -1 +0,0 @@ -return require "resty.session.encoders.base16" \ No newline at end of file diff --git a/resty/session/identifiers/random.lua b/resty/session/identifiers/random.lua deleted file mode 100755 index fe79cfd..0000000 --- a/resty/session/identifiers/random.lua +++ /dev/null @@ -1,13 +0,0 @@ -local tonumber = tonumber -local random = require "resty.random".bytes -local var = ngx.var - -local defaults = { - length = tonumber(var.session_random_length) or 16 -} - -return function(config) - local c = config.random or defaults - local l = c.length or defaults.length - return random(l, true) or random(l) -end \ No newline at end of file diff --git a/resty/session/serializers/json.lua b/resty/session/serializers/json.lua deleted file mode 100755 index c857e02..0000000 --- a/resty/session/serializers/json.lua +++ /dev/null @@ -1,6 +0,0 @@ -local json = require "cjson.safe" - -return { - serialize = json.encode, - deserialize = json.decode -} \ No newline at end of file diff --git a/resty/session/storage/cookie.lua b/resty/session/storage/cookie.lua deleted file mode 100755 index 1ab38bf..0000000 --- a/resty/session/storage/cookie.lua +++ /dev/null @@ -1,47 +0,0 @@ -local concat = table.concat -local tonumber = tonumber -local setmetatable = setmetatable - -local cookie = {} - -cookie.__index = cookie - -function cookie.new(config) - return setmetatable({ - encode = config.encoder.encode, - decode = config.encoder.decode, - delimiter = config.cookie.delimiter - }, cookie) -end - -function cookie:cookie(c) - local r, d = {}, self.delimiter - local i, p, s, e = 1, 1, c:find(d, 1, true) - while s do - if i > 3 then - return nil - end - r[i] = c:sub(p, e - 1) - i, p = i + 1, e + 1 - s, e = c:find(d, p, true) - end - if i ~= 4 then - return nil - end - r[4] = c:sub(p) - return r -end - -function cookie:open(cookie) - local r = self:cookie(cookie) - if r and r[1] and r[2] and r[3] and r[4] then - return self.decode(r[1]), tonumber(r[2]), self.decode(r[3]), self.decode(r[4]) - end - return nil, "invalid" -end - -function cookie:save(i, e, d, h) - return concat({ self.encode(i), e, self.encode(d), self.encode(h) }, self.delimiter) -end - -return cookie \ No newline at end of file diff --git a/resty/session/storage/memcache.lua b/resty/session/storage/memcache.lua deleted file mode 100755 index f1a978b..0000000 --- a/resty/session/storage/memcache.lua +++ /dev/null @@ -1,219 +0,0 @@ -local memcached = require "resty.memcached" -local setmetatable = setmetatable -local tonumber = tonumber -local concat = table.concat -local floor = math.floor -local sleep = ngx.sleep -local null = ngx.null -local now = ngx.now -local var = ngx.var - -local function enabled(val) - if val == nil then return nil end - return val == true or (val == "1" or val == "true" or val == "on") -end - -local defaults = { - prefix = var.session_memcache_prefix or "sessions", - socket = var.session_memcache_socket, - host = var.session_memcache_host or "127.0.0.1", - port = tonumber(var.session_memcache_port) or 11211, - uselocking = enabled(var.session_memcache_uselocking or true), - spinlockwait = tonumber(var.session_memcache_spinlockwait) or 10000, - maxlockwait = tonumber(var.session_memcache_maxlockwait) or 30, - pool = { - timeout = tonumber(var.session_memcache_pool_timeout), - size = tonumber(var.session_memcache_pool_size) - } -} - -local memcache = {} - -memcache.__index = memcache - -function memcache.new(config) - local c = config.memcache or defaults - local p = c.pool or defaults.pool - local l = enabled(c.uselocking) - if l == nil then - l = defaults.uselocking - end - local self = { - memcache = memcached:new(), - encode = config.encoder.encode, - decode = config.encoder.decode, - delimiter = config.cookie.delimiter, - prefix = c.prefix or defaults.prefix, - uselocking = l, - spinlockwait = tonumber(c.spinlockwait) or defaults.spinlockwait, - maxlockwait = tonumber(c.maxlockwait) or defaults.maxlockwait, - pool = { - timeout = tonumber(p.timeout) or defaults.pool.timeout, - size = tonumber(p.size) or defaults.pool.size - } - } - local s = c.socket or defaults.socket - if s then - self.socket = s - else - self.host = c.host or defaults.host - self.port = c.port or defaults.port - end - return setmetatable(self, memcache) -end - -function memcache:connect() - local socket = self.socket - if socket then - return self.memcache:connect(socket) - end - return self.memcache:connect(self.host, self.port) -end - -function memcache:set_keepalive() - local pool = self.pool - local timeout, size = pool.timeout, pool.size - if timeout and size then - return self.memcache:set_keepalive(timeout, size) - end - if timeout then - return self.memcache:set_keepalive(timeout) - end - return self.memcache:set_keepalive() -end - -function memcache:key(i) - return concat({ self.prefix, self.encode(i) }, ":" ) -end - -function memcache:lock(k) - if not self.uselocking then - return true, nil - end - local s = self.spinlockwait - local m = self.maxlockwait - local w = s / 1000000 - local c = self.memcache - local i = 1000000 / s * m - local l = concat({ k, "lock" }, "." ) - for _ = 1, i do - local ok = c:add(l, "1", m + 1) - if ok then - return true, nil - end - sleep(w) - end - return false, "no lock" -end - -function memcache:unlock(k) - if self.uselocking then - return self.memcache:delete(concat({ k, "lock" }, "." )) - end - return true, nil -end - -function memcache:get(k) - local d = self.memcache:get(k) - return d ~= null and d or nil -end - -function memcache:set(k, d, l) - return self.memcache:set(k, d, l) -end - -function memcache:expire(k, l) - self.memcache:touch(k, l) -end - -function memcache:delete(k) - self.memcache:delete(k) -end - -function memcache:cookie(c) - local r, d = {}, self.delimiter - local i, p, s, e = 1, 1, c:find(d, 1, true) - while s do - if i > 2 then return end - r[i] = c:sub(p, e - 1) - i, p = i + 1, e + 1 - s, e = c:find(d, p, true) - end - if i ~= 3 then - return nil - end - r[3] = c:sub(p) - return r -end - -function memcache:open(cookie, lifetime) - local c = self:cookie(cookie) - if c and c[1] and c[2] and c[3] then - local ok, err = self:connect() - if ok then - local i, e, h = self.decode(c[1]), tonumber(c[2]), self.decode(c[3]) - local k = self:key(i) - ok, err = self:lock(k) - if ok then - local d = self:get(k) - if d then - self:expire(k, floor(lifetime)) - end - self:unlock(k) - self:set_keepalive() - return i, e, d, h - end - self:set_keepalive() - return nil, err - else - return nil, err - end - end - return nil, "invalid" -end - -function memcache:start(i) - local ok, err = self:connect() - if ok then - ok, err = self:lock(self:key(i)) - self:set_keepalive() - end - return ok, err -end - -function memcache:save(i, e, d, h, close) - local ok, err = self:connect() - if ok then - local l, k = floor(e - now()), self:key(i) - if l > 0 then - ok, err = self:set(k, d, l) - if close then - self:unlock(k) - end - self:set_keepalive() - if ok then - return concat({ self.encode(i), e, self.encode(h) }, self.delimiter) - end - return ok, err - end - if close then - self:unlock(k) - self:set_keepalive() - end - return nil, "expired" - end - return ok, err -end - -function memcache:destroy(i) - local ok, err = self:connect() - if ok then - local k = self:key(i) - self:delete(k) - self:unlock(k) - self:set_keepalive() - end - return ok, err -end - -return memcache \ No newline at end of file diff --git a/resty/session/storage/memcached.lua b/resty/session/storage/memcached.lua deleted file mode 100755 index 0ecc508..0000000 --- a/resty/session/storage/memcached.lua +++ /dev/null @@ -1 +0,0 @@ -return require "resty.session.storage.memcache" diff --git a/resty/session/storage/redis.lua b/resty/session/storage/redis.lua deleted file mode 100755 index 597f8e7..0000000 --- a/resty/session/storage/redis.lua +++ /dev/null @@ -1,232 +0,0 @@ -local red = require "resty.redis" -local setmetatable = setmetatable -local tonumber = tonumber -local concat = table.concat -local floor = math.floor -local sleep = ngx.sleep -local null = ngx.null -local now = ngx.now -local var = ngx.var - -local function enabled(val) - if val == nil then return nil end - return val == true or (val == "1" or val == "true" or val == "on") -end - -local defaults = { - prefix = var.session_redis_prefix or "sessions", - socket = var.session_redis_socket, - host = var.session_redis_host or "127.0.0.1", - port = tonumber(var.session_redis_port) or 6379, - auth = var.session_redis_auth, - uselocking = enabled(var.session_redis_uselocking or true), - spinlockwait = tonumber(var.session_redis_spinlockwait) or 10000, - maxlockwait = tonumber(var.session_redis_maxlockwait) or 30, - pool = { - timeout = tonumber(var.session_redis_pool_timeout), - size = tonumber(var.session_redis_pool_size) - } -} - -local redis = {} - -redis.__index = redis - -function redis.new(config) - local r = config.redis or defaults - local p = r.pool or defaults.pool - local l = enabled(r.uselocking) - if l == nil then - l = defaults.uselocking - end - local self = { - redis = red:new(), - auth = r.auth or defaults.auth, - encode = config.encoder.encode, - decode = config.encoder.decode, - delimiter = config.cookie.delimiter, - prefix = r.prefix or defaults.prefix, - uselocking = l, - spinlockwait = tonumber(r.spinlockwait) or defaults.spinlockwait, - maxlockwait = tonumber(r.maxlockwait) or defaults.maxlockwait, - pool = { - timeout = tonumber(p.timeout) or defaults.pool.timeout, - size = tonumber(p.size) or defaults.pool.size - } - } - local s = r.socket or defaults.socket - if s then - self.socket = s - else - self.host = r.host or defaults.host - self.port = r.port or defaults.port - end - return setmetatable(self, redis) -end - -function redis:connect() - local redis = self.redis - local ok, err - if self.socket then - ok, err = redis:connect(self.socket) - else - ok, err = redis:connect(self.host, self.port) - end - if ok and self.auth then - ok, err = redis:get_reused_times() - if ok == 0 then - ok, err = redis:auth(self.auth) - end - end - return ok, err -end - -function redis:set_keepalive() - local pool = self.pool - local timeout, size = pool.timeout, pool.size - if timeout and size then - return self.redis:set_keepalive(timeout, size) - end - if timeout then - return self.redis:set_keepalive(timeout) - end - return self.redis:set_keepalive() -end - -function redis:key(i) - return concat({ self.prefix, self.encode(i) }, ":" ) -end - -function redis:lock(k) - if not self.uselocking then - return true, nil - end - local s = self.spinlockwait - local m = self.maxlockwait - local w = s / 1000000 - local r = self.redis - local i = 1000000 / s * m - local l = concat({ k, "lock" }, "." ) - for _ = 1, i do - local ok = r:setnx(l, "1") - if ok then - return r:expire(l, m + 1) - end - sleep(w) - end - return false, "no lock" -end - -function redis:unlock(k) - if self.uselocking then - return self.redis:del(concat({ k, "lock" }, "." )) - end - return true, nil -end - -function redis:get(k) - local d = self.redis:get(k) - return d ~= null and d or nil -end - -function redis:set(k, d, l) - return self.redis:setex(k, l, d) -end - -function redis:expire(k, l) - self.redis:expire(k, l) -end - -function redis:delete(k) - self.redis:del(k) -end - -function redis:cookie(c) - local r, d = {}, self.delimiter - local i, p, s, e = 1, 1, c:find(d, 1, true) - while s do - if i > 2 then - return nil - end - r[i] = c:sub(p, e - 1) - i, p = i + 1, e + 1 - s, e = c:find(d, p, true) - end - if i ~= 3 then - return nil - end - r[3] = c:sub(p) - return r -end - -function redis:open(cookie, lifetime) - local c = self:cookie(cookie) - if c and c[1] and c[2] and c[3] then - local ok, err = self:connect() - if ok then - local i, e, h = self.decode(c[1]), tonumber(c[2]), self.decode(c[3]) - local k = self:key(i) - ok, err = self:lock(k) - if ok then - local d = self:get(k) - if d then - self:expire(k, floor(lifetime)) - end - self:unlock(k) - self:set_keepalive() - return i, e, d, h - end - self:set_keepalive() - return nil, err - else - return nil, err - end - end - return nil, "invalid" -end - -function redis:start(i) - local ok, err = self:connect() - if ok then - ok, err = self:lock(self:key(i)) - self:set_keepalive() - end - return ok, err -end - -function redis:save(i, e, d, h, close) - local ok, err = self:connect() - if ok then - local l, k = floor(e - now()), self:key(i) - if l > 0 then - ok, err = self:set(k, d, l) - if close then - self:unlock(k) - end - self:set_keepalive() - if ok then - return concat({ self.encode(i), e, self.encode(h) }, self.delimiter) - end - return ok, err - end - if close then - self:unlock(k) - self:set_keepalive() - end - return nil, "expired" - end - return ok, err -end - -function redis:destroy(i) - local ok, err = self:connect() - if ok then - local k = self:key(i) - self:delete(k) - self:unlock(k) - self:set_keepalive() - end - return ok, err -end - -return redis \ No newline at end of file diff --git a/resty/session/storage/shm.lua b/resty/session/storage/shm.lua deleted file mode 100755 index cc99ab9..0000000 --- a/resty/session/storage/shm.lua +++ /dev/null @@ -1,140 +0,0 @@ -local lock = require "resty.lock" -local setmetatable = setmetatable -local tonumber = tonumber -local concat = table.concat -local now = ngx.now -local var = ngx.var -local shared = ngx.shared - -local function enabled(val) - if val == nil then return nil end - return val == true or (val == "1" or val == "true" or val == "on") -end - -local defaults = { - store = var.session_shm_store or "sessions", - uselocking = enabled(var.session_shm_uselocking or true), - lock = { - exptime = tonumber(var.session_shm_lock_exptime) or 30, - timeout = tonumber(var.session_shm_lock_timeout) or 5, - step = tonumber(var.session_shm_lock_step) or 0.001, - ratio = tonumber(var.session_shm_lock_ratio) or 2, - max_step = tonumber(var.session_shm_lock_max_step) or 0.5, - } -} - -local shm = {} - -shm.__index = shm - -function shm.new(config) - local c = config.shm or defaults - local l = enabled(c.uselocking) - if l == nil then - l = defaults.uselocking - end - local m = c.store or defaults.store - local self = { - store = shared[m], - encode = config.encoder.encode, - decode = config.encoder.decode, - delimiter = config.cookie.delimiter, - uselocking = l - } - if l then - local x = c.lock or defaults.lock - local s = { - exptime = tonumber(x.exptime) or defaults.exptime, - timeout = tonumber(x.timeout) or defaults.timeout, - step = tonumber(x.step) or defaults.step, - ratio = tonumber(x.ratio) or defaults.ratio, - max_step = tonumber(x.max_step) or defaults.max_step - } - self.lock = lock:new(m, s) - end - return setmetatable(self, shm) -end - -function shm:key(i) - return self.encode(i) -end - -function shm:cookie(c) - local r, d = {}, self.delimiter - local i, p, s, e = 1, 1, c:find(d, 1, true) - while s do - if i > 2 then - return nil - end - r[i] = c:sub(p, e - 1) - i, p = i + 1, e + 1 - s, e = c:find(d, p, true) - end - if i ~= 3 then - return nil - end - r[3] = c:sub(p) - return r -end - -function shm:open(cookie, lifetime) - local r = self:cookie(cookie) - if r and r[1] and r[2] and r[3] then - local i, e, h = self.decode(r[1]), tonumber(r[2]), self.decode(r[3]) - local k = self:key(i) - if self.uselocking then - local l = self.lock - local ok, err = l:lock(concat{k, ".lock"}) - if ok then - local s = self.store - local d = s:get(k) - s:set(k, d, lifetime) - l:unlock() - return i, e, d, h - end - return nil, err - else - local s = self.store - local d = s:get(k) - s:set(k, d, lifetime) - return i, e, d, h - end - end - return nil, "invalid" -end - -function shm:start(i) - if self.uselocking then - return self.lock:lock(concat{self:key(i), ".lock"}) - end - return true, nil -end - -function shm:save(i, e, d, h, close) - local l = e - now() - if l > 0 then - local k = self:key(i) - local ok, err = self.store:set(k, d, l) - if self.uselocking and close then - self.lock:unlock() - end - if ok then - return concat({ k, e, self.encode(h) }, self.delimiter) - end - return nil, err - end - if self.uselocking and close then - self.lock:unlock() - end - return nil, "expired" -end - -function shm:destroy(i) - self.store:delete(self:key(i)) - if self.uselocking then - self.lock:unlock() - end - return true, nil -end - -return shm \ No newline at end of file