1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/httptester/server.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,207 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +"""Regression testing HTTP 'server' 1.9 + 1.10 +The intent of this is to provide a (scriptable) framework for regression 1.11 +testing mozilla stuff. See the docs for details. 1.12 +""" 1.13 + 1.14 +__version__ = "0.1" 1.15 + 1.16 +import BaseHTTPServer 1.17 +import string 1.18 +import time 1.19 +import os 1.20 +from stat import * 1.21 +import results 1.22 + 1.23 +class RegressionHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 1.24 + server_version = "Regression/" + __version__ 1.25 + 1.26 + protocol_version = "HTTP/1.1" 1.27 + 1.28 + def do_GET(self): 1.29 + if self.path == '/': 1.30 + return self.initial_redirect() 1.31 + if self.path[:1] != '/': 1.32 + return self.send_error(400, 1.33 + "Path %s does not begin with /" % `self.path`) 1.34 + 1.35 + try: 1.36 + id, req = string.split(self.path[1:], '/', 1) 1.37 + except ValueError: 1.38 + return self.send_error(404, "Missing id and path") 1.39 + 1.40 + if not req: 1.41 + # Initial request. Need to get a file list 1.42 + return self.list_tests(id) 1.43 + elif req == 'report': 1.44 + self.send_response(200) 1.45 + self.send_header('Content-Type', "text/plain") 1.46 + self.end_headers() 1.47 + res = results.results(id) 1.48 + res.write_report(self.wfile) 1.49 + del res 1.50 + return 1.51 + else: 1.52 + return self.handle_request(id, req) 1.53 + 1.54 + def initial_redirect(self): 1.55 + """Redirect the initial query. 1.56 + 1.57 + I don't want to use cookies, because that would bring 1.58 + wallet into all the tests. So the url will be: 1.59 + http://foo/123456/path/and/filename.html""" 1.60 + 1.61 + self.send_response(302) 1.62 + self.send_header("Location","/%s/" % (long(time.time()*1000))) 1.63 + self.end_headers() 1.64 + 1.65 + def list_tests(self, id): 1.66 + """List all test cases.""" 1.67 + 1.68 + try: 1.69 + os.stat("tests") 1.70 + except IOError: 1.71 + return self.send_error(500, "Tests were not found") 1.72 + 1.73 + self.send_response(200) 1.74 + self.send_header('Content-Type', "text/plain") 1.75 + self.end_headers() 1.76 + return self.recurse_dir(id,"tests") 1.77 + 1.78 + def recurse_dir(self, id, path): 1.79 + hasDir = 0 1.80 + 1.81 + dir = os.listdir(path) 1.82 + dir.sort() 1.83 + 1.84 + for i in dir: 1.85 + if i == 'CVS': 1.86 + continue 1.87 + mode = os.stat(path+'/'+i)[ST_MODE] 1.88 + if S_ISDIR(mode): 1.89 + hasDir = 1 1.90 + self.recurse_dir(id,path+"/"+i) 1.91 + elif hasDir: 1.92 + print "%s: Warning! dir and non dir are mixed." % (path) 1.93 + 1.94 + if not hasDir: 1.95 + self.wfile.write("http://localhost:8000/%s/%s/\n" % (id, path)) 1.96 + 1.97 + def copyfileobj(self, src, dst): 1.98 + """See shutil.copyfileobj from 2.x 1.99 + 1.100 + I want this to be usable with 1.5 though""" 1.101 + 1.102 + while 1: 1.103 + data = src.read(4096) 1.104 + if not data: break 1.105 + dst.write(data) 1.106 + 1.107 + default_reply = "Testcase %s for %s loaded\n" 1.108 + 1.109 + def handle_request(self, id, req): 1.110 + """Answer a request 1.111 + 1.112 + We first look for a file with the name of the request. 1.113 + If that exists, then we spit that out, otherwise we 1.114 + open req.headers and req.body (if available) separately. 1.115 + 1.116 + Why would you want to split it out? 1.117 + a) binary files 1.118 + b) Separating it out will send the 'standard' headers, 1.119 + and handle the Connection: details for you, if you're 1.120 + not testing that. 1.121 + c) You don't need to come up with your own body""" 1.122 + 1.123 + res = results.results(id) 1.124 + 1.125 + path = string.join(string.split(req, '/')[:-1], '/') 1.126 + 1.127 + path = path + '/' 1.128 + 1.129 + tester = res.get_tester(path) 1.130 + 1.131 + self.fname = string.split(req,'/')[-1] 1.132 + 1.133 + if not self.fname: 1.134 + self.fname = tester.baseName 1.135 + 1.136 + if not tester.verify_request(self): 1.137 + res.set_tester(req, tester) 1.138 + return self.send_error(400, tester.reason) 1.139 + 1.140 + ### perhaps this isn't the best design model... 1.141 + res.set_tester(path, tester) 1.142 + 1.143 + del res 1.144 + 1.145 + if req[-1:] == '/': 1.146 + req = req + tester.baseName 1.147 + 1.148 + try: 1.149 + f = open(req, 'rb') 1.150 + self.log_message('"%s" sent successfully for %s', 1.151 + self.requestline, 1.152 + id) 1.153 + self.copyfileobj(f,self.wfile) 1.154 + return f.close() 1.155 + except IOError: 1.156 + try: 1.157 + f = open(req+".headers", 'rb') 1.158 + except IOError: 1.159 + return self.send_error(404, "File %s not found" % (req)) 1.160 + 1.161 + self.send_response(f.readline()) 1.162 + # XXX - I should parse these out, and use send_header instead 1.163 + # so that I can change behaviour (like keep-alive...) 1.164 + # But then I couldn't test 'incorrect' header formats 1.165 + self.copyfileobj(f,self.wfile) 1.166 + f.close() 1.167 + 1.168 + try: 1.169 + f = open(req+".body", 'rb') 1.170 + ## XXXXX - Need to configify this 1.171 + ## and then send content-length, etc 1.172 + self.end_headers() 1.173 + self.copyfileobj(f, self.wfile) 1.174 + return f.close() 1.175 + except IOError: 1.176 + self.send_header('Content-Type', "text/plain") 1.177 + body = self.default_reply % (req, id) 1.178 + self.send_header('Content-Length', len(body)) 1.179 + self.end_headers() 1.180 + self.wfile.write(body) 1.181 + 1.182 + def send_response(self, line, msg=None): 1.183 + if msg: 1.184 + return BaseHTTPServer.BaseHTTPRequestHandler.send_response(self, line,msg) 1.185 + try: 1.186 + x = int(line) 1.187 + BaseHTTPServer.BaseHTTPRequestHandler.send_response(self, x) 1.188 + except ValueError: 1.189 + tuple = string.split(line, ' ',2) 1.190 + ## XXXX 1.191 + old = self.protocol_version 1.192 + self.protocol_version = tuple[0] 1.193 + BaseHTTPServer.BaseHTTPRequestHandler.send_response(self, int(tuple[1]), tuple[2][:-1]) 1.194 + self.protocol_version = old 1.195 + 1.196 +import socket 1.197 + 1.198 +# I need to thread this, with the mixin class 1.199 +class RegressionHTTPServer(BaseHTTPServer.HTTPServer): 1.200 + # The 1.5.2 version doesn't do this: 1.201 + def server_bind(self): 1.202 + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 1.203 + BaseHTTPServer.HTTPServer.server_bind(self) 1.204 + 1.205 +def run(HandlerClass = RegressionHTTPRequestHandler, 1.206 + ServerClass = RegressionHTTPServer): 1.207 + BaseHTTPServer.test(HandlerClass, ServerClass) 1.208 + 1.209 +if __name__ == '__main__': 1.210 + run()