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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ppapi/native_client/tools/browser_tester/browsertester/rpclistener.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ppapi/native_client/tools/browser_tester/browsertester/server.py
diff --git a/ppapi/native_client/tools/browser_tester/browsertester/server.py b/ppapi/native_client/tools/browser_tester/browsertester/server.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff885808b057ccf9b127e304513825b3755b6402
--- /dev/null
+++ b/ppapi/native_client/tools/browser_tester/browsertester/server.py
@@ -0,0 +1,246 @@
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import BaseHTTPServer
+import cgi
+import mimetypes
+import os
+import os.path
+import posixpath
+import SimpleHTTPServer
+import SocketServer
+import threading
+import time
+import urllib
+import urlparse
+
+class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+
+ def NormalizePath(self, path):
+ path = path.split('?', 1)[0]
+ path = path.split('#', 1)[0]
+ path = posixpath.normpath(urllib.unquote(path))
+ words = path.split('/')
+
+ bad = set((os.curdir, os.pardir, ''))
+ words = [word for word in words if word not in bad]
+ # The path of the request should always use POSIX-style path separators, so
+ # that the filename input of --map_file can be a POSIX-style path and still
+ # match correctly in translate_path().
+ return '/'.join(words)
+
+ def translate_path(self, path):
+ path = self.NormalizePath(path)
+ if path in self.server.file_mapping:
+ return self.server.file_mapping[path]
+ for extra_dir in self.server.serving_dirs:
+ # TODO(halyavin): set allowed paths in another parameter?
+ full_path = os.path.join(extra_dir, os.path.basename(path))
+ if os.path.isfile(full_path):
+ return full_path
+ if not path.endswith('favicon.ico') and not self.server.allow_404:
+ self.server.listener.ServerError('Cannot find file \'%s\'' % path)
+ return path
+
+ def guess_type(self, path):
+ # We store the extension -> MIME type mapping in the server instead of the
+ # request handler so we that can add additional mapping entries via the
+ # command line.
+ base, ext = posixpath.splitext(path)
+ if ext in self.server.extensions_mapping:
+ return self.server.extensions_mapping[ext]
+ ext = ext.lower()
+ if ext in self.server.extensions_mapping:
+ return self.server.extensions_mapping[ext]
+ else:
+ return self.server.extensions_mapping['']
+
+ def SendRPCResponse(self, response):
+ self.send_response(200)
+ self.send_header("Content-type", "text/plain")
+ self.send_header("Content-length", str(len(response)))
+ self.end_headers()
+ self.wfile.write(response)
+
+ # shut down the connection
+ self.wfile.flush()
+ self.connection.shutdown(1)
+
+ def HandleRPC(self, name, query):
+ kargs = {}
+ for k, v in query.iteritems():
+ assert len(v) == 1, k
+ kargs[k] = v[0]
+
+ l = self.server.listener
+ try:
+ response = getattr(l, name)(**kargs)
+ except Exception, e:
+ self.SendRPCResponse('%r' % (e,))
+ raise
+ else:
+ self.SendRPCResponse(response)
+
+ # For Last-Modified-based caching, the timestamp needs to be old enough
+ # for the browser cache to be used (at least 60 seconds).
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
+ # Often we clobber and regenerate files for testing, so this is needed
+ # to actually use the browser cache.
+ def send_header(self, keyword, value):
+ if keyword == 'Last-Modified':
+ last_mod_format = '%a, %d %b %Y %H:%M:%S GMT'
+ old_value_as_t = time.strptime(value, last_mod_format)
+ old_value_in_secs = time.mktime(old_value_as_t)
+ new_value_in_secs = old_value_in_secs - 360
+ value = time.strftime(last_mod_format,
+ time.localtime(new_value_in_secs))
+ SimpleHTTPServer.SimpleHTTPRequestHandler.send_header(self,
+ keyword,
+ value)
+
+ def do_POST(self):
+ # Backwards compatible - treat result as tuple without named fields.
+ _, _, path, _, query, _ = urlparse.urlparse(self.path)
+
+ self.server.listener.Log('POST %s (%s)' % (self.path, path))
+ if path == '/echo':
+ self.send_response(200)
+ self.end_headers()
+ data = self.rfile.read(int(self.headers.getheader('content-length')))
+ self.wfile.write(data)
+ else:
+ self.send_error(404, 'File not found')
+
+ self.server.ResetTimeout()
+
+ def do_GET(self):
+ # Backwards compatible - treat result as tuple without named fields.
+ _, _, path, _, query, _ = urlparse.urlparse(self.path)
+
+ tester = '/TESTER/'
+ if path.startswith(tester):
+ # If the path starts with '/TESTER/', the GET is an RPC call.
+ name = path[len(tester):]
+ # Supporting Python 2.5 prevents us from using urlparse.parse_qs
+ query = cgi.parse_qs(query, True)
+
+ self.server.rpc_lock.acquire()
+ try:
+ self.HandleRPC(name, query)
+ finally:
+ self.server.rpc_lock.release()
+
+ # Don't reset the timeout. This is not "part of the test", rather it's
+ # used to tell us if the renderer process is still alive.
+ if name == 'JavaScriptIsAlive':
+ self.server.JavaScriptIsAlive()
+ return
+
+ elif path in self.server.redirect_mapping:
+ dest = self.server.redirect_mapping[path]
+ self.send_response(301, 'Moved')
+ self.send_header('Location', dest)
+ self.end_headers()
+ self.wfile.write(self.error_message_format %
+ {'code': 301,
+ 'message': 'Moved',
+ 'explain': 'Object moved permanently'})
+ self.server.listener.Log('REDIRECT %s (%s -> %s)' %
+ (self.path, path, dest))
+ else:
+ self.server.listener.Log('GET %s (%s)' % (self.path, path))
+ # A normal GET request for transferring files, etc.
+ f = self.send_head()
+ if f:
+ self.copyfile(f, self.wfile)
+ f.close()
+
+ self.server.ResetTimeout()
+
+ def copyfile(self, source, outputfile):
+ # Bandwidth values <= 0.0 are considered infinite
+ if self.server.bandwidth <= 0.0:
+ return SimpleHTTPServer.SimpleHTTPRequestHandler.copyfile(
+ self, source, outputfile)
+
+ self.server.listener.Log('Simulating %f mbps server BW' %
+ self.server.bandwidth)
+ chunk_size = 1500 # What size to use?
+ bits_per_sec = self.server.bandwidth * 1000000
+ start_time = time.time()
+ data_sent = 0
+ while True:
+ chunk = source.read(chunk_size)
+ if len(chunk) == 0:
+ break
+ cur_elapsed = time.time() - start_time
+ target_elapsed = (data_sent + len(chunk)) * 8 / bits_per_sec
+ if (cur_elapsed < target_elapsed):
+ time.sleep(target_elapsed - cur_elapsed)
+ outputfile.write(chunk)
+ data_sent += len(chunk)
+ self.server.listener.Log('Streamed %d bytes in %f s' %
+ (data_sent, time.time() - start_time))
+
+ # Disable the built-in logging
+ def log_message(self, format, *args):
+ pass
+
+
+# The ThreadingMixIn allows the server to handle multiple requests
+# concurently (or at least as concurently as Python allows). This is desirable
+# because server sockets only allow a limited "backlog" of pending connections
+# and in the worst case the browser could make multiple connections and exceed
+# this backlog - causing the server to drop requests. Using ThreadingMixIn
+# helps reduce the chance this will happen.
+# There were apparently some problems using this Mixin with Python 2.5, but we
+# are no longer using anything older than 2.6.
+class Server(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
+
+ def Configure(
+ self, file_mapping, redirect_mapping, extensions_mapping, allow_404,
+ bandwidth, listener, serving_dirs=[]):
+ self.file_mapping = file_mapping
+ self.redirect_mapping = redirect_mapping
+ self.extensions_mapping.update(extensions_mapping)
+ self.allow_404 = allow_404
+ self.bandwidth = bandwidth
+ self.listener = listener
+ self.rpc_lock = threading.Lock()
+ self.serving_dirs = serving_dirs
+
+ def TestingBegun(self, timeout):
+ self.test_in_progress = True
+ # self.timeout does not affect Python 2.5.
+ self.timeout = timeout
+ self.ResetTimeout()
+ self.JavaScriptIsAlive()
+ # Have we seen any requests from the browser?
+ self.received_request = False
+
+ def ResetTimeout(self):
+ self.last_activity = time.time()
+ self.received_request = True
+
+ def JavaScriptIsAlive(self):
+ self.last_js_activity = time.time()
+
+ def TimeSinceJSHeartbeat(self):
+ return time.time() - self.last_js_activity
+
+ def TestingEnded(self):
+ self.test_in_progress = False
+
+ def TimedOut(self, total_time):
+ return (total_time >= 0.0 and
+ (time.time() - self.last_activity) >= total_time)
+
+
+def Create(host, port):
+ server = Server((host, port), RequestHandler)
+ server.extensions_mapping = mimetypes.types_map.copy()
+ server.extensions_mapping.update({
+ '': 'application/octet-stream' # Default
+ })
+ return server
« 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