cookie.lua 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. -- Copyright (C) 2013 Jiale Zhi (calio), Cloudflare Inc.
  2. -- See RFC6265 http://tools.ietf.org/search/rfc6265
  3. -- require "luacov"
  4. local type = type
  5. local byte = string.byte
  6. local sub = string.sub
  7. local format = string.format
  8. local log = ngx.log
  9. local ERR = ngx.ERR
  10. local ngx_header = ngx.header
  11. local EQUAL = byte("=")
  12. local SEMICOLON = byte(";")
  13. local SPACE = byte(" ")
  14. local HTAB = byte("\t")
  15. local ok, new_tab = pcall(require, "table.new")
  16. if not ok then
  17. new_tab = function (narr, nrec) return {} end
  18. end
  19. local ok, clear_tab = pcall(require, "table.clear")
  20. if not ok then
  21. clear_tab = function(tab) for k, _ in pairs(tab) do tab[k] = nil end end
  22. end
  23. local _M = new_tab(0, 2)
  24. _M._VERSION = '0.01'
  25. local mt = { __index = _M }
  26. local function get_cookie_table(text_cookie)
  27. if type(text_cookie) ~= "string" then
  28. log(ERR, format("expect text_cookie to be \"string\" but found %s",
  29. type(text_cookie)))
  30. return {}
  31. end
  32. local EXPECT_KEY = 1
  33. local EXPECT_VALUE = 2
  34. local EXPECT_SP = 3
  35. local n = 0
  36. local len = #text_cookie
  37. for i=1, len do
  38. if byte(text_cookie, i) == SEMICOLON then
  39. n = n + 1
  40. end
  41. end
  42. local cookie_table = new_tab(0, n + 1)
  43. local state = EXPECT_SP
  44. local i = 1
  45. local j = 1
  46. local key, value
  47. while j <= len do
  48. if state == EXPECT_KEY then
  49. if byte(text_cookie, j) == EQUAL then
  50. key = sub(text_cookie, i, j - 1)
  51. state = EXPECT_VALUE
  52. i = j + 1
  53. end
  54. elseif state == EXPECT_VALUE then
  55. if byte(text_cookie, j) == SEMICOLON
  56. or byte(text_cookie, j) == SPACE
  57. or byte(text_cookie, j) == HTAB
  58. then
  59. value = sub(text_cookie, i, j - 1)
  60. cookie_table[key] = value
  61. key, value = nil, nil
  62. state = EXPECT_SP
  63. i = j + 1
  64. end
  65. elseif state == EXPECT_SP then
  66. if byte(text_cookie, j) ~= SPACE
  67. and byte(text_cookie, j) ~= HTAB
  68. then
  69. state = EXPECT_KEY
  70. i = j
  71. j = j - 1
  72. end
  73. end
  74. j = j + 1
  75. end
  76. if key ~= nil and value == nil then
  77. cookie_table[key] = sub(text_cookie, i)
  78. end
  79. return cookie_table
  80. end
  81. function _M.new(self)
  82. local _cookie = ngx.var.http_cookie
  83. --if not _cookie then
  84. --return nil, "no cookie found in current request"
  85. --end
  86. return setmetatable({ _cookie = _cookie, set_cookie_table = new_tab(4, 0) },
  87. mt)
  88. end
  89. function _M.get(self, key)
  90. if not self._cookie then
  91. return nil, "no cookie found in the current request"
  92. end
  93. if self.cookie_table == nil then
  94. self.cookie_table = get_cookie_table(self._cookie)
  95. end
  96. return self.cookie_table[key]
  97. end
  98. function _M.get_all(self)
  99. local err
  100. if not self._cookie then
  101. return nil, "no cookie found in the current request"
  102. end
  103. if self.cookie_table == nil then
  104. self.cookie_table = get_cookie_table(self._cookie)
  105. end
  106. return self.cookie_table
  107. end
  108. local function bake(cookie)
  109. if not cookie.key or not cookie.value then
  110. return nil, 'missing cookie field "key" or "value"'
  111. end
  112. if cookie["max-age"] then
  113. cookie.max_age = cookie["max-age"]
  114. end
  115. local str = cookie.key .. "=" .. cookie.value
  116. .. (cookie.expires and "; Expires=" .. cookie.expires or "")
  117. .. (cookie.max_age and "; Max-Age=" .. cookie.max_age or "")
  118. .. (cookie.domain and "; Domain=" .. cookie.domain or "")
  119. .. (cookie.path and "; Path=" .. cookie.path or "")
  120. .. (cookie.secure and "; Secure" or "")
  121. .. (cookie.httponly and "; HttpOnly" or "")
  122. .. (cookie.extension and "; " .. cookie.extension or "")
  123. return str
  124. end
  125. function _M.set(self, cookie)
  126. local cookie_str, err = bake(cookie)
  127. if not cookie_str then
  128. return nil, err
  129. end
  130. local set_cookie = ngx_header['Set-Cookie']
  131. local set_cookie_type = type(set_cookie)
  132. local t = self.set_cookie_table
  133. clear_tab(t)
  134. if set_cookie_type == "string" then
  135. -- only one cookie has been setted
  136. if set_cookie ~= cookie_str then
  137. t[1] = set_cookie
  138. t[2] = cookie_str
  139. ngx_header['Set-Cookie'] = t
  140. end
  141. elseif set_cookie_type == "table" then
  142. -- more than one cookies has been setted
  143. local size = #set_cookie
  144. -- we can not set cookie like ngx.header['Set-Cookie'][3] = val
  145. -- so create a new table, copy all the values, and then set it back
  146. for i=1, size do
  147. t[i] = ngx_header['Set-Cookie'][i]
  148. if t[i] == cookie_str then
  149. -- new cookie is duplicated
  150. return true
  151. end
  152. end
  153. t[size + 1] = cookie_str
  154. ngx_header['Set-Cookie'] = t
  155. else
  156. -- no cookie has been setted
  157. ngx_header['Set-Cookie'] = cookie_str
  158. end
  159. return true
  160. end
  161. return _M