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