1-- needs Alias from /home/c/diego/tec/luasocket/test to
2-- "/luasocket-test" and "/luasocket-test/"
3-- needs ScriptAlias from /home/c/diego/tec/luasocket/test/cgi
4-- to "/luasocket-test-cgi" and "/luasocket-test-cgi/"
5-- needs "AllowOverride AuthConfig" on /home/c/diego/tec/luasocket/test/auth
6local socket = require("socket")
7local http = require("socket.http")
8local url = require("socket.url")
9
10local mime = require("mime")
11local ltn12 = require("ltn12")
12
13-- override protection to make sure we see all errors
14-- socket.protect = function(s) return s end
15
16dofile("testsupport.lua")
17
18local host, proxy, request, response, index_file
19local ignore, expect, index, prefix, cgiprefix, index_crlf
20
21http.TIMEOUT = 10
22
23local t = socket.gettime()
24
25--host = host or "diego.student.princeton.edu"
26--host = host or "diego.student.princeton.edu"
27host = host or "localhost"
28proxy = proxy or "http://localhost:3128"
29prefix = prefix or "/luasocket-test"
30cgiprefix = cgiprefix or "/luasocket-test-cgi"
31index_file = "index.html"
32
33-- read index with CRLF convention
34index = readfile(index_file)
35
36local check_result = function(response, expect, ignore)
37    for i,v in pairs(response) do
38        if not ignore[i] then
39            if v ~= expect[i] then
40                local f = io.open("err", "w")
41                f:write(tostring(v), "\n\n versus\n\n", tostring(expect[i]))
42                f:close()
43                fail(i .. " differs!")
44            end
45        end
46    end
47    for i,v in pairs(expect) do
48        if not ignore[i] then
49            if v ~= response[i] then
50                local f = io.open("err", "w")
51                f:write(tostring(response[i]), "\n\n versus\n\n", tostring(v))
52                v = string.sub(type(v) == "string" and v or "", 1, 70)
53                f:close()
54                fail(i .. " differs!")
55            end
56        end
57    end
58    print("ok")
59end
60
61local check_request = function(request, expect, ignore)
62    local t
63    if not request.sink then request.sink, t = ltn12.sink.table() end
64    request.source = request.source or
65        (request.body and ltn12.source.string(request.body))
66    local response = {}
67    response.code, response.headers, response.status =
68        socket.skip(1, http.request(request))
69    if t and #t > 0 then response.body = table.concat(t) end
70    check_result(response, expect, ignore)
71end
72
73------------------------------------------------------------------------
74io.write("testing request uri correctness: ")
75local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string"
76local back, c, h = http.request("http://" .. host .. forth)
77if not back then fail(c) end
78back = url.parse(back)
79if similar(back.query, "this+is+the+query+string") then print("ok")
80else fail(back.query) end
81
82------------------------------------------------------------------------
83io.write("testing query string correctness: ")
84forth = "this+is+the+query+string"
85back = http.request("http://" .. host .. cgiprefix ..
86    "/query-string?" .. forth)
87if similar(back, forth) then print("ok")
88else fail("failed!") end
89
90------------------------------------------------------------------------
91io.write("testing document retrieval: ")
92request = {
93    url = "http://" .. host .. prefix .. "/index.html"
94}
95expect = {
96    body = index,
97    code = 200
98}
99ignore = {
100    status = 1,
101    headers = 1
102}
103check_request(request, expect, ignore)
104
105------------------------------------------------------------------------
106io.write("testing redirect loop: ")
107request = {
108    url = "http://" .. host .. cgiprefix .. "/redirect-loop"
109}
110expect = {
111    code = 302
112}
113ignore = {
114    status = 1,
115    headers = 1,
116    body = 1
117}
118check_request(request, expect, ignore)
119
120------------------------------------------------------------------------
121io.write("testing invalid url: ")
122local r, e = http.request{url = host .. prefix}
123assert(r == nil and e == "invalid host ''")
124r, re = http.request(host .. prefix)
125assert(r == nil and e == re, tostring(r) ..", " .. tostring(re) ..
126    " vs " .. tostring(e))
127print("ok")
128
129io.write("testing invalid empty port: ")
130request = {
131    url = "http://" .. host .. ":" .. prefix .. "/index.html"
132}
133expect = {
134    body = index,
135    code = 200
136}
137ignore = {
138    status = 1,
139    headers = 1
140}
141check_request(request, expect, ignore)
142
143------------------------------------------------------------------------
144io.write("testing post method: ")
145-- wanted to test chunked post, but apache doesn't support it...
146request = {
147    url = "http://" .. host .. cgiprefix .. "/cat",
148    method = "POST",
149    body = index,
150    -- remove content-length header to send chunked body
151    headers = { ["content-length"] = string.len(index) }
152}
153expect = {
154    body = index,
155    code = 200
156}
157ignore = {
158    status = 1,
159    headers = 1
160}
161check_request(request, expect, ignore)
162
163------------------------------------------------------------------------
164--[[
165io.write("testing proxy with post method: ")
166request = {
167    url = "http://" .. host .. cgiprefix .. "/cat",
168    method = "POST",
169    body = index,
170    headers = { ["content-length"] = string.len(index) },
171    proxy= proxy
172}
173expect = {
174    body = index,
175    code = 200
176}
177ignore = {
178    status = 1,
179    headers = 1
180}
181check_request(request, expect, ignore)
182]]
183
184------------------------------------------------------------------------
185io.write("testing simple post function: ")
186back = http.request("http://" .. host .. cgiprefix .. "/cat", index)
187assert(back == index)
188print("ok")
189
190------------------------------------------------------------------------
191io.write("testing ltn12.(sink|source).file: ")
192request = {
193    url = "http://" .. host .. cgiprefix .. "/cat",
194    method = "POST",
195    source = ltn12.source.file(io.open(index_file, "rb")),
196    sink = ltn12.sink.file(io.open(index_file .. "-back", "wb")),
197    headers = { ["content-length"] = string.len(index) }
198}
199expect = {
200    code = 200
201}
202ignore = {
203    status = 1,
204    headers = 1
205}
206check_request(request, expect, ignore)
207back = readfile(index_file .. "-back")
208assert(back == index)
209os.remove(index_file .. "-back")
210
211------------------------------------------------------------------------
212io.write("testing ltn12.(sink|source).chain and mime.(encode|decode): ")
213
214local function b64length(len)
215    local a = math.ceil(len/3)*4
216    local l = math.ceil(a/76)
217    return a + l*2
218end
219
220local source = ltn12.source.chain(
221    ltn12.source.file(io.open(index_file, "rb")),
222    ltn12.filter.chain(
223        mime.encode("base64"),
224        mime.wrap("base64")
225    )
226)
227
228local sink = ltn12.sink.chain(
229    mime.decode("base64"),
230    ltn12.sink.file(io.open(index_file .. "-back", "wb"))
231)
232
233request = {
234    url = "http://" .. host .. cgiprefix .. "/cat",
235    method = "POST",
236    source = source,
237    sink = sink,
238    headers = { ["content-length"] = b64length(string.len(index)) }
239}
240expect = {
241    code = 200
242}
243ignore = {
244    body_cb = 1,
245    status = 1,
246    headers = 1
247}
248check_request(request, expect, ignore)
249back = readfile(index_file .. "-back")
250assert(back == index)
251os.remove(index_file .. "-back")
252
253------------------------------------------------------------------------
254io.write("testing http redirection: ")
255request = {
256    url = "http://" .. host .. prefix
257}
258expect = {
259    body = index,
260    code = 200
261}
262ignore = {
263    status = 1,
264    headers = 1
265}
266check_request(request, expect, ignore)
267
268------------------------------------------------------------------------
269--[[
270io.write("testing proxy with redirection: ")
271request = {
272    url = "http://" .. host .. prefix,
273    proxy = proxy
274}
275expect = {
276    body = index,
277    code = 200
278}
279ignore = {
280    status = 1,
281    headers = 1
282}
283check_request(request, expect, ignore)
284]]
285
286------------------------------------------------------------------------
287io.write("testing automatic auth failure: ")
288request = {
289    url = "http://really:wrong@" .. host .. prefix .. "/auth/index.html"
290}
291expect = {
292    code = 401
293}
294ignore = {
295    body = 1,
296    status = 1,
297    headers = 1
298}
299check_request(request, expect, ignore)
300
301------------------------------------------------------------------------
302io.write("testing http redirection failure: ")
303request = {
304    url = "http://" .. host .. prefix,
305    redirect = false
306}
307expect = {
308    code = 301
309}
310ignore = {
311    body = 1,
312    status = 1,
313    headers = 1
314}
315check_request(request, expect, ignore)
316
317------------------------------------------------------------------------
318io.write("testing document not found: ")
319request = {
320    url = "http://" .. host .. "/wrongdocument.html"
321}
322expect = {
323    code = 404
324}
325ignore = {
326    body = 1,
327    status = 1,
328    headers = 1
329}
330check_request(request, expect, ignore)
331
332------------------------------------------------------------------------
333io.write("testing auth failure: ")
334request = {
335    url = "http://" .. host .. prefix .. "/auth/index.html"
336}
337expect = {
338    code = 401
339}
340ignore = {
341    body = 1,
342    status = 1,
343    headers = 1
344}
345check_request(request, expect, ignore)
346
347------------------------------------------------------------------------
348io.write("testing manual basic auth: ")
349request = {
350    url = "http://" .. host .. prefix .. "/auth/index.html",
351    headers = {
352        authorization = "Basic " .. (mime.b64("luasocket:password"))
353    }
354}
355expect = {
356    code = 200,
357    body = index
358}
359ignore = {
360    status = 1,
361    headers = 1
362}
363check_request(request, expect, ignore)
364
365------------------------------------------------------------------------
366io.write("testing automatic basic auth: ")
367request = {
368    url = "http://luasocket:password@" .. host .. prefix .. "/auth/index.html"
369}
370expect = {
371    code = 200,
372    body = index
373}
374ignore = {
375    status = 1,
376    headers = 1
377}
378check_request(request, expect, ignore)
379
380------------------------------------------------------------------------
381io.write("testing auth info overriding: ")
382request = {
383    url = "http://really:wrong@" .. host .. prefix .. "/auth/index.html",
384    user = "luasocket",
385    password = "password"
386}
387expect = {
388    code = 200,
389    body = index
390}
391ignore = {
392    status = 1,
393    headers = 1
394}
395check_request(request, expect, ignore)
396
397------------------------------------------------------------------------
398io.write("testing cgi output retrieval (probably chunked...): ")
399request = {
400    url = "http://" .. host .. cgiprefix .. "/cat-index-html"
401}
402expect = {
403    body = index,
404    code = 200
405}
406ignore = {
407    status = 1,
408    headers = 1
409}
410check_request(request, expect, ignore)
411
412------------------------------------------------------------------------
413local body
414io.write("testing simple request function: ")
415body = http.request("http://" .. host .. prefix .. "/index.html")
416assert(body == index)
417print("ok")
418
419------------------------------------------------------------------------
420io.write("testing HEAD method: ")
421local r, c, h = http.request {
422  method = "HEAD",
423  url = "http://www.tecgraf.puc-rio.br/~diego/"
424}
425assert(r and h and (c == 200), c)
426print("ok")
427
428------------------------------------------------------------------------
429io.write("testing host not found: ")
430local c, e = socket.connect("example.invalid", 80)
431local r, re = http.request{url = "http://example.invalid/does/not/exist"}
432assert(r == nil and e == re, tostring(r) .. " " .. tostring(re))
433r, re = http.request("http://example.invalid/does/not/exist")
434assert(r == nil and e == re)
435print("ok")
436
437------------------------------------------------------------------------
438print("passed all tests")
439os.remove("err")
440
441print(string.format("done in %.2fs", socket.gettime() - t))
442