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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
-- SPDX-License-Identifier: GPL-3.0-or-later
-- check prerequisites
local has_http = pcall(require, 'kres_modules.http') and pcall(require, 'http.request')
if not has_http then
-- skipping daf module test because http its not installed
os.exit(77)
else
local path = worker.cwd..'/control/'..worker.pid
same(true, net.listen(path, nil, {kind = 'control'}),
'new control sockets were created so map() can work')
local request = require('http.request')
modules.load('http')
modules.load('daf')
local bound
for _ = 1,1000 do
bound, _err = pcall(net.listen, '127.0.0.1', math.random(40000, 49999), { kind = 'webmgmt'})
if bound then
break
end
end
assert(bound, 'unable to bind a port for HTTP module (1000 attempts)')
-- globals for this module
local _, host, port, baseuri
local function start_server()
local server_fd = next(http.servers)
assert(server_fd)
local server = http.servers[server_fd].server
ok(server ~= nil, 'creates server instance')
_, host, port = server:localname()
ok(host and port, 'binds to an interface')
baseuri = string.format('http://%s:%d/daf', host, port)
end
-- helper for returning useful values to test on
-- local function http_get(uri)
-- local headers, stream = assert(request.new_from_uri(uri):go(16))
-- local body = assert(stream:get_body_as_string())
-- return tonumber(headers:get(':status')), body, headers:get('content-type')
-- end
local function http_req(uri, method, reqbody)
local req = assert(request.new_from_uri(baseuri .. uri))
req.headers:upsert(':method', method)
req:set_body(reqbody)
local headers, stream = assert(req:go(16))
local ansbody = assert(stream:get_body_as_string())
return tonumber(headers:get(':status')), ansbody, headers:get('content-type')
end
local function http_get(uri)
return http_req(uri, 'GET')
end
-- compare two tables, expected value is specified as JSON
-- comparison relies on table_print which sorts table keys
local function compare_tables(expectedjson, gotjson, desc)
same(
table_print(fromjson(expectedjson)),
table_print(fromjson(gotjson)),
desc)
end
-- test whether http interface responds and binds
local function test_daf_api()
local code, body, mime
-- rule listing /daf
code, body, mime = http_get('/')
same(code, 200, 'rule listing return 200 OK')
same(body, '{}', 'daf rule list is empty after start')
same(mime, 'application/json', 'daf rule list has application/json content type')
-- get non-existing rule
code, body = http_req('/0', 'GET')
same(code, 404, 'non-existing rule retrieval returns 404')
same(body, '"No such rule"', 'explanatory message is present')
-- delete non-existing rule
code, body = http_req('/0', 'DELETE')
same(code, 404, 'non-existing rule deletion returns 404')
same(body, '"No such rule"', 'explanatory message is present')
-- bad PATCH
code = http_req('/0', 'PATCH')
same(code, 400, 'PATCH detects missing parameters')
-- bad POST
code = http_req('/', 'POST')
same(code, 500, 'POST without parameters is detected')
-- POST first new rule
code, body, mime = http_req('/', 'POST', 'src = 192.0.2.0 pass')
same(code, 200, 'first POST succeeds')
compare_tables(body,
'{"count":0,"active":true,"id":0,"info":"src = 192.0.2.0 pass"}',
'POST returns new rule in JSON')
same(mime, 'application/json', 'rule has application/json content type')
-- GET first rule
code, body, mime = http_req('/0', 'GET')
same(code, 200, 'GET for first rule succeeds')
compare_tables(body,
'{"count":0,"active":true,"id":0,"info":"src = 192.0.2.0 pass"}',
'POST returns new rule in JSON')
same(mime, 'application/json', 'rule has application/json content type')
-- POST second new rule
code, body, mime = http_req('/', 'POST', 'src = 192.0.2.1 pass')
same(code, 200, 'second POST succeeds')
compare_tables(body,
'{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}',
'POST returns new rule in JSON')
same(mime, 'application/json', 'rule has application/json content type')
-- GET second rule
code, body, mime = http_req('/1', 'GET')
same(code, 200, 'GET for second rule succeeds')
compare_tables(body,
'{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}',
'POST returns new rule in JSON')
same(mime, 'application/json', 'rule has application/json content type')
-- PATCH first rule
code, body, mime = http_req('/0/active/false', 'PATCH')
same(code, 200, 'PATCH for first rule succeeds')
same(body, 'true', 'PATCH returns success in body')
same(mime, 'application/json', 'PATCH return value has application/json content type')
-- GET modified first rule
code, body, mime = http_req('/0', 'GET')
same(code, 200, 'GET for first rule succeeds')
compare_tables(body,
'{"count":0,"active":false,"id":0,"info":"src = 192.0.2.0 pass"}',
'GET returns modified rule in JSON')
same(mime, 'application/json', 'rule has application/json content type')
-- GET both rules
code, body, mime = http_req('/', 'GET')
same(code, 200, 'GET for both rule succeeds')
compare_tables(body, [[
[
{"count":0,"active":false,"info":"src = 192.0.2.0 pass","id":0},
{"count":0,"active":true,"info":"src = 192.0.2.1 pass","id":1}]
]],
'GET returns both rules in JSON including modifications')
same(mime, 'application/json', 'rule list has application/json content type')
-- PATCH first rule back to original state
code, body, mime = http_req('/0/active/true', 'PATCH')
same(code, 200, 'PATCH for first rule succeeds')
same(body, 'true', 'PATCH returns success in body')
same(mime, 'application/json', 'PATCH return value has application/json content type')
-- GET modified (reversed) first rule
code, body, mime = http_req('/0', 'GET')
same(code, 200, 'GET for first rule succeeds')
compare_tables(body,
'{"count":0,"active":true,"id":0,"info":"src = 192.0.2.0 pass"}',
'GET returns modified rule in JSON')
same(mime, 'application/json', 'rule has application/json content type')
-- DELETE first rule
code, body, mime = http_req('/0', 'DELETE')
same(code, 200, 'DELETE for first rule succeeds')
same(body, 'true', 'DELETE returns success in body')
same(mime, 'application/json', 'DELETE return value has application/json content type')
-- GET deleted (first) rule
code, body = http_req('/0', 'GET')
same(code, 404, 'GET for deleted fails with 404')
same(body, '"No such rule"', 'failed GET contains explanatory message')
-- GET second rule
code, body, mime = http_req('/1', 'GET')
same(code, 200, 'GET for second rule still succeeds')
compare_tables(body,
'{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}',
'POST returns new rule in JSON')
same(mime, 'application/json', 'rule has application/json content type')
-- GET list of all rules
code, body, mime = http_req('/', 'GET')
same(code, 200, 'GET returns list with the remaining rule')
compare_tables(body,
'[{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}]',
'rule list contains only the remaining rule in JSON')
same(mime, 'application/json', 'rule has application/json content type')
-- try to DELETE first rule again
code, body = http_req('/0', 'DELETE')
same(code, 404, 'DELETE for already deleted rule fails with 404')
same(body, '"No such rule"', 'DELETE explains failure')
-- DELETE second rule
code, body, mime = http_req('/1', 'DELETE')
same(code, 200, 'DELETE for second rule succeeds')
same(body, 'true', 'DELETE returns success in body')
same(mime, 'application/json', 'DELETE return value has application/json content type')
-- GET (supposedly empty) list of all rules
code, body, mime = http_req('/', 'GET')
same(code, 200, 'GET returns list with the remaining rule')
compare_tables(body, '[]', 'rule list is now empty JSON list')
same(mime, 'application/json', 'rule has application/json content type')
end
-- plan tests
local tests = {
start_server,
test_daf_api,
}
return tests
end
|