tools/httptester/server.py

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial