| #!/usr/bin/env python |
| # Copyright (c) 2012 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 base64 |
| import json |
| import optparse |
| import os |
| import socket |
| import sys |
| import time |
| import traceback |
| |
| from tvcm import project as project_module |
| from tvcm import generate |
| from tvcm import resource_loader |
| |
| import SocketServer |
| import SimpleHTTPServer |
| import StringIO |
| import BaseHTTPServer |
| |
| |
| class DevServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): |
| def __init__(self, *args, **kwargs): |
| SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs) |
| |
| def send_response(self, code, message=None): |
| SimpleHTTPServer.SimpleHTTPRequestHandler.send_response(self, code, message) |
| if code == 200: |
| self.send_header('Cache-Control', 'no-cache') |
| |
| def do_GET(self): |
| if self.do_path_handler('GET'): |
| return |
| |
| return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) |
| |
| def do_POST(self): |
| if self.do_path_handler('POST'): |
| return |
| return SimpleHTTPServer.SimpleHTTPRequestHandler.do_POST(self) |
| |
| |
| def do_path_handler(self, method): |
| handler = self.server.GetPathHandler(self.path, method) |
| if handler: |
| try: |
| handler(self) |
| except Exception, ex: |
| send_500(self, "While parsing %s" % self.path, ex, path=self.path) |
| return True |
| return False |
| |
| def send_head(self): |
| return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self) |
| |
| def translate_path(self, path): |
| path = path.split('?',1)[0] |
| path = path.split('#',1)[0] |
| for mapped_path in self.server.project.source_paths: |
| rel = os.path.relpath(path, '/') |
| candidate = os.path.join(mapped_path, rel) |
| if os.path.exists(candidate): |
| return candidate |
| return '' |
| |
| def log_error(self, format, *args): |
| if self.server._quiet: |
| return |
| if self.path == '/favicon.ico': |
| return |
| self.log_message("While processing %s: ", self.path) |
| SimpleHTTPServer.SimpleHTTPRequestHandler.log_error(self, format, *args) |
| |
| def log_request(self, code='-', size='-'): |
| # Dont spam the console unless it is important. |
| pass |
| |
| def finish(self): |
| try: |
| SimpleHTTPServer.SimpleHTTPRequestHandler.finish(self) |
| except socket.error: |
| # An final socket error may have occurred here, such as |
| # the local error ECONNABORTED. |
| pass |
| |
| def send_500(self, msg, ex, log_error=True, path=None): |
| if path == None: |
| is_html_output = False |
| else: |
| path = path.split('?',1)[0] |
| path = path.split('#',1)[0] |
| is_html_output = path.endswith('.html') |
| |
| |
| if is_html_output: |
| msg = """<!DOCTYPE html> |
| <html> |
| <body> |
| <h1>OMG something is wrong</h1> |
| <b><pre><code id="message"></code></pre></b></p> |
| <pre><code id="details"></code></pre> |
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| document.querySelector('#details').textContent = %s; |
| document.querySelector('#message').textContent = %s; |
| }); |
| </script> |
| </body> |
| </html> |
| """ % (json.dumps(traceback.format_exc()), json.dumps(ex.message)) |
| ctype = 'text/html' |
| else: |
| msg = json.dumps({"details": traceback.format_exc(), |
| "message": ex.message}); |
| ctype = 'application/json' |
| |
| if log_error: |
| self.log_error('%s: %s', msg, ex.message) |
| self.send_response(500) |
| self.send_header('Content-Type', ctype) |
| self.send_header('Cache-Control', 'no-cache') |
| self.send_header('Content-Length', len(msg)) |
| self.end_headers() |
| self.wfile.write(msg) |
| return |
| |
| class PathHandler(object): |
| def __init__(self, path, handler, supports_get, supports_post): |
| self.path = path |
| self.handler = handler |
| self.supports_get = supports_get |
| self.supports_post = supports_post |
| |
| def CanHandle(self, path, method): |
| if path != self.path: |
| return False |
| if method == 'GET' and self.supports_get: |
| return True |
| if method == 'POST' and self.supports_post: |
| return True |
| return False |
| |
| def do_GET_root(request): |
| request.send_response(301) |
| request.send_header("Location", request.server.default_path) |
| request.end_headers() |
| |
| class DevServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): |
| def __init__(self, port, quiet=False, project=None): |
| BaseHTTPServer.HTTPServer.__init__( |
| self, ('localhost', port), DevServerHandler) |
| self._shutdown_request = None |
| self._quiet = quiet |
| if port == 0: |
| port = self.server_address[1] |
| self._port = port |
| self._path_handlers = [] |
| self._test_links = [] |
| if project: |
| self._project = project |
| else: |
| self._project = project_module.Project([]) |
| |
| self.AddPathHandler('/', do_GET_root) |
| self.AddPathHandler('', do_GET_root) |
| self.default_path = '/base/tests.html' |
| # Redirect old tests.html places to the new location until folks have gotten used to its new |
| # location. |
| self.AddPathHandler('/tvcm/tests.html', do_GET_root) |
| self.AddPathHandler('/tests.html', do_GET_root) |
| |
| |
| def AddPathHandler(self, path, handler, supports_get=True, supports_post=False): |
| self._path_handlers.append(PathHandler(path, handler, supports_get, supports_post)) |
| |
| def GetPathHandler(self, path, method): |
| for h in self._path_handlers: |
| if h.CanHandle(path, method): |
| return h.handler |
| return None |
| |
| def AddSourcePathMapping(self, file_system_path): |
| self._project.AddSourcePath(file_system_path) |
| |
| def AddTestLink(self, path, title): |
| self._test_links.append({'path': path, |
| 'title': title}) |
| |
| def RequestShutdown(self, exit_code): |
| self._shutdown_request = exit_code |
| |
| @property |
| def test_links(self): |
| return self._test_links |
| |
| @property |
| def project(self): |
| return self._project |
| |
| @property |
| def loader(self): |
| return self._project.loader |
| |
| @property |
| def port(self): |
| return self._port |
| |
| def serve_forever(self): |
| if not self._quiet: |
| sys.stderr.write("Now running on http://localhost:%i\n" % self._port) |
| try: |
| self.timeout = 0.5 |
| while True: |
| BaseHTTPServer.HTTPServer.handle_request(self) |
| if self._shutdown_request is not None: |
| sys.exit(self._shutdown_request) |
| except KeyboardInterrupt: |
| sys.exit(0) |