-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathhandler.lua
More file actions
183 lines (151 loc) · 4.53 KB
/
handler.lua
File metadata and controls
183 lines (151 loc) · 4.53 KB
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
local BasePlugin = require "kong.plugins.base_plugin"
local CacheHandler = BasePlugin:extend()
local responses = require "kong.tools.responses"
local req_get_method = ngx.req.get_method
local redis = require "resty.redis"
local header_filter = require "kong.plugins.response-transformer.header_transformer"
local is_json_body = header_filter.is_json_body
local cjson_decode = require("cjson").decode
local cjson_encode = require("cjson").encode
local function cacheable_request(method, uri, conf)
if method ~= "GET" then
return false
end
for _,v in ipairs(conf.cache_policy.uris) do
if string.match(uri, "^"..v.."$") then
return true
end
end
return false
end
local function get_cache_key(uri, headers, query_params, conf)
local cache_key = uri
table.sort(query_params)
for _,param in ipairs(conf.cache_policy.vary_by_query_string_parameters) do
local query_value = query_params[param]
if query_value then
if type(query_value) == "table" then
table.sort(query_value)
query_value = table.concat(query_value, ",")
end
ngx.log(ngx.NOTICE, "varying cache key by query string ("..param..":"..query_value..")")
cache_key = cache_key..":"..param.."="..query_value
end
end
table.sort(headers)
for _,header in ipairs(conf.cache_policy.vary_by_headers) do
local header_value = headers[header]
if header_value then
if type(header_value) == "table" then
table.sort(header_value)
header_value = table.concat(header_value, ",")
end
ngx.log(ngx.NOTICE, "varying cache key by matched header ("..header..":"..header_value..")")
cache_key = cache_key..":"..header.."="..header_value
end
end
return cache_key
end
local function json_decode(json)
if json then
local status, res = pcall(cjson_decode, json)
if status then
return res
end
end
end
local function json_encode(table)
if table then
local status, res = pcall(cjson_encode, table)
if status then
return res
end
end
end
local function connect_to_redis(conf)
local red = redis:new()
red:set_timeout(conf.redis_timeout)
local ok, err = red:connect(conf.redis_host, conf.redis_port)
if err then
return nil, err
end
if conf.redis_password and conf.redis_password ~= "" then
local ok, err = red:auth(conf.redis_password)
if err then
return nil, err
end
end
return red
end
local function red_set(premature, key, val, conf)
local red, err = connect_to_redis(conf)
if err then
ngx_log(ngx.ERR, "failed to connect to Redis: ", err)
end
red:init_pipeline()
red:set(key, val)
if conf.cache_policy.duration_in_seconds then
red:expire(key, conf.cache_policy.duration_in_seconds)
end
local results, err = red:commit_pipeline()
if err then
ngx_log(ngx.ERR, "failed to commit the pipelined requests: ", err)
end
end
function CacheHandler:new()
CacheHandler.super.new(self, "response-cache")
end
function CacheHandler:access(conf)
CacheHandler.super.access(self)
local uri = ngx.var.uri
if not cacheable_request(req_get_method(), uri, conf) then
ngx.log(ngx.NOTICE, "not cacheable")
return
end
local cache_key = get_cache_key(uri, ngx.req.get_headers(), ngx.req.get_uri_args(), conf)
local red, err = connect_to_redis(conf)
if err then
ngx_log(ngx.ERR, "failed to connect to Redis: ", err)
return
end
local cached_val, err = red:get(cache_key)
if cached_val and cached_val ~= ngx.null then
ngx.log(ngx.NOTICE, "cache hit")
local val = json_decode(cached_val)
for k,v in pairs(val.headers) do
ngx.req.set_header(k, v)
end
return responses.send_HTTP_OK(val.content)
end
ngx.log(ngx.NOTICE, "cache miss")
ngx.ctx.response_cache = {
cache_key = cache_key
}
end
function CacheHandler:header_filter(conf)
CacheHandler.super.header_filter(self)
local ctx = ngx.ctx.response_cache
if not ctx then
return
end
ctx.headers = ngx.resp.get_headers()
end
function CacheHandler:body_filter(conf)
CacheHandler.super.body_filter(self)
local ctx = ngx.ctx.response_cache
if not ctx then
return
end
local chunk = ngx.arg[1]
local eof = ngx.arg[2]
local res_body = ctx and ctx.res_body or ""
res_body = res_body .. (chunk or "")
ctx.res_body = res_body
if eof then
local content = json_decode(ctx.res_body)
local value = { content = content, headers = ctx.headers }
local value_json = json_encode(value)
ngx.timer.at(0, red_set, ctx.cache_key, value_json, conf)
end
end
return CacheHandler