Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(497)

Side by Side Diff: ppapi/native_client/tools/browser_tester/browsertester/server.py

Issue 10914053: Relocating files in the nacl repo that belong in chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: merge Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ppapi/native_client/tools/browser_tester/browsertester/rpclistener.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import BaseHTTPServer
6 import cgi
7 import mimetypes
8 import os
9 import os.path
10 import posixpath
11 import SimpleHTTPServer
12 import SocketServer
13 import threading
14 import time
15 import urllib
16 import urlparse
17
18 class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
19
20 def NormalizePath(self, path):
21 path = path.split('?', 1)[0]
22 path = path.split('#', 1)[0]
23 path = posixpath.normpath(urllib.unquote(path))
24 words = path.split('/')
25
26 bad = set((os.curdir, os.pardir, ''))
27 words = [word for word in words if word not in bad]
28 # The path of the request should always use POSIX-style path separators, so
29 # that the filename input of --map_file can be a POSIX-style path and still
30 # match correctly in translate_path().
31 return '/'.join(words)
32
33 def translate_path(self, path):
34 path = self.NormalizePath(path)
35 if path in self.server.file_mapping:
36 return self.server.file_mapping[path]
37 for extra_dir in self.server.serving_dirs:
38 # TODO(halyavin): set allowed paths in another parameter?
39 full_path = os.path.join(extra_dir, os.path.basename(path))
40 if os.path.isfile(full_path):
41 return full_path
42 if not path.endswith('favicon.ico') and not self.server.allow_404:
43 self.server.listener.ServerError('Cannot find file \'%s\'' % path)
44 return path
45
46 def guess_type(self, path):
47 # We store the extension -> MIME type mapping in the server instead of the
48 # request handler so we that can add additional mapping entries via the
49 # command line.
50 base, ext = posixpath.splitext(path)
51 if ext in self.server.extensions_mapping:
52 return self.server.extensions_mapping[ext]
53 ext = ext.lower()
54 if ext in self.server.extensions_mapping:
55 return self.server.extensions_mapping[ext]
56 else:
57 return self.server.extensions_mapping['']
58
59 def SendRPCResponse(self, response):
60 self.send_response(200)
61 self.send_header("Content-type", "text/plain")
62 self.send_header("Content-length", str(len(response)))
63 self.end_headers()
64 self.wfile.write(response)
65
66 # shut down the connection
67 self.wfile.flush()
68 self.connection.shutdown(1)
69
70 def HandleRPC(self, name, query):
71 kargs = {}
72 for k, v in query.iteritems():
73 assert len(v) == 1, k
74 kargs[k] = v[0]
75
76 l = self.server.listener
77 try:
78 response = getattr(l, name)(**kargs)
79 except Exception, e:
80 self.SendRPCResponse('%r' % (e,))
81 raise
82 else:
83 self.SendRPCResponse(response)
84
85 # For Last-Modified-based caching, the timestamp needs to be old enough
86 # for the browser cache to be used (at least 60 seconds).
87 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
88 # Often we clobber and regenerate files for testing, so this is needed
89 # to actually use the browser cache.
90 def send_header(self, keyword, value):
91 if keyword == 'Last-Modified':
92 last_mod_format = '%a, %d %b %Y %H:%M:%S GMT'
93 old_value_as_t = time.strptime(value, last_mod_format)
94 old_value_in_secs = time.mktime(old_value_as_t)
95 new_value_in_secs = old_value_in_secs - 360
96 value = time.strftime(last_mod_format,
97 time.localtime(new_value_in_secs))
98 SimpleHTTPServer.SimpleHTTPRequestHandler.send_header(self,
99 keyword,
100 value)
101
102 def do_POST(self):
103 # Backwards compatible - treat result as tuple without named fields.
104 _, _, path, _, query, _ = urlparse.urlparse(self.path)
105
106 self.server.listener.Log('POST %s (%s)' % (self.path, path))
107 if path == '/echo':
108 self.send_response(200)
109 self.end_headers()
110 data = self.rfile.read(int(self.headers.getheader('content-length')))
111 self.wfile.write(data)
112 else:
113 self.send_error(404, 'File not found')
114
115 self.server.ResetTimeout()
116
117 def do_GET(self):
118 # Backwards compatible - treat result as tuple without named fields.
119 _, _, path, _, query, _ = urlparse.urlparse(self.path)
120
121 tester = '/TESTER/'
122 if path.startswith(tester):
123 # If the path starts with '/TESTER/', the GET is an RPC call.
124 name = path[len(tester):]
125 # Supporting Python 2.5 prevents us from using urlparse.parse_qs
126 query = cgi.parse_qs(query, True)
127
128 self.server.rpc_lock.acquire()
129 try:
130 self.HandleRPC(name, query)
131 finally:
132 self.server.rpc_lock.release()
133
134 # Don't reset the timeout. This is not "part of the test", rather it's
135 # used to tell us if the renderer process is still alive.
136 if name == 'JavaScriptIsAlive':
137 self.server.JavaScriptIsAlive()
138 return
139
140 elif path in self.server.redirect_mapping:
141 dest = self.server.redirect_mapping[path]
142 self.send_response(301, 'Moved')
143 self.send_header('Location', dest)
144 self.end_headers()
145 self.wfile.write(self.error_message_format %
146 {'code': 301,
147 'message': 'Moved',
148 'explain': 'Object moved permanently'})
149 self.server.listener.Log('REDIRECT %s (%s -> %s)' %
150 (self.path, path, dest))
151 else:
152 self.server.listener.Log('GET %s (%s)' % (self.path, path))
153 # A normal GET request for transferring files, etc.
154 f = self.send_head()
155 if f:
156 self.copyfile(f, self.wfile)
157 f.close()
158
159 self.server.ResetTimeout()
160
161 def copyfile(self, source, outputfile):
162 # Bandwidth values <= 0.0 are considered infinite
163 if self.server.bandwidth <= 0.0:
164 return SimpleHTTPServer.SimpleHTTPRequestHandler.copyfile(
165 self, source, outputfile)
166
167 self.server.listener.Log('Simulating %f mbps server BW' %
168 self.server.bandwidth)
169 chunk_size = 1500 # What size to use?
170 bits_per_sec = self.server.bandwidth * 1000000
171 start_time = time.time()
172 data_sent = 0
173 while True:
174 chunk = source.read(chunk_size)
175 if len(chunk) == 0:
176 break
177 cur_elapsed = time.time() - start_time
178 target_elapsed = (data_sent + len(chunk)) * 8 / bits_per_sec
179 if (cur_elapsed < target_elapsed):
180 time.sleep(target_elapsed - cur_elapsed)
181 outputfile.write(chunk)
182 data_sent += len(chunk)
183 self.server.listener.Log('Streamed %d bytes in %f s' %
184 (data_sent, time.time() - start_time))
185
186 # Disable the built-in logging
187 def log_message(self, format, *args):
188 pass
189
190
191 # The ThreadingMixIn allows the server to handle multiple requests
192 # concurently (or at least as concurently as Python allows). This is desirable
193 # because server sockets only allow a limited "backlog" of pending connections
194 # and in the worst case the browser could make multiple connections and exceed
195 # this backlog - causing the server to drop requests. Using ThreadingMixIn
196 # helps reduce the chance this will happen.
197 # There were apparently some problems using this Mixin with Python 2.5, but we
198 # are no longer using anything older than 2.6.
199 class Server(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
200
201 def Configure(
202 self, file_mapping, redirect_mapping, extensions_mapping, allow_404,
203 bandwidth, listener, serving_dirs=[]):
204 self.file_mapping = file_mapping
205 self.redirect_mapping = redirect_mapping
206 self.extensions_mapping.update(extensions_mapping)
207 self.allow_404 = allow_404
208 self.bandwidth = bandwidth
209 self.listener = listener
210 self.rpc_lock = threading.Lock()
211 self.serving_dirs = serving_dirs
212
213 def TestingBegun(self, timeout):
214 self.test_in_progress = True
215 # self.timeout does not affect Python 2.5.
216 self.timeout = timeout
217 self.ResetTimeout()
218 self.JavaScriptIsAlive()
219 # Have we seen any requests from the browser?
220 self.received_request = False
221
222 def ResetTimeout(self):
223 self.last_activity = time.time()
224 self.received_request = True
225
226 def JavaScriptIsAlive(self):
227 self.last_js_activity = time.time()
228
229 def TimeSinceJSHeartbeat(self):
230 return time.time() - self.last_js_activity
231
232 def TestingEnded(self):
233 self.test_in_progress = False
234
235 def TimedOut(self, total_time):
236 return (total_time >= 0.0 and
237 (time.time() - self.last_activity) >= total_time)
238
239
240 def Create(host, port):
241 server = Server((host, port), RequestHandler)
242 server.extensions_mapping = mimetypes.types_map.copy()
243 server.extensions_mapping.update({
244 '': 'application/octet-stream' # Default
245 })
246 return server
OLDNEW
« no previous file with comments | « ppapi/native_client/tools/browser_tester/browsertester/rpclistener.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698