Bug 877527
[gecko.git] / tools / httptester / server.py
blob66e8071c8c49da2805974f45a3cc95d09a89052f
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 """Regression testing HTTP 'server'
7 The intent of this is to provide a (scriptable) framework for regression
8 testing mozilla stuff. See the docs for details.
9 """
11 __version__ = "0.1"
13 import BaseHTTPServer
14 import string
15 import time
16 import os
17 from stat import *
18 import results
20 class RegressionHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
21 server_version = "Regression/" + __version__
23 protocol_version = "HTTP/1.1"
25 def do_GET(self):
26 if self.path == '/':
27 return self.initial_redirect()
28 if self.path[:1] != '/':
29 return self.send_error(400,
30 "Path %s does not begin with /" % `self.path`)
32 try:
33 id, req = string.split(self.path[1:], '/', 1)
34 except ValueError:
35 return self.send_error(404, "Missing id and path")
37 if not req:
38 # Initial request. Need to get a file list
39 return self.list_tests(id)
40 elif req == 'report':
41 self.send_response(200)
42 self.send_header('Content-Type', "text/plain")
43 self.end_headers()
44 res = results.results(id)
45 res.write_report(self.wfile)
46 del res
47 return
48 else:
49 return self.handle_request(id, req)
51 def initial_redirect(self):
52 """Redirect the initial query.
54 I don't want to use cookies, because that would bring
55 wallet into all the tests. So the url will be:
56 http://foo/123456/path/and/filename.html"""
58 self.send_response(302)
59 self.send_header("Location","/%s/" % (long(time.time()*1000)))
60 self.end_headers()
62 def list_tests(self, id):
63 """List all test cases."""
65 try:
66 os.stat("tests")
67 except IOError:
68 return self.send_error(500, "Tests were not found")
70 self.send_response(200)
71 self.send_header('Content-Type', "text/plain")
72 self.end_headers()
73 return self.recurse_dir(id,"tests")
75 def recurse_dir(self, id, path):
76 hasDir = 0
78 dir = os.listdir(path)
79 dir.sort()
81 for i in dir:
82 if i == 'CVS':
83 continue
84 mode = os.stat(path+'/'+i)[ST_MODE]
85 if S_ISDIR(mode):
86 hasDir = 1
87 self.recurse_dir(id,path+"/"+i)
88 elif hasDir:
89 print "%s: Warning! dir and non dir are mixed." % (path)
91 if not hasDir:
92 self.wfile.write("http://localhost:8000/%s/%s/\n" % (id, path))
94 def copyfileobj(self, src, dst):
95 """See shutil.copyfileobj from 2.x
97 I want this to be usable with 1.5 though"""
99 while 1:
100 data = src.read(4096)
101 if not data: break
102 dst.write(data)
104 default_reply = "Testcase %s for %s loaded\n"
106 def handle_request(self, id, req):
107 """Answer a request
109 We first look for a file with the name of the request.
110 If that exists, then we spit that out, otherwise we
111 open req.headers and req.body (if available) separately.
113 Why would you want to split it out?
114 a) binary files
115 b) Separating it out will send the 'standard' headers,
116 and handle the Connection: details for you, if you're
117 not testing that.
118 c) You don't need to come up with your own body"""
120 res = results.results(id)
122 path = string.join(string.split(req, '/')[:-1], '/')
124 path = path + '/'
126 tester = res.get_tester(path)
128 self.fname = string.split(req,'/')[-1]
130 if not self.fname:
131 self.fname = tester.baseName
133 if not tester.verify_request(self):
134 res.set_tester(req, tester)
135 return self.send_error(400, tester.reason)
137 ### perhaps this isn't the best design model...
138 res.set_tester(path, tester)
140 del res
142 if req[-1:] == '/':
143 req = req + tester.baseName
145 try:
146 f = open(req, 'rb')
147 self.log_message('"%s" sent successfully for %s',
148 self.requestline,
150 self.copyfileobj(f,self.wfile)
151 return f.close()
152 except IOError:
153 try:
154 f = open(req+".headers", 'rb')
155 except IOError:
156 return self.send_error(404, "File %s not found" % (req))
158 self.send_response(f.readline())
159 # XXX - I should parse these out, and use send_header instead
160 # so that I can change behaviour (like keep-alive...)
161 # But then I couldn't test 'incorrect' header formats
162 self.copyfileobj(f,self.wfile)
163 f.close()
165 try:
166 f = open(req+".body", 'rb')
167 ## XXXXX - Need to configify this
168 ## and then send content-length, etc
169 self.end_headers()
170 self.copyfileobj(f, self.wfile)
171 return f.close()
172 except IOError:
173 self.send_header('Content-Type', "text/plain")
174 body = self.default_reply % (req, id)
175 self.send_header('Content-Length', len(body))
176 self.end_headers()
177 self.wfile.write(body)
179 def send_response(self, line, msg=None):
180 if msg:
181 return BaseHTTPServer.BaseHTTPRequestHandler.send_response(self, line,msg)
182 try:
183 x = int(line)
184 BaseHTTPServer.BaseHTTPRequestHandler.send_response(self, x)
185 except ValueError:
186 tuple = string.split(line, ' ',2)
187 ## XXXX
188 old = self.protocol_version
189 self.protocol_version = tuple[0]
190 BaseHTTPServer.BaseHTTPRequestHandler.send_response(self, int(tuple[1]), tuple[2][:-1])
191 self.protocol_version = old
193 import socket
195 # I need to thread this, with the mixin class
196 class RegressionHTTPServer(BaseHTTPServer.HTTPServer):
197 # The 1.5.2 version doesn't do this:
198 def server_bind(self):
199 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
200 BaseHTTPServer.HTTPServer.server_bind(self)
202 def run(HandlerClass = RegressionHTTPRequestHandler,
203 ServerClass = RegressionHTTPServer):
204 BaseHTTPServer.test(HandlerClass, ServerClass)
206 if __name__ == '__main__':
207 run()