8aedb4d9a73fd03368772dacf448b7ec56cad15f
[darkhttpd] / devel / test.py
1 #!/usr/bin/env python
2 import unittest
3 import socket
4 import signal
5 import re
6 import os
7
8 WWWROOT = "tmp.httpd.tests"
9
10 class Conn:
11 def __init__(self):
12 self.port = 12346
13 self.s = socket.socket()
14 self.s.connect(("0.0.0.0", self.port))
15 # connect throws socket.error on connection refused
16
17 def get(self, url, http_ver="1.0", endl="\n", req_hdrs={}):
18 req = "GET "+url
19 if http_ver is not None:
20 req += " HTTP/"+http_ver
21 req += endl
22 if http_ver is not None:
23 req_hdrs["User-Agent"] = "test.py"
24 req_hdrs["Connection"] = "close"
25 for k,v in req_hdrs.items():
26 req += k+": "+v+endl
27 req += endl # end of request
28 self.s.send(req)
29 ret = ""
30 while True:
31 signal.alarm(1) # don't wait forever
32 r = self.s.recv(65536)
33 signal.alarm(0)
34 if r == "":
35 break
36 else:
37 ret += r
38 return ret
39
40 def parse(resp):
41 """
42 Parse response into status line, headers and body.
43 """
44 pos = resp.index("\r\n\r\n") # throws exception on failure
45 head = resp[:pos]
46 body = resp[pos+4:]
47 status,head = head.split("\r\n", 1)
48 hdrs = {}
49 for line in head.split("\r\n"):
50 k, v = line.split(": ", 1)
51 hdrs[k] = v
52 return (status, hdrs, body)
53
54 class TestHelper(unittest.TestCase):
55 def assertContains(self, body, *strings):
56 for s in strings:
57 self.assertTrue(s in body,
58 msg="expected %s in %s"%(repr(s), repr(body)))
59
60 def assertIsIndex(self, body, path):
61 self.assertContains(body,
62 "<title>%s</title>\n"%path,
63 "<h1>%s</h1>\n"%path,
64 '<a href="..">..</a>/',
65 'Generated by darkhttpd')
66
67 def assertIsInvalid(self, body, path):
68 self.assertContains(body,
69 "<title>400 Bad Request</title>",
70 "<h1>Bad Request</h1>\n",
71 "You requested an invalid URL: %s\n"%path,
72 'Generated by darkhttpd')
73
74 class TestDirList(TestHelper):
75 def setUp(self):
76 self.fn = WWWROOT+"/escape#this"
77 open(self.fn, "w").write("x"*12345)
78
79 def tearDown(self):
80 os.unlink(self.fn)
81
82 def test_dirlist_escape(self):
83 resp = Conn().get("/")
84 status, hdrs, body = parse(resp)
85 self.assertEquals(ord("#"), 0x23)
86 self.assertContains(body, "escape%23this", "12345")
87
88 class TestCases(TestHelper):
89 pass # these get autogenerated in setUpModule()
90
91 def nerf(s):
92 return re.sub("[^a-zA-Z0-9]", "_", s)
93
94 def makeCase(name, url, hdr_checker=None, body_checker=None,
95 req_hdrs={"User-Agent": "test.py"},
96 http_ver=None, endl="\n"):
97 def do_test(self):
98 resp = Conn().get(url, http_ver, endl, req_hdrs)
99 if http_ver is None:
100 status = ""
101 hdrs = {}
102 body = resp
103 else:
104 status, hdrs, body = parse(resp)
105
106 if hdr_checker is not None and http_ver is not None:
107 hdr_checker(self, hdrs)
108
109 if body_checker is not None:
110 body_checker(self, body)
111
112 # FIXME: check status
113 if http_ver is not None:
114 prefix = "HTTP/1.1 " # should 1.0 stay 1.0?
115 self.assertTrue(status.startswith(prefix),
116 msg="%s at start of %s"%(repr(prefix), repr(status)))
117
118 v = http_ver
119 if v is None:
120 v = "0.9"
121 test_name = "_".join([
122 "test",
123 nerf(name),
124 nerf("HTTP"+v),
125 {"\n":"LF", "\r\n":"CRLF"}[endl],
126 ])
127 do_test.__name__ = test_name # hax
128 setattr(TestCases, test_name, do_test)
129
130 def makeCases(name, url, hdr_checker=None, body_checker=None,
131 req_hdrs={"User-Agent": "test.py"}):
132 for http_ver in [None, "1.0", "1.1"]:
133 for endl in ["\n", "\r\n"]:
134 makeCase(name, url, hdr_checker, body_checker,
135 req_hdrs, http_ver, endl)
136
137 def makeSimpleCases(name, url, assert_name):
138 makeCases(name, url, None,
139 lambda self,body: getattr(self, assert_name)(body, url))
140
141 def setUpModule():
142 for args in [
143 ["index", "/", "assertIsIndex"],
144 ["up dir", "/dir/../", "assertIsIndex"],
145 ["extra slashes", "//dir///..////", "assertIsIndex"],
146 ["no trailing slash", "/dir/..", "assertIsIndex"],
147 ["no leading slash", "dir/../", "assertIsInvalid"],
148 ["invalid up dir", "/../", "assertIsInvalid"],
149 ["fancy invalid up dir", "/./dir/./../../", "assertIsInvalid"],
150 ]:
151 makeSimpleCases(*args)
152
153 if __name__ == '__main__':
154 setUpModule()
155 unittest.main()
156
157 # vim:set ts=4 sw=4 et: