diff options
Diffstat (limited to 'server/resty/session/storage')
-rw-r--r-- | server/resty/session/storage/cookie.lua | 7 | ||||
-rw-r--r-- | server/resty/session/storage/dshm.lua | 163 | ||||
-rw-r--r-- | server/resty/session/storage/memcache.lua | 303 | ||||
-rw-r--r-- | server/resty/session/storage/memcached.lua | 1 | ||||
-rw-r--r-- | server/resty/session/storage/redis.lua | 478 | ||||
-rw-r--r-- | server/resty/session/storage/shm.lua | 125 |
6 files changed, 0 insertions, 1077 deletions
diff --git a/server/resty/session/storage/cookie.lua b/server/resty/session/storage/cookie.lua deleted file mode 100644 index 95e26d1..0000000 --- a/server/resty/session/storage/cookie.lua +++ /dev/null @@ -1,7 +0,0 @@ -local storage = {} - -function storage.new() - return storage -end - -return storage diff --git a/server/resty/session/storage/dshm.lua b/server/resty/session/storage/dshm.lua deleted file mode 100644 index e6d887f..0000000 --- a/server/resty/session/storage/dshm.lua +++ /dev/null @@ -1,163 +0,0 @@ -local dshm = require "resty.dshm" - -local setmetatable = setmetatable -local tonumber = tonumber -local concat = table.concat -local var = ngx.var - -local defaults = { - region = var.session_dshm_region or "sessions", - connect_timeout = tonumber(var.session_dshm_connect_timeout, 10), - read_timeout = tonumber(var.session_dshm_read_timeout, 10), - send_timeout = tonumber(var.session_dshm_send_timeout, 10), - host = var.session_dshm_host or "127.0.0.1", - port = tonumber(var.session_dshm_port, 10) or 4321, - pool = { - name = var.session_dshm_pool_name, - size = tonumber(var.session_dshm_pool_size, 10) or 100, - timeout = tonumber(var.session_dshm_pool_timeout, 10) or 1000, - backlog = tonumber(var.session_dshm_pool_backlog, 10), - }, -} - -local storage = {} - -storage.__index = storage - -function storage.new(session) - local config = session.dshm or defaults - local pool = config.pool or defaults.pool - - local connect_timeout = tonumber(config.connect_timeout, 10) or defaults.connect_timeout - - local store = dshm:new() - if store.set_timeouts then - local send_timeout = tonumber(config.send_timeout, 10) or defaults.send_timeout - local read_timeout = tonumber(config.read_timeout, 10) or defaults.read_timeout - - if connect_timeout then - if send_timeout and read_timeout then - store:set_timeouts(connect_timeout, send_timeout, read_timeout) - else - store:set_timeout(connect_timeout) - end - end - - elseif store.set_timeout and connect_timeout then - store:set_timeout(connect_timeout) - end - - - local self = { - store = store, - encoder = session.encoder, - region = config.region or defaults.region, - host = config.host or defaults.host, - port = tonumber(config.port, 10) or defaults.port, - pool_timeout = tonumber(pool.timeout, 10) or defaults.pool.timeout, - connect_opts = { - pool = pool.name or defaults.pool.name, - pool_size = tonumber(pool.size, 10) or defaults.pool.size, - backlog = tonumber(pool.backlog, 10) or defaults.pool.backlog, - }, - } - - return setmetatable(self, storage) -end - -function storage:connect() - return self.store:connect(self.host, self.port, self.connect_opts) -end - -function storage:set_keepalive() - return self.store:set_keepalive(self.pool_timeout) -end - -function storage:key(id) - return concat({ self.region, id }, "::") -end - -function storage:set(key, ttl, data) - local ok, err = self:connect() - if not ok then - return nil, err - end - - data, err = self.encoder.encode(data) - - if not data then - self:set_keepalive() - return nil, err - end - - ok, err = self.store:set(key, data, ttl) - - self:set_keepalive() - - return ok, err -end - -function storage:get(key) - local ok, err = self:connect() - if not ok then - return nil, err - end - - local data - data, err = self.store:get(key) - if data then - data, err = self.encoder.decode(data) - end - - self:set_keepalive() - - return data, err -end - -function storage:delete(key) - local ok, err = self:connect() - if not ok then - return nil, err - end - - ok, err = self.store:delete(key) - - self:set_keepalive() - - return ok, err -end - -function storage:touch(key, ttl) - local ok, err = self:connect() - if not ok then - return nil, err - end - - ok, err = self.store:touch(key, ttl) - - self:set_keepalive() - - return ok, err -end - -function storage:open(id) - local key = self:key(id) - return self:get(key) -end - -function storage:save(id, ttl, data) - local key = self:key(id) - return self:set(key, ttl, data) -end - -function storage:destroy(id) - local key = self:key(id) - return self:delete(key) -end - -function storage:ttl(id, ttl) - local key = self:key(id) - return self:touch(key, ttl) -end - -return storage diff --git a/server/resty/session/storage/memcache.lua b/server/resty/session/storage/memcache.lua deleted file mode 100644 index da44ba7..0000000 --- a/server/resty/session/storage/memcache.lua +++ /dev/null @@ -1,303 +0,0 @@ -local memcached = require "resty.memcached" -local setmetatable = setmetatable -local tonumber = tonumber -local concat = table.concat -local sleep = ngx.sleep -local null = ngx.null -local var = ngx.var - -local function enabled(value) - if value == nil then - return nil - end - - return value == true - or value == "1" - or value == "true" - or value == "on" -end - -local function ifnil(value, default) - if value == nil then - return default - end - - return enabled(value) -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", - uselocking = enabled(var.session_memcache_uselocking or true), - connect_timeout = tonumber(var.session_memcache_connect_timeout, 10), - read_timeout = tonumber(var.session_memcache_read_timeout, 10), - send_timeout = tonumber(var.session_memcache_send_timeout, 10), - port = tonumber(var.session_memcache_port, 10) or 11211, - spinlockwait = tonumber(var.session_memcache_spinlockwait, 10) or 150, - maxlockwait = tonumber(var.session_memcache_maxlockwait, 10) or 30, - pool = { - name = var.session_memcache_pool_name, - timeout = tonumber(var.session_memcache_pool_timeout, 10), - size = tonumber(var.session_memcache_pool_size, 10), - backlog = tonumber(var.session_memcache_pool_backlog, 10), - }, -} - -local storage = {} - -storage.__index = storage - -function storage.new(session) - local config = session.memcache or defaults - local pool = config.pool or defaults.pool - local locking = ifnil(config.uselocking, defaults.uselocking) - - local connect_timeout = tonumber(config.connect_timeout, 10) or defaults.connect_timeout - - local memcache = memcached:new() - if memcache.set_timeouts then - local send_timeout = tonumber(config.send_timeout, 10) or defaults.send_timeout - local read_timeout = tonumber(config.read_timeout, 10) or defaults.read_timeout - - if connect_timeout then - if send_timeout and read_timeout then - memcache:set_timeouts(connect_timeout, send_timeout, read_timeout) - else - memcache:set_timeout(connect_timeout) - end - end - - elseif memcache.set_timeout and connect_timeout then - memcache:set_timeout(connect_timeout) - end - - local self = { - memcache = memcache, - prefix = config.prefix or defaults.prefix, - uselocking = locking, - spinlockwait = tonumber(config.spinlockwait, 10) or defaults.spinlockwait, - maxlockwait = tonumber(config.maxlockwait, 10) or defaults.maxlockwait, - pool_timeout = tonumber(pool.timeout, 10) or defaults.pool.timeout, - connect_opts = { - pool = pool.name or defaults.pool.name, - pool_size = tonumber(pool.size, 10) or defaults.pool.size, - backlog = tonumber(pool.backlog, 10) or defaults.pool.backlog, - }, - } - - local socket = config.socket or defaults.socket - if socket and socket ~= "" then - self.socket = socket - else - self.host = config.host or defaults.host - self.port = config.port or defaults.port - end - - return setmetatable(self, storage) -end - -function storage:connect() - local socket = self.socket - if socket then - return self.memcache:connect(socket, self.connect_opts) - end - return self.memcache:connect(self.host, self.port, self.connect_opts) -end - -function storage:set_keepalive() - return self.memcache:set_keepalive(self.pool_timeout) -end - -function storage:key(id) - return concat({ self.prefix, id }, ":" ) -end - -function storage:lock(key) - if not self.uselocking or self.locked then - return true - end - - if not self.token then - self.token = var.request_id - end - - local lock_key = concat({ key, "lock" }, "." ) - local lock_ttl = self.maxlockwait + 1 - local attempts = (1000 / self.spinlockwait) * self.maxlockwait - local waittime = self.spinlockwait / 1000 - - for _ = 1, attempts do - local ok = self.memcache:add(lock_key, self.token, lock_ttl) - if ok then - self.locked = true - return true - end - - sleep(waittime) - end - - return false, "unable to acquire a session lock" -end - -function storage:unlock(key) - if not self.uselocking or not self.locked then - return true - end - - local lock_key = concat({ key, "lock" }, "." ) - local token = self:get(lock_key) - - if token == self.token then - self.memcache:delete(lock_key) - self.locked = nil - end -end - -function storage:get(key) - local data, err = self.memcache:get(key) - if not data then - return nil, err - end - - if data == null then - return nil - end - - return data -end - -function storage:set(key, data, ttl) - return self.memcache:set(key, data, ttl) -end - -function storage:expire(key, ttl) - return self.memcache:touch(key, ttl) -end - -function storage:delete(key) - return self.memcache:delete(key) -end - -function storage:open(id, keep_lock) - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - ok, err = self:lock(key) - if not ok then - self:set_keepalive() - return nil, err - end - - local data - data, err = self:get(key) - - if err or not data or not keep_lock then - self:unlock(key) - end - - self:set_keepalive() - - return data, err -end - -function storage:start(id) - if not self.uselocking or not self.locked then - return true - end - - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - ok, err = self:lock(key) - - self:set_keepalive() - - return ok, err -end - -function storage:save(id, ttl, data, close) - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - ok, err = self:set(key, data, ttl) - - if close then - self:unlock(key) - end - - self:set_keepalive() - - if not ok then - return nil, err - end - - return true -end - -function storage:close(id) - if not self.uselocking or not self.locked then - return true - end - - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - self:unlock(key) - self:set_keepalive() - - return true -end - -function storage:destroy(id) - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - ok, err = self:delete(key) - - self:unlock(key) - self:set_keepalive() - - return ok, err -end - -function storage:ttl(id, ttl, close) - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - ok, err = self:expire(key, ttl) - - if close then - self:unlock(key) - end - - self:set_keepalive() - - return ok, err -end - -return storage diff --git a/server/resty/session/storage/memcached.lua b/server/resty/session/storage/memcached.lua deleted file mode 100644 index 0ecc508..0000000 --- a/server/resty/session/storage/memcached.lua +++ /dev/null @@ -1 +0,0 @@ -return require "resty.session.storage.memcache" diff --git a/server/resty/session/storage/redis.lua b/server/resty/session/storage/redis.lua deleted file mode 100644 index 3de0472..0000000 --- a/server/resty/session/storage/redis.lua +++ /dev/null @@ -1,478 +0,0 @@ -local setmetatable = setmetatable -local tonumber = tonumber -local type = type -local reverse = string.reverse -local gmatch = string.gmatch -local find = string.find -local byte = string.byte -local sub = string.sub -local concat = table.concat -local sleep = ngx.sleep -local null = ngx.null -local var = ngx.var - -local LB = byte("[") -local RB = byte("]") - -local function parse_cluster_nodes(nodes) - if not nodes or nodes == "" then - return nil - end - - if type(nodes) == "table" then - return nodes - end - - local addrs - local i - for node in gmatch(nodes, "%S+") do - local ip = node - local port = 6379 - local pos = find(reverse(ip), ":", 2, true) - if pos then - local p = tonumber(sub(ip, -pos + 1), 10) - if p >= 1 and p <= 65535 then - local addr = sub(ip, 1, -pos - 1) - if find(addr, ":", 1, true) then - if byte(addr, -1) == RB then - ip = addr - port = p - end - - else - ip = addr - port = p - end - end - end - - if byte(ip, 1, 1) == LB then - ip = sub(ip, 2) - end - - if byte(ip, -1) == RB then - ip = sub(ip, 1, -2) - end - - if not addrs then - i = 1 - addrs = {{ - ip = ip, - port = port, - }} - else - i = i + 1 - addrs[i] = { - ip = ip, - port = port, - } - end - end - - if not i then - return - end - - return addrs -end - -local redis_single = require "resty.redis" -local redis_cluster -do - local pcall = pcall - local require = require - local ok - ok, redis_cluster = pcall(require, "resty.rediscluster") - if not ok then - ok, redis_cluster = pcall(require, "rediscluster") - if not ok then - redis_cluster = nil - end - end -end - -local UNLOCK = [[ -if redis.call("GET", KEYS[1]) == ARGV[1] then - return redis.call("DEL", KEYS[1]) -else - return 0 -end -]] - -local function enabled(value) - if value == nil then return nil end - return value == true or (value == "1" or value == "true" or value == "on") -end - -local function ifnil(value, default) - if value == nil then - return default - end - - return enabled(value) -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", - username = var.session_redis_username, - password = var.session_redis_password or var.session_redis_auth, - server_name = var.session_redis_server_name, - ssl = enabled(var.session_redis_ssl) or false, - ssl_verify = enabled(var.session_redis_ssl_verify) or false, - uselocking = enabled(var.session_redis_uselocking or true), - port = tonumber(var.session_redis_port, 10) or 6379, - database = tonumber(var.session_redis_database, 10) or 0, - connect_timeout = tonumber(var.session_redis_connect_timeout, 10), - read_timeout = tonumber(var.session_redis_read_timeout, 10), - send_timeout = tonumber(var.session_redis_send_timeout, 10), - spinlockwait = tonumber(var.session_redis_spinlockwait, 10) or 150, - maxlockwait = tonumber(var.session_redis_maxlockwait, 10) or 30, - pool = { - name = var.session_redis_pool_name, - timeout = tonumber(var.session_redis_pool_timeout, 10), - size = tonumber(var.session_redis_pool_size, 10), - backlog = tonumber(var.session_redis_pool_backlog, 10), - }, -} - - -if redis_cluster then - defaults.cluster = { - name = var.session_redis_cluster_name, - dict = var.session_redis_cluster_dict, - maxredirections = tonumber(var.session_redis_cluster_maxredirections, 10), - nodes = parse_cluster_nodes(var.session_redis_cluster_nodes), - } -end - -local storage = {} - -storage.__index = storage - -function storage.new(session) - local config = session.redis or defaults - local pool = config.pool or defaults.pool - local cluster = config.cluster or defaults.cluster - local locking = ifnil(config.uselocking, defaults.uselocking) - - local self = { - prefix = config.prefix or defaults.prefix, - uselocking = locking, - spinlockwait = tonumber(config.spinlockwait, 10) or defaults.spinlockwait, - maxlockwait = tonumber(config.maxlockwait, 10) or defaults.maxlockwait, - } - - local username = config.username or defaults.username - if username == "" then - username = nil - end - local password = config.password or config.auth or defaults.password - if password == "" then - password = nil - end - - local connect_timeout = tonumber(config.connect_timeout, 10) or defaults.connect_timeout - - local cluster_nodes - if redis_cluster then - cluster_nodes = parse_cluster_nodes(cluster.nodes or defaults.cluster.nodes) - end - - local connect_opts = { - pool = pool.name or defaults.pool.name, - pool_size = tonumber(pool.size, 10) or defaults.pool.size, - backlog = tonumber(pool.backlog, 10) or defaults.pool.backlog, - server_name = config.server_name or defaults.server_name, - ssl = ifnil(config.ssl, defaults.ssl), - ssl_verify = ifnil(config.ssl_verify, defaults.ssl_verify), - } - - if cluster_nodes then - self.redis = redis_cluster:new({ - name = cluster.name or defaults.cluster.name, - dict_name = cluster.dict or defaults.cluster.dict, - username = var.session_redis_username, - password = var.session_redis_password or defaults.password, - connection_timout = connect_timeout, -- typo in library - connection_timeout = connect_timeout, - keepalive_timeout = tonumber(pool.timeout, 10) or defaults.pool.timeout, - keepalive_cons = tonumber(pool.size, 10) or defaults.pool.size, - max_redirection = tonumber(cluster.maxredirections, 10) or defaults.cluster.maxredirections, - serv_list = cluster_nodes, - connect_opts = connect_opts, - }) - self.cluster = true - - else - local redis = redis_single:new() - - if redis.set_timeouts then - local send_timeout = tonumber(config.send_timeout, 10) or defaults.send_timeout - local read_timeout = tonumber(config.read_timeout, 10) or defaults.read_timeout - - if connect_timeout then - if send_timeout and read_timeout then - redis:set_timeouts(connect_timeout, send_timeout, read_timeout) - else - redis:set_timeout(connect_timeout) - end - end - - elseif redis.set_timeout and connect_timeout then - redis:set_timeout(connect_timeout) - end - - self.redis = redis - self.username = username - self.password = password - self.database = tonumber(config.database, 10) or defaults.database - self.pool_timeout = tonumber(pool.timeout, 10) or defaults.pool.timeout - self.connect_opts = connect_opts - - local socket = config.socket or defaults.socket - if socket and socket ~= "" then - self.socket = socket - else - self.host = config.host or defaults.host - self.port = config.port or defaults.port - end - end - - return setmetatable(self, storage) -end - -function storage:connect() - if self.cluster then - return true -- cluster handles this on its own - end - - local ok, err - if self.socket then - ok, err = self.redis:connect(self.socket, self.connect_opts) - else - ok, err = self.redis:connect(self.host, self.port, self.connect_opts) - end - - if not ok then - return nil, err - end - - if self.password and self.redis:get_reused_times() == 0 then - -- usernames are supported only on Redis 6+, so use new AUTH form only when absolutely necessary - if self.username then - ok, err = self.redis:auth(self.username, self.password) - else - ok, err = self.redis:auth(self.password) - end - if not ok then - self.redis:close() - return nil, err - end - end - - if self.database ~= 0 then - ok, err = self.redis:select(self.database) - if not ok then - self.redis:close() - end - end - - return ok, err -end - -function storage:set_keepalive() - if self.cluster then - return true -- cluster handles this on its own - end - - return self.redis:set_keepalive(self.pool_timeout) -end - -function storage:key(id) - return concat({ self.prefix, id }, ":" ) -end - -function storage:lock(key) - if not self.uselocking or self.locked then - return true - end - - if not self.token then - self.token = var.request_id - end - - local lock_key = concat({ key, "lock" }, "." ) - local lock_ttl = self.maxlockwait + 1 - local attempts = (1000 / self.spinlockwait) * self.maxlockwait - local waittime = self.spinlockwait / 1000 - - for _ = 1, attempts do - local ok = self.redis:set(lock_key, self.token, "EX", lock_ttl, "NX") - if ok ~= null then - self.locked = true - return true - end - - sleep(waittime) - end - - return false, "unable to acquire a session lock" -end - -function storage:unlock(key) - if not self.uselocking or not self.locked then - return - end - - local lock_key = concat({ key, "lock" }, "." ) - - self.redis:eval(UNLOCK, 1, lock_key, self.token) - self.locked = nil -end - -function storage:get(key) - local data, err = self.redis:get(key) - if not data then - return nil, err - end - - if data == null then - return nil - end - - return data -end - -function storage:set(key, data, lifetime) - return self.redis:setex(key, lifetime, data) -end - -function storage:expire(key, lifetime) - return self.redis:expire(key, lifetime) -end - -function storage:delete(key) - return self.redis:del(key) -end - -function storage:open(id, keep_lock) - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - ok, err = self:lock(key) - if not ok then - self:set_keepalive() - return nil, err - end - - local data - data, err = self:get(key) - - if err or not data or not keep_lock then - self:unlock(key) - end - self:set_keepalive() - - return data, err -end - -function storage:start(id) - if not self.uselocking or not self.locked then - return true - end - - local ok, err = self:connect() - if not ok then - return nil, err - end - - ok, err = self:lock(self:key(id)) - - self:set_keepalive() - - return ok, err -end - -function storage:save(id, ttl, data, close) - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - ok, err = self:set(key, data, ttl) - - if close then - self:unlock(key) - end - - self:set_keepalive() - - if not ok then - return nil, err - end - - return true -end - -function storage:close(id) - if not self.uselocking or not self.locked then - return true - end - - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - self:unlock(key) - self:set_keepalive() - - return true -end - -function storage:destroy(id) - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - ok, err = self:delete(key) - - self:unlock(key) - self:set_keepalive() - - return ok, err -end - -function storage:ttl(id, ttl, close) - local ok, err = self:connect() - if not ok then - return nil, err - end - - local key = self:key(id) - - ok, err = self:expire(key, ttl) - - if close then - self:unlock(key) - end - - self:set_keepalive() - - return ok, err -end - -return storage diff --git a/server/resty/session/storage/shm.lua b/server/resty/session/storage/shm.lua deleted file mode 100644 index 6f81435..0000000 --- a/server/resty/session/storage/shm.lua +++ /dev/null @@ -1,125 +0,0 @@ -local lock = require "resty.lock" - -local setmetatable = setmetatable -local tonumber = tonumber -local concat = table.concat -local var = ngx.var -local shared = ngx.shared - -local function enabled(value) - if value == nil then return nil end - return value == true or (value == "1" or value == "true" or value == "on") -end - -local function ifnil(value, default) - if value == nil then - return default - end - - return enabled(value) -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, 10) or 30, - timeout = tonumber(var.session_shm_lock_timeout, 10) or 5, - step = tonumber(var.session_shm_lock_step, 10) or 0.001, - ratio = tonumber(var.session_shm_lock_ratio, 10) or 2, - max_step = tonumber(var.session_shm_lock_max_step, 10) or 0.5, - } -} - -local storage = {} - -storage.__index = storage - -function storage.new(session) - local config = session.shm or defaults - local store = config.store or defaults.store - local locking = ifnil(config.uselocking, defaults.uselocking) - - local self = { - store = shared[store], - uselocking = locking, - } - - if locking then - local lock_opts = config.lock or defaults.lock - local opts = { - exptime = tonumber(lock_opts.exptime, 10) or defaults.exptime, - timeout = tonumber(lock_opts.timeout, 10) or defaults.timeout, - step = tonumber(lock_opts.step, 10) or defaults.step, - ratio = tonumber(lock_opts.ratio, 10) or defaults.ratio, - max_step = tonumber(lock_opts.max_step, 10) or defaults.max_step, - } - self.lock = lock:new(store, opts) - end - - return setmetatable(self, storage) -end - -function storage:open(id, keep_lock) - if self.uselocking then - local ok, err = self.lock:lock(concat{ id, ".lock" }) - if not ok then - return nil, err - end - end - - local data, err = self.store:get(id) - - if self.uselocking and (err or not data or not keep_lock) then - self.lock:unlock() - end - - return data, err -end - -function storage:start(id) - if self.uselocking then - return self.lock:lock(concat{ id, ".lock" }) - end - - return true -end - -function storage:save(id, ttl, data, close) - local ok, err = self.store:set(id, data, ttl) - if close and self.uselocking then - self.lock:unlock() - end - - return ok, err -end - -function storage:close() - if self.uselocking then - self.lock:unlock() - end - - return true -end - -function storage:destroy(id) - self.store:delete(id) - - if self.uselocking then - self.lock:unlock() - end - - return true -end - -function storage:ttl(id, lifetime, close) - local ok, err = self.store:expire(id, lifetime) - - if close and self.uselocking then - self.lock:unlock() - end - - return ok, err -end - -return storage |