123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- -- Copyright (C) 2013 Jiale Zhi (calio), Cloudflare Inc.
- -- See RFC6265 http://tools.ietf.org/search/rfc6265
- -- require "luacov"
- local type = type
- local byte = string.byte
- local sub = string.sub
- local format = string.format
- local log = ngx.log
- local ERR = ngx.ERR
- local ngx_header = ngx.header
- local EQUAL = byte("=")
- local SEMICOLON = byte(";")
- local SPACE = byte(" ")
- local HTAB = byte("\t")
- local ok, new_tab = pcall(require, "table.new")
- if not ok then
- new_tab = function (narr, nrec) return {} end
- end
- local ok, clear_tab = pcall(require, "table.clear")
- if not ok then
- clear_tab = function(tab) for k, _ in pairs(tab) do tab[k] = nil end end
- end
- local _M = new_tab(0, 2)
- _M._VERSION = '0.01'
- local mt = { __index = _M }
- local function get_cookie_table(text_cookie)
- if type(text_cookie) ~= "string" then
- log(ERR, format("expect text_cookie to be \"string\" but found %s",
- type(text_cookie)))
- return {}
- end
- local EXPECT_KEY = 1
- local EXPECT_VALUE = 2
- local EXPECT_SP = 3
- local n = 0
- local len = #text_cookie
- for i=1, len do
- if byte(text_cookie, i) == SEMICOLON then
- n = n + 1
- end
- end
- local cookie_table = new_tab(0, n + 1)
- local state = EXPECT_SP
- local i = 1
- local j = 1
- local key, value
- while j <= len do
- if state == EXPECT_KEY then
- if byte(text_cookie, j) == EQUAL then
- key = sub(text_cookie, i, j - 1)
- state = EXPECT_VALUE
- i = j + 1
- end
- elseif state == EXPECT_VALUE then
- if byte(text_cookie, j) == SEMICOLON
- or byte(text_cookie, j) == SPACE
- or byte(text_cookie, j) == HTAB
- then
- value = sub(text_cookie, i, j - 1)
- cookie_table[key] = value
- key, value = nil, nil
- state = EXPECT_SP
- i = j + 1
- end
- elseif state == EXPECT_SP then
- if byte(text_cookie, j) ~= SPACE
- and byte(text_cookie, j) ~= HTAB
- then
- state = EXPECT_KEY
- i = j
- j = j - 1
- end
- end
- j = j + 1
- end
- if key ~= nil and value == nil then
- cookie_table[key] = sub(text_cookie, i)
- end
- return cookie_table
- end
- function _M.new(self)
- local _cookie = ngx.var.http_cookie
- --if not _cookie then
- --return nil, "no cookie found in current request"
- --end
- return setmetatable({ _cookie = _cookie, set_cookie_table = new_tab(4, 0) },
- mt)
- end
- function _M.get(self, key)
- if not self._cookie then
- return nil, "no cookie found in the current request"
- end
- if self.cookie_table == nil then
- self.cookie_table = get_cookie_table(self._cookie)
- end
- return self.cookie_table[key]
- end
- function _M.get_all(self)
- local err
- if not self._cookie then
- return nil, "no cookie found in the current request"
- end
- if self.cookie_table == nil then
- self.cookie_table = get_cookie_table(self._cookie)
- end
- return self.cookie_table
- end
- local function bake(cookie)
- if not cookie.key or not cookie.value then
- return nil, 'missing cookie field "key" or "value"'
- end
- if cookie["max-age"] then
- cookie.max_age = cookie["max-age"]
- end
- local str = cookie.key .. "=" .. cookie.value
- .. (cookie.expires and "; Expires=" .. cookie.expires or "")
- .. (cookie.max_age and "; Max-Age=" .. cookie.max_age or "")
- .. (cookie.domain and "; Domain=" .. cookie.domain or "")
- .. (cookie.path and "; Path=" .. cookie.path or "")
- .. (cookie.secure and "; Secure" or "")
- .. (cookie.httponly and "; HttpOnly" or "")
- .. (cookie.extension and "; " .. cookie.extension or "")
- return str
- end
- function _M.set(self, cookie)
- local cookie_str, err = bake(cookie)
- if not cookie_str then
- return nil, err
- end
- local set_cookie = ngx_header['Set-Cookie']
- local set_cookie_type = type(set_cookie)
- local t = self.set_cookie_table
- clear_tab(t)
- if set_cookie_type == "string" then
- -- only one cookie has been setted
- if set_cookie ~= cookie_str then
- t[1] = set_cookie
- t[2] = cookie_str
- ngx_header['Set-Cookie'] = t
- end
- elseif set_cookie_type == "table" then
- -- more than one cookies has been setted
- local size = #set_cookie
- -- we can not set cookie like ngx.header['Set-Cookie'][3] = val
- -- so create a new table, copy all the values, and then set it back
- for i=1, size do
- t[i] = ngx_header['Set-Cookie'][i]
- if t[i] == cookie_str then
- -- new cookie is duplicated
- return true
- end
- end
- t[size + 1] = cookie_str
- ngx_header['Set-Cookie'] = t
- else
- -- no cookie has been setted
- ngx_header['Set-Cookie'] = cookie_str
- end
- return true
- end
- return _M
|