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 logging 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import multiprocessing 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import SimpleHTTPServer # pylint: disable=W0611 122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import socket 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import time 152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import urlparse 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)if sys.version_info < (2, 6, 0): 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sys.stderr.write("python 2.6 or later is required run this script\n") 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sys.exit(1) 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NACL_SDK_ROOT = os.path.dirname(SCRIPT_DIR) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We only run from the examples directory so that not too much is exposed 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# via this HTTP server. Everything in the directory is served, so there should 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# never be anything potentially sensitive in the serving directory, especially 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# if the machine might be a multi-user machine and not all users are trusted. 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We only serve via the loopback interface. 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def SanityCheckDirectory(dirname): 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) abs_serve_dir = os.path.abspath(dirname) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Verify we don't serve anywhere above NACL_SDK_ROOT. 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if abs_serve_dir[:len(NACL_SDK_ROOT)] == NACL_SDK_ROOT: 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.error('For security, httpd.py should only be run from within the') 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.error('example directory tree.') 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.error('Attempting to serve from %s.' % abs_serve_dir) 404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) logging.error('Run with --no-dir-check to bypass this check.') 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(1) 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 44116680a4aac90f2aa7413d9095a592090648e557Ben Murdochclass HTTPServer(BaseHTTPServer.HTTPServer): 452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def __init__(self, *args, **kwargs): 462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BaseHTTPServer.HTTPServer.__init__(self, *args) 472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.running = True 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.result = 0 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def Shutdown(self, result=0): 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.running = False 522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.result = result 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 55116680a4aac90f2aa7413d9095a592090648e557Ben Murdochclass HTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def _SendNothingAndDie(self, result=0): 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.send_response(200, 'OK') 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.send_header('Content-type', 'text/html') 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.send_header('Content-length', '0') 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.end_headers() 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.server.Shutdown(result) 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def do_GET(self): 64116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch # Browsing to ?quit=1 will kill the server cleanly. 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) _, _, _, query, _ = urlparse.urlsplit(self.path) 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if query: 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) params = urlparse.parse_qs(query) 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if '1' in params.get('quit', []): 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self._SendNothingAndDie() 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LocalHTTPServer(object): 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Class to start a local HTTP server as a child process.""" 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 78116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def __init__(self, dirname, port): 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parent_conn, child_conn = multiprocessing.Pipe() 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.process = multiprocessing.Process( 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) target=_HTTPServerProcess, 82116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch args=(child_conn, dirname, port, {})) 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.process.start() 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if parent_conn.poll(10): # wait 10 seconds 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.port = parent_conn.recv() 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise Exception('Unable to launch HTTP server.') 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.conn = parent_conn 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def ServeForever(self): 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Serve until the child HTTP process tells us to stop. 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Returns: 952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) The result from the child (as an errorcode), or 0 if the server was 962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) killed not by the child (by KeyboardInterrupt for example). 972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """ 982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = 0 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Block on this pipe, waiting for a response from the child process. 1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = self.conn.recv() 1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except KeyboardInterrupt: 1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pass 1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) finally: 1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.Shutdown() 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return child_result 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def ServeUntilSubprocessDies(self, process): 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Serve until the child HTTP process tells us to stop or |subprocess| dies. 1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Returns: 1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) The result from the child (as an errorcode), or 0 if |subprocess| died, 1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) or the server was killed some other way (by KeyboardInterrupt for 1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) example). 1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """ 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = 0 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) while True: 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if process.poll() is not None: 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = 0 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if self.conn.poll(): 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) child_result = self.conn.recv() 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) time.sleep(0) 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except KeyboardInterrupt: 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pass 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) finally: 1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.Shutdown() 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return child_result 1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Shutdown(self): 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Send a message to the child HTTP server process and wait for it to 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) finish.""" 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.conn.send(False) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.process.join() 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetURL(self, rel_url): 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Get the full url for a file on the local HTTP server. 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rel_url: A URL fragment to convert to a full URL. For example, 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetURL('foobar.baz') -> 'http://localhost:1234/foobar.baz' 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 'http://localhost:%d/%s' % (self.port, rel_url) 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def _HTTPServerProcess(conn, dirname, port, server_kwargs): 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Run a local httpserver with the given port or an ephemeral port. 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) This function assumes it is run as a child process using multiprocessing. 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) conn: A connection to the parent process. The child process sends 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) the local port, and waits for a message from the parent to 1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) stop serving. It also sends a "result" back to the parent -- this can 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) be used to allow a client-side test to notify the server of results. 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirname: The directory to serve. All files are accessible through 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) http://localhost:<port>/path/to/filename. 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) port: The port to serve on. If 0, an ephemeral port will be chosen. 1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) server_kwargs: A dict that will be passed as kwargs to the server. 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.chdir(dirname) 165116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch httpd = HTTPServer(('', port), HTTPRequestHandler, **server_kwargs) 1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except socket.error as e: 1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sys.stderr.write('Error creating HTTPServer: %s\n' % e) 1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sys.exit(1) 1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) conn.send(httpd.server_address[1]) # the chosen port number 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) httpd.timeout = 0.5 # seconds 1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) while httpd.running: 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Flush output for MSVS Add-In. 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.stdout.flush() 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.stderr.flush() 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) httpd.handle_request() 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if conn.poll(): 1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) httpd.running = conn.recv() 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except KeyboardInterrupt: 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pass 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) finally: 1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) conn.send(httpd.result) 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) conn.close() 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main(args): 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser = optparse.OptionParser() 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('-C', '--serve-dir', 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help='Serve files out of this directory.', 1914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) default=os.path.abspath('.')) 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parser.add_option('-p', '--port', 1934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) help='Run server on this port.', default=5103) 1944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) parser.add_option('--no-dir-check', '--no_dir_check', 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) help='No check to ensure serving from safe directory.', 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dest='do_safe_check', action='store_false', default=True) 1970f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 1980f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) # To enable bash completion for this command first install optcomplete 1990f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) # and then add this line to your .bashrc: 2000f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) # complete -F _optcomplete httpd.py 2010f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) try: 2020f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) import optcomplete 2030f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) optcomplete.autocomplete(parser) 2040f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) except ImportError: 2050f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) pass 2060f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) options, args = parser.parse_args(args) 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if options.do_safe_check: 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SanityCheckDirectory(options.serve_dir) 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 211116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch server = LocalHTTPServer(options.serve_dir, int(options.port)) 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Serve until the client tells us to stop. When it does, it will give us an 2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # errorcode. 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print 'Serving %s on %s...' % (options.serve_dir, server.GetURL('')) 2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return server.ServeForever() 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__': 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sys.exit(main(sys.argv[1:])) 220