Skip to content

Commit 31ce2e9

Browse files
committed
wip
1 parent 1648335 commit 31ce2e9

File tree

2 files changed

+398
-0
lines changed

2 files changed

+398
-0
lines changed
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
--- Headers policy
2+
-- This policy allows to include custom headers that will be sent to the
3+
-- upstream as well as delete headers included in the request that should not
4+
-- be sent to the upstream.
5+
-- Similarly, this policy also allows to add and delete headers of the
6+
-- response.
7+
8+
local ipairs = ipairs
9+
10+
local policy = require('apicast.policy')
11+
local _M = policy.new('Headers policy')
12+
13+
local new = _M.new
14+
15+
local function set_request_header(header, value)
16+
ngx.req.clear_header(header)
17+
18+
if value and value ~= '' then
19+
ngx.req.set_header(header, value)
20+
end
21+
end
22+
23+
local function add_request_header(header, value)
24+
local current_value = ngx.req.get_headers()[header]
25+
26+
local new_value
27+
if current_value then
28+
new_value = current_value .. ', ' .. value
29+
else
30+
new_value = value
31+
end
32+
33+
ngx.req.set_header(header, new_value)
34+
end
35+
36+
local function set_resp_header(header, value)
37+
ngx.header[header] = nil
38+
39+
if value and value ~= "" then
40+
ngx.header[header] = value
41+
end
42+
end
43+
44+
local function add_resp_header(header, value)
45+
local current_value = ngx.header[header]
46+
47+
local new_value
48+
if current_value then
49+
new_value = current_value .. ', ' .. value
50+
else
51+
new_value = value
52+
end
53+
54+
ngx.header[header] = new_value
55+
end
56+
57+
local command_functions = {
58+
request = { add = add_request_header, set = set_request_header },
59+
response = { add = add_resp_header, set = set_resp_header }
60+
}
61+
62+
-- header_type can be 'request' or 'response'
63+
local function run_commands(commands, header_type)
64+
for _, command in ipairs(commands) do
65+
command_functions[header_type][command.op](command.header, command.value)
66+
end
67+
end
68+
69+
-- Initialize the config so we do not have to check for nulls in the rest of
70+
-- the code
71+
local function init_config(config)
72+
local res = config or {}
73+
res.request = res.request or {}
74+
res.response = res.response or {}
75+
return res
76+
end
77+
78+
-- TODO: write documentation
79+
--- Initialize a Headers policy
80+
-- @tparam[opt] table config
81+
-- @field[opt] request
82+
-- @field[opt] response
83+
function _M.new(config)
84+
local self = new()
85+
self.config = init_config(config)
86+
return self
87+
end
88+
89+
function _M:rewrite()
90+
run_commands(self.config.request, 'request')
91+
end
92+
93+
function _M:header_filter()
94+
-- Clear the headers we set in the rewrite phase that were only set for the
95+
-- upstream.
96+
-- TODO
97+
98+
run_commands(self.config.response, 'response')
99+
end
100+
101+
return _M

t/apicast-policy-headers.t

+297
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
use lib 't';
2+
use TestAPIcastBlackbox 'no_plan';
3+
4+
repeat_each(1);
5+
run_tests();
6+
7+
__DATA__
8+
9+
TODO: Verify that all the headers added in the request are not received in the final response.
10+
11+
=== TEST 1: 'set' operation in request headers
12+
We test 3 things:
13+
1) Set op with a header that does not exist creates it with the given value.
14+
2) Set op with a header that exists, clears it and sets the given value.
15+
3) Set op with an empty value clears the header.
16+
--- configuration
17+
{
18+
"services": [
19+
{
20+
"id": 42,
21+
"backend_version": 1,
22+
"backend_authentication_type": "service_token",
23+
"backend_authentication_value": "token-value",
24+
"proxy": {
25+
"policy_chain": [
26+
{ "name": "apicast.policy.apicast" },
27+
{
28+
"name": "apicast.policy.headers",
29+
"configuration":
30+
{
31+
"request":
32+
[
33+
{ "op": "set", "header": "New-Header", "value": "config_value_nh" },
34+
{ "op": "set", "header": "Existing-Header", "value": "config_value_eh" },
35+
{ "op": "set", "header": "Header-To-Delete", "value": "" }
36+
]
37+
}
38+
}
39+
],
40+
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
41+
"proxy_rules": [
42+
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 }
43+
]
44+
}
45+
}
46+
]
47+
}
48+
--- backend
49+
location /transactions/authrep.xml {
50+
content_by_lua_block {
51+
local expected = "service_token=token-value&service_id=42&usage%5Bhits%5D=2&user_key=value"
52+
local args = ngx.var.args
53+
if args == expected then
54+
ngx.exit(200)
55+
else
56+
ngx.log(ngx.ERR, expected, ' did not match: ', args)
57+
ngx.exit(403)
58+
end
59+
}
60+
}
61+
--- upstream
62+
location / {
63+
content_by_lua_block {
64+
if ngx.req.get_headers()['New-Header'] ~= 'config_value_nh' then
65+
ngx.log(ngx.ERR, "The header 'New-Header' does not have the correct value")
66+
elseif ngx.req.get_headers()['Existing-Header'] ~= 'config_value_eh' then
67+
ngx.log(ngx.ERR, "The header 'Existing-Header' does not have the correct value")
68+
elseif ngx.req.get_headers()['Header-To-Delete'] then
69+
ngx.log(ngx.ERR, "Received unexpected 'Header-To-Delete' header")
70+
else
71+
ngx.say('yay, api backend');
72+
end
73+
}
74+
}
75+
--- request
76+
GET /?user_key=value
77+
Existing-Header: request_value_eh
78+
Header-To-Delete: request_value_htd
79+
--- response_body
80+
yay, api backend
81+
--- response_headers
82+
New-Header:
83+
Existing-Header:
84+
Header-To-Delete:
85+
--- error_code: 200
86+
--- no_error_log
87+
[error]
88+
89+
=== TEST 2: 'add' operation in request headers
90+
We test 2 things:
91+
1) Add op with a header that does not exist creates it with the given value.
92+
2) Add op with a header that exists, appends the given value to the existing one.
93+
--- configuration
94+
{
95+
"services": [
96+
{
97+
"id": 42,
98+
"backend_version": 1,
99+
"backend_authentication_type": "service_token",
100+
"backend_authentication_value": "token-value",
101+
"proxy": {
102+
"policy_chain": [
103+
{ "name": "apicast.policy.apicast" },
104+
{
105+
"name": "apicast.policy.headers",
106+
"configuration":
107+
{
108+
"request":
109+
[
110+
{ "op": "add", "header": "New-Header", "value": "config_value_nh" },
111+
{ "op": "add", "header": "Existing-Header", "value": "config_value_eh" }
112+
]
113+
}
114+
}
115+
],
116+
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
117+
"proxy_rules": [
118+
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 }
119+
]
120+
}
121+
}
122+
]
123+
}
124+
--- backend
125+
location /transactions/authrep.xml {
126+
content_by_lua_block {
127+
local expected = "service_token=token-value&service_id=42&usage%5Bhits%5D=2&user_key=value"
128+
local args = ngx.var.args
129+
if args == expected then
130+
ngx.exit(200)
131+
else
132+
ngx.log(ngx.ERR, expected, ' did not match: ', args)
133+
ngx.exit(403)
134+
end
135+
}
136+
}
137+
--- upstream
138+
location / {
139+
content_by_lua_block {
140+
if ngx.req.get_headers()['New-Header'] ~= 'config_value_nh' then
141+
ngx.log(ngx.ERR, "The header 'New-Header' does not have the correct value")
142+
elseif ngx.req.get_headers()['Existing-Header'] ~= 'request_value_eh, config_value_eh' then
143+
ngx.log(ngx.ERR, "The header 'Existing-Header' does not have the correct value")
144+
else
145+
ngx.say('yay, api backend');
146+
end
147+
}
148+
}
149+
--- request
150+
GET /?user_key=value
151+
Existing-Header: request_value_eh
152+
--- response_body
153+
yay, api backend
154+
--- response_headers
155+
New-Header:
156+
Existing-Header:
157+
Header-To-Delete:
158+
--- error_code: 200
159+
--- no_error_log
160+
[error]
161+
162+
=== TEST 3: 'set' operation in response headers
163+
We test 3 things:
164+
1) Set op with a header that does not exit creates it with the given value.
165+
2) Set op with a header that exists, clears it and sets the given value.
166+
3) Set op with an empty value clears the header.
167+
--- configuration
168+
{
169+
"services": [
170+
{
171+
"id": 42,
172+
"backend_version": 1,
173+
"backend_authentication_type": "service_token",
174+
"backend_authentication_value": "token-value",
175+
"proxy": {
176+
"policy_chain": [
177+
{ "name": "apicast.policy.apicast" },
178+
{
179+
"name": "apicast.policy.headers",
180+
"configuration":
181+
{
182+
"response":
183+
[
184+
{ "op": "set", "header": "New-Header", "value": "config_value_nh" },
185+
{ "op": "set", "header": "Existing-Header", "value": "config_value_eh" },
186+
{ "op": "set", "header": "Header-To-Delete", "value": "" }
187+
]
188+
}
189+
}
190+
],
191+
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
192+
"proxy_rules": [
193+
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 }
194+
]
195+
}
196+
}
197+
]
198+
}
199+
--- backend
200+
location /transactions/authrep.xml {
201+
content_by_lua_block {
202+
local expected = "service_token=token-value&service_id=42&usage%5Bhits%5D=2&user_key=value"
203+
local args = ngx.var.args
204+
if args == expected then
205+
ngx.exit(200)
206+
else
207+
ngx.log(ngx.ERR, expected, ' did not match: ', args)
208+
ngx.exit(403)
209+
end
210+
}
211+
}
212+
--- upstream
213+
location / {
214+
content_by_lua_block {
215+
ngx.header['Existing-Header'] = 'upstream_value_eh'
216+
ngx.header['Header-To-Delete'] = 'upstream_value_htd'
217+
ngx.say('yay, api backend')
218+
}
219+
}
220+
--- request
221+
GET /?user_key=value
222+
--- response_body
223+
yay, api backend
224+
--- response_headers
225+
New-Header: config_value_nh
226+
Existing-Header: config_value_eh
227+
Header-To-Delete:
228+
--- error_code: 200
229+
--- no_error_log
230+
[error]
231+
232+
=== TEST 4: 'add' operation in response headers
233+
We test 2 things:
234+
1) Add op with a header that does not exist creates it with the given value.
235+
2) Add op with a header that exists, appends the given value to the existing one.
236+
--- configuration
237+
{
238+
"services": [
239+
{
240+
"id": 42,
241+
"backend_version": 1,
242+
"backend_authentication_type": "service_token",
243+
"backend_authentication_value": "token-value",
244+
"proxy": {
245+
"policy_chain": [
246+
{ "name": "apicast.policy.apicast" },
247+
{
248+
"name": "apicast.policy.headers",
249+
"configuration":
250+
{
251+
"response":
252+
[
253+
{ "op": "add", "header": "New-Header", "value": "config_value_nh" },
254+
{ "op": "add", "header": "Existing-Header", "value": "config_value_eh" }
255+
]
256+
}
257+
}
258+
],
259+
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
260+
"proxy_rules": [
261+
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 }
262+
]
263+
}
264+
}
265+
]
266+
}
267+
--- backend
268+
location /transactions/authrep.xml {
269+
content_by_lua_block {
270+
local expected = "service_token=token-value&service_id=42&usage%5Bhits%5D=2&user_key=value"
271+
local args = ngx.var.args
272+
if args == expected then
273+
ngx.exit(200)
274+
else
275+
ngx.log(ngx.ERR, expected, ' did not match: ', args)
276+
ngx.exit(403)
277+
end
278+
}
279+
}
280+
--- upstream
281+
location / {
282+
content_by_lua_block {
283+
ngx.header['Existing-Header'] = 'upstream_value_eh'
284+
ngx.say('yay, api backend')
285+
}
286+
}
287+
--- request
288+
GET /?user_key=value
289+
--- response_body
290+
yay, api backend
291+
--- response_headers
292+
New-Header: config_value_nh
293+
Existing-Header: upstream_value_eh, config_value_eh
294+
Header-To-Delete:
295+
--- error_code: 200
296+
--- no_error_log
297+
[error]

0 commit comments

Comments
 (0)