| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 | |
| 7 """Replay server for Gmail benchmark.""" | |
| 8 | |
| 9 | |
| 10 import BaseHTTPServer | |
| 11 import binascii | |
| 12 import cgi | |
| 13 from hashlib import md5 # pylint: disable=E0611 | |
| 14 from optparse import OptionParser | |
| 15 import os | |
| 16 import re | |
| 17 import simplejson as json | |
| 18 import SocketServer | |
| 19 import sys | |
| 20 import urllib | |
| 21 | |
| 22 _BUSTERS = ['random', 'zx', 'ai', 'ik', 'jsid', 'it', 'seid', 'rid'] | |
| 23 | |
| 24 _UPHEADERS = ['Refresh', 'Content-Type', 'Set-Cookie'] | |
| 25 | |
| 26 _LOCATION = ('/berlin.zrh.corp.google.com:9380/mail/' | |
| 27 '?shva=1&jsmode=DEBUG_OPTIMIZED#') | |
| 28 | |
| 29 def DefReBuster(buster): | |
| 30 return buster + r'((' + urllib.quote_plus('=') + r')|=)[^\&]+' | |
| 31 | |
| 32 _RE_BUSTERS = [re.compile(DefReBuster(buster_)) for buster_ in _BUSTERS] | |
| 33 | |
| 34 | |
| 35 def __RemoveCacheBusters(path): | |
| 36 for re_buster in _RE_BUSTERS: | |
| 37 path = re.sub(re_buster, '', path) | |
| 38 return path | |
| 39 | |
| 40 | |
| 41 class ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |
| 42 """Proxy handler class.""" | |
| 43 | |
| 44 def __init__(self, request, client_address, server, benchmark): | |
| 45 self.benchmark = benchmark | |
| 46 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, | |
| 47 client_address, server) | |
| 48 | |
| 49 def do_GET(self): | |
| 50 """The GET handler.""" | |
| 51 if self.path == '/': | |
| 52 self.send_response(302) | |
| 53 self.send_header('Location', _LOCATION) | |
| 54 self.end_headers() | |
| 55 return | |
| 56 else: | |
| 57 self.__ProcessRequest() | |
| 58 | |
| 59 def do_POST(self): | |
| 60 """The post handler. | |
| 61 | |
| 62 Handles replayed post requests as well as result reporting | |
| 63 """ | |
| 64 if self.path.startswith('/postResult'): | |
| 65 self.__PostResults() | |
| 66 else: | |
| 67 self.do_GET() | |
| 68 | |
| 69 do_HEAD = do_GET | |
| 70 do_PUT = do_GET | |
| 71 do_CONNECT = do_GET | |
| 72 | |
| 73 def __BrowserString(self): | |
| 74 agent = str(self.headers['user-agent']) | |
| 75 substrings = [] | |
| 76 position = 0 | |
| 77 depth = 0 | |
| 78 for i in xrange(len(agent)): | |
| 79 if agent[i] == '(': | |
| 80 if depth == 0: | |
| 81 substrings.append(agent[position:i]) | |
| 82 depth += 1 | |
| 83 if agent[i] == ')': | |
| 84 depth -= 1 | |
| 85 if depth == 0: | |
| 86 position = i + 1 | |
| 87 substrings.append(agent[position:]) | |
| 88 agent = ''.join(substrings) | |
| 89 agent = re.sub('\([^\)]+\)', '', agent) | |
| 90 agent = re.sub('\/[0-9.]+', '', agent) | |
| 91 agent = re.sub(' +', '-', agent) | |
| 92 return agent.lower() | |
| 93 | |
| 94 def __ProcessRequest(self): | |
| 95 browserstring = self.__BrowserString() | |
| 96 path = __RemoveCacheBusters(self.path)[1:] | |
| 97 key = md5(path).hexdigest() | |
| 98 try: | |
| 99 cachedresponse = self.benchmark.GetResponse(browserstring, key) | |
| 100 except KeyError: | |
| 101 self.send_response(404, 'Not in cache!') | |
| 102 return | |
| 103 | |
| 104 self.send_response(int(cachedresponse['code']), 'I have my reasons.') | |
| 105 for key, value in cachedresponse['header'].iteritems(): | |
| 106 if key in _UPHEADERS: | |
| 107 self.send_header(key, value) | |
| 108 self.end_headers() | |
| 109 self.wfile.write(cachedresponse['body']) | |
| 110 | |
| 111 def __PostResults(self): | |
| 112 length = int(self.headers['content-length']) | |
| 113 result = cgi.parse_qs(self.rfile.read(length))['result'][0] | |
| 114 result = json.loads(result)[0] | |
| 115 self.benchmark.PostResult(result) | |
| 116 self.send_response(200, 'Got it.') | |
| 117 # Needed for goog.net.IframeIo, which checks the body to | |
| 118 # see if the request worked. | |
| 119 self.wfile.write('<html><body>OK</body></html>') | |
| 120 | |
| 121 def log_request(self, code='-', size='-'): | |
| 122 pass | |
| 123 | |
| 124 | |
| 125 class ThreadingHTTPServer(SocketServer.ThreadingMixIn, | |
| 126 BaseHTTPServer.HTTPServer): | |
| 127 pass | |
| 128 | |
| 129 | |
| 130 class ReplayBenchmark(object): | |
| 131 """Main benchmark class.""" | |
| 132 | |
| 133 def __init__(self, callback, data_dir, port): | |
| 134 | |
| 135 self.__callback = callback | |
| 136 self.__responses = {} | |
| 137 self.__port = port | |
| 138 self.__httpd = ThreadingHTTPServer( | |
| 139 ('', self.__port), | |
| 140 ReplayBenchmark.__HandleRequest.__get__(self)) | |
| 141 self.__LoadCachedResponses(data_dir) | |
| 142 | |
| 143 def RunForever(self): | |
| 144 print 'Listening on port %d.' % self.__port | |
| 145 self.__httpd.serve_forever() | |
| 146 | |
| 147 def PostResult(self, json_string): | |
| 148 self.__callback(json_string) | |
| 149 | |
| 150 def GetResponse(self, browserstring, key): | |
| 151 return self.__responses[browserstring][key] | |
| 152 | |
| 153 def __HandleRequest(self, request, client_address, server): | |
| 154 return ProxyHandler(request, client_address, server, self) | |
| 155 | |
| 156 def __LoadCachedResponses(self, base_dir): | |
| 157 """Preload all cached responses.""" | |
| 158 print 'Loading data...' | |
| 159 for browser in os.listdir(base_dir): | |
| 160 filesread = 0 | |
| 161 if not os.path.isdir(os.path.join(base_dir, browser)): | |
| 162 continue | |
| 163 self.__responses[browser] = {} | |
| 164 print 'Reading responses for', browser, | |
| 165 for key in os.listdir(os.path.join(base_dir, browser)): | |
| 166 sys.stdout.flush() | |
| 167 f = open(os.path.join(base_dir, browser, key)) | |
| 168 cachedresponse = json.load(f) | |
| 169 f.close() | |
| 170 if filesread % 10 == 0: | |
| 171 print '.', | |
| 172 repl = '' | |
| 173 if cachedresponse['body']: | |
| 174 lines = cachedresponse['body'].split('\n') | |
| 175 else: | |
| 176 lines = [] | |
| 177 | |
| 178 for line in lines: | |
| 179 repl += binascii.a2b_uu(line) | |
| 180 # signal that we are in replay mode. | |
| 181 | |
| 182 # uudecode here, or uuencode in php inserts a lot of 0 chars. | |
| 183 # will be fixed when we get rid of php. | |
| 184 | |
| 185 repl = repl.replace(chr(0), '') | |
| 186 repl = re.sub('<script>', '<script>DEV_BENCHMARK_REPLAY=1;', repl) | |
| 187 self.__responses[browser][key] = { | |
| 188 'code': cachedresponse['code'], | |
| 189 'header': cachedresponse['header'], | |
| 190 'body': repl | |
| 191 } | |
| 192 filesread += 1 | |
| 193 print filesread, 'read.' | |
| 194 | |
| 195 | |
| 196 def PrintResult(result): | |
| 197 print json.dumps(result) | |
| 198 | |
| 199 | |
| 200 def main(): | |
| 201 parser = OptionParser() | |
| 202 parser.add_option('-p', '--port', dest='port', type='int', default=8000) | |
| 203 parser.add_option('-d', '--dir', dest='dir', type='string') | |
| 204 options = parser.parse_args()[0] | |
| 205 benchmark = ReplayBenchmark(PrintResult, options.dir, options.port) | |
| 206 benchmark.RunForever() | |
| 207 | |
| 208 if __name__ == '__main__': | |
| 209 main() | |
| OLD | NEW |