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 |