Oops, wrong kind of line ending.
[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 TestCases(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 def test_dirlist_escape(self):
75 fn = WWWROOT+"/escape#this"
76 open(fn, "w").write("x"*12345)
77 try:
78 resp = Conn().get("/")
79 finally:
80 os.unlink(fn)
81 status, hdrs, body = parse(resp)
82 self.assertEquals(ord("#"), 0x23)
83 self.assertContains(body, "escape%23this", "12345")
84
85 def nerf(s):
86 return re.sub("[^a-zA-Z0-9]", "_", s)
87
88 def makeCase(name, url, hdr_checker=None, body_checker=None,
89 req_hdrs={"User-Agent": "test.py"},
90 http_ver=None, endl="\n"):
91 def do_test(self):
92 resp = Conn().get(url, http_ver, endl, req_hdrs)
93 if http_ver is None:
94 status = ""
95 hdrs = {}
96 body = resp
97 else:
98 status, hdrs, body = parse(resp)
99
100 if hdr_checker is not None and http_ver is not None:
101 hdr_checker(self, hdrs)
102
103 if body_checker is not None:
104 body_checker(self, body)
105
106 # FIXME: check status
107 if http_ver is not None:
108 prefix = "HTTP/1.1 " # should 1.0 stay 1.0?
109 self.assertTrue(status.startswith(prefix),
110 msg="%s at start of %s"%(repr(prefix), repr(status)))
111
112 v = http_ver
113 if v is None:
114 v = "0.9"
115 test_name = "_".join([
116 "test",
117 nerf(name),
118 nerf("HTTP"+v),
119 {"\n":"LF", "\r\n":"CRLF"}[endl],
120 ])
121 do_test.__name__ = test_name # hax
122 setattr(TestCases, test_name, do_test)
123
124 def makeCases(name, url, hdr_checker=None, body_checker=None,
125 req_hdrs={"User-Agent": "test.py"}):
126 # FIXME: 0.9 is broken
127 for http_ver in [None, "1.0", "1.1"]:
128 #for http_ver in ["1.0", "1.1"]:
129 for endl in ["\n", "\r\n"]:
130 makeCase(name, url, hdr_checker, body_checker,
131 req_hdrs, http_ver, endl)
132
133 def makeSimpleCases(name, url, assert_name):
134 makeCases(name, url, None,
135 lambda self,body: getattr(self, assert_name)(body, url))
136
137 def setUpModule():
138 for args in [
139 ["index", "/", "assertIsIndex"],
140 ["up dir", "/dir/../", "assertIsIndex"],
141 ["extra slashes", "//dir///..////", "assertIsIndex"],
142 ["no trailing slash", "/dir/..", "assertIsIndex"],
143 ["no leading slash", "dir/../", "assertIsInvalid"],
144 ["invalid up dir", "/../", "assertIsInvalid"],
145 ["fancy invalid up dir", "/./dir/./../../", "assertIsInvalid"],
146 ]:
147 makeSimpleCases(*args)
148
149 if __name__ == '__main__':
150 setUpModule()
151 unittest.main()
152 #x = Conn().get("/xyz/../", "1.0")
153 #y = parse(x)
154 #print repr(y)
155
156 # vim:set ts=4 sw=4 et: