12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#!/usr/bin/env python 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved. 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import BaseHTTPServer 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import imp 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import multiprocessing 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import SimpleHTTPServer # pylint: disable=W0611 132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import socket 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys 152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import time 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import urlparse 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)if sys.version_info < (2, 6, 0): 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sys.stderr.write("python 2.6 or later is required run this script\n") 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sys.exit(1) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NACL_SDK_ROOT = os.path.dirname(SCRIPT_DIR) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We only run from the examples directory so that not too much is exposed 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# via this HTTP server. Everything in the directory is served, so there should 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# never be anything potentially sensitive in the serving directory, especially 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# if the machine might be a multi-user machine and not all users are trusted. 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We only serve via the loopback interface. 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def SanityCheckDirectory(dirname): 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) abs_serve_dir = os.path.abspath(dirname) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Verify we don't serve anywhere above NACL_SDK_ROOT. 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if abs_serve_dir[:len(NACL_SDK_ROOT)] == NACL_SDK_ROOT: 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.error('For security, httpd.py should only be run from within the') 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.error('example directory tree.') 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.error('Attempting to serve from %s.' % abs_serve_dir) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.error('Run with --no_dir_check to bypass this check.') 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(1) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class PluggableHTTPServer(BaseHTTPServer.HTTPServer): 462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def __init__(self, *args, **kwargs): 472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BaseHTTPServer.HTTPServer.__init__(self, *args) 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.serve_dir = kwargs.get('serve_dir', '.') 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.test_mode = kwargs.get('test_mode', False) 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.delegate_map = {} 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.running = True 522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.result = 0 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def Shutdown(self, result=0): 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.running = False 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.result = result 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class PluggableHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _FindDelegateAtPath(self, dirname): 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # First check the cache... 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('Looking for cached delegate in %s...' % dirname) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) handler_script = os.path.join(dirname, 'handler.py') 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if dirname in self.server.delegate_map: 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) result = self.server.delegate_map[dirname] 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if result is None: 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('Found None.') 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('Found delegate.') 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Don't have one yet, look for one. 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate = None 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('Testing file %s for existence...' % handler_script) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.path.exists(handler_script): 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug( 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'File %s exists, looking for HTTPRequestHandlerDelegate.' % 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) handler_script) 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) module = imp.load_source('handler', handler_script) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_class = getattr(module, 'HTTPRequestHandlerDelegate', None) 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate = delegate_class() 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not delegate: 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.warn( 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Unable to find symbol HTTPRequestHandlerDelegate in module %s.' % 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) handler_script) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return delegate 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _FindDelegateForURLRecurse(self, cur_dir, abs_root): 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate = self._FindDelegateAtPath(cur_dir) 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not delegate: 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Didn't find it, try the parent directory, but stop if this is the server 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # root. 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cur_dir != abs_root: 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parent_dir = os.path.dirname(cur_dir) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate = self._FindDelegateForURLRecurse(parent_dir, abs_root) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('Adding delegate to cache for %s.' % cur_dir) 1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.server.delegate_map[cur_dir] = delegate 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return delegate 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _FindDelegateForURL(self, url_path): 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path = self.translate_path(url_path) 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if os.path.isdir(path): 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirname = path 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirname = os.path.dirname(path) 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) abs_serve_dir = os.path.abspath(self.server.serve_dir) 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate = self._FindDelegateForURLRecurse(dirname, abs_serve_dir) 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not delegate: 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.info('No handler found for path %s. Using default.' % url_path) 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return delegate 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def _SendNothingAndDie(self, result=0): 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.send_response(200, 'OK') 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.send_header('Content-type', 'text/html') 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.send_header('Content-length', '0') 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.end_headers() 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.server.Shutdown(result) 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def send_head(self): 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate = self._FindDelegateForURL(self.path) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if delegate: 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return delegate.send_head(self) 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.base_send_head() 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def base_send_head(self): 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def do_GET(self): 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # TODO(binji): pyauto tests use the ?quit=1 method to kill the server. 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Remove this when we kill the pyauto tests. 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) _, _, _, query, _ = urlparse.urlsplit(self.path) 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if query: 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) params = urlparse.parse_qs(query) 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if '1' in params.get('quit', []): 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self._SendNothingAndDie() 1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return 1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate = self._FindDelegateForURL(self.path) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if delegate: 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return delegate.do_GET(self) 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.base_do_GET() 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def base_do_GET(self): 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def do_POST(self): 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate = self._FindDelegateForURL(self.path) 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if delegate: 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return delegate.do_POST(self) 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.base_do_POST() 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def base_do_POST(self): 1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if self.server.test_mode: 1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if self.path == '/ok': 1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self._SendNothingAndDie(0) 1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) elif self.path == '/fail': 1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self._SendNothingAndDie(1) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LocalHTTPServer(object): 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Class to start a local HTTP server as a child process.""" 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def __init__(self, dirname, port, test_mode): 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parent_conn, child_conn = multiprocessing.Pipe() 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.process = multiprocessing.Process( 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) target=_HTTPServerProcess, 1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) args=(child_conn, dirname, port, { 1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'serve_dir': dirname, 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'test_mode': test_mode, 1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) })) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.process.start() 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if parent_conn.poll(10): # wait 10 seconds 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.port = parent_conn.recv() 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise Exception('Unable to launch HTTP server.') 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.conn = parent_conn 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def ServeForever(self): 1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Serve until the child HTTP process tells us to stop. 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Returns: 1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) The result from the child (as an errorcode), or 0 if the server was 1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) killed not by the child (by KeyboardInterrupt for example). 1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """ 1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = 0 1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Block on this pipe, waiting for a response from the child process. 1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = self.conn.recv() 1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except KeyboardInterrupt: 1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pass 1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) finally: 1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.Shutdown() 1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return child_result 2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def ServeUntilSubprocessDies(self, process): 2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Serve until the child HTTP process tells us to stop or |subprocess| dies. 2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Returns: 2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) The result from the child (as an errorcode), or 0 if |subprocess| died, 2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) or the server was killed some other way (by KeyboardInterrupt for 2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) example). 2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """ 2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = 0 2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) while True: 2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if process.poll() is not None: 2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = 0 2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break 2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if self.conn.poll(): 2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = self.conn.recv() 2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break 2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) time.sleep(0) 2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except KeyboardInterrupt: 2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pass 2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) finally: 2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.Shutdown() 2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return child_result 2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Shutdown(self): 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Send a message to the child HTTP server process and wait for it to 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) finish.""" 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.conn.send(False) 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.process.join() 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetURL(self, rel_url): 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Get the full url for a file on the local HTTP server. 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rel_url: A URL fragment to convert to a full URL. For example, 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetURL('foobar.baz') -> 'http://localhost:1234/foobar.baz' 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 'http://localhost:%d/%s' % (self.port, rel_url) 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def _HTTPServerProcess(conn, dirname, port, server_kwargs): 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Run a local httpserver with the given port or an ephemeral port. 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) This function assumes it is run as a child process using multiprocessing. 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) conn: A connection to the parent process. The child process sends 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) the local port, and waits for a message from the parent to 2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) stop serving. It also sends a "result" back to the parent -- this can 2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) be used to allow a client-side test to notify the server of results. 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirname: The directory to serve. All files are accessible through 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) http://localhost:<port>/path/to/filename. 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) port: The port to serve on. If 0, an ephemeral port will be chosen. 2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) server_kwargs: A dict that will be passed as kwargs to the server. 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.chdir(dirname) 2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) httpd = PluggableHTTPServer(('', port), PluggableHTTPRequestHandler, 2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) **server_kwargs) 2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except socket.error as e: 2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sys.stderr.write('Error creating HTTPServer: %s\n' % e) 2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sys.exit(1) 2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) conn.send(httpd.server_address[1]) # the chosen port number 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) httpd.timeout = 0.5 # seconds 2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) while httpd.running: 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Flush output for MSVS Add-In. 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.stdout.flush() 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.stderr.flush() 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) httpd.handle_request() 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if conn.poll(): 2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) httpd.running = conn.recv() 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except KeyboardInterrupt: 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) finally: 2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) conn.send(httpd.result) 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) conn.close() 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main(args): 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser = optparse.OptionParser() 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('-C', '--serve-dir', 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help='Serve files out of this directory.', 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dest='serve_dir', default=os.path.abspath('.')) 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('-p', '--port', 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help='Run server on this port.', 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dest='port', default=5103) 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('--no_dir_check', 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help='No check to ensure serving from safe directory.', 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dest='do_safe_check', action='store_false', default=True) 2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) parser.add_option('--test-mode', 2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) help='Listen for posts to /ok or /fail and shut down the server with ' 2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ' errorcodes 0 and 1 respectively.', 2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) dest='test_mode', action='store_true') 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) options, args = parser.parse_args(args) 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if options.do_safe_check: 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SanityCheckDirectory(options.serve_dir) 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) server = LocalHTTPServer(options.serve_dir, int(options.port), 3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) options.test_mode) 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Serve until the client tells us to stop. When it does, it will give us an 3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # errorcode. 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print 'Serving %s on %s...' % (options.serve_dir, server.GetURL('')) 3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return server.ServeForever() 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__': 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(main(sys.argv[1:])) 310