text: #!/usr/bin/env python3
  #
  # 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.
  
  """Provides a convenient wrapper for spawning a test lighttpd instance.
  
  Usage:
    lighttpd_server PATH_TO_DOC_ROOT
  """
  
  from __future__ import print_function
  
  import codecs
  import contextlib
  import os
  import random
  import shutil
  import socket
  import subprocess
  import sys
  import tempfile
  import time
  
  from six.moves import http_client
  from six.moves import input  # pylint: disable=redefined-builtin
  
  from pylib import constants
  from pylib import pexpect
  
  
  class LighttpdServer(object):
    """Wraps lighttpd server, providing robust startup.
  
    Args:
      document_root: Path to root of this server's hosted files.
      port: TCP port on the _host_ machine that the server will listen on. If
          omitted it will attempt to use 9000, or if unavailable it will find
          a free port from 8001 - 8999.
      lighttpd_path, lighttpd_module_path: Optional paths to lighttpd binaries.
      base_config_path: If supplied this file will replace the built-in default
          lighttpd config file.
      extra_config_contents: If specified, this string will be appended to the
          base config (default built-in, or from base_config_path).
      config_path, error_log, access_log: Optional paths where the class should
          place temporary files for this session.
    """
  
    def __init__(self, document_root, port=None,
                 lighttpd_path=None, lighttpd_module_path=None,
                 base_config_path=None, extra_config_contents=None,
                 config_path=None, error_log=None, access_log=None):
      self.temp_dir = tempfile.mkdtemp(prefix='lighttpd_for_chrome_android')
      self.document_root = os.path.abspath(document_root)
      self.fixed_port = port
      self.port = port or constants.LIGHTTPD_DEFAULT_PORT
      self.server_tag = 'LightTPD ' + str(random.randint(111111, 999999))
      self.lighttpd_path = lighttpd_path or '/usr/sbin/lighttpd'
      self.lighttpd_module_path = lighttpd_module_path or '/usr/lib/lighttpd'
      self.base_config_path = base_config_path
      self.extra_config_contents = extra_config_contents
      self.config_path = config_path or self._Mktmp('config')
      self.error_log = error_log or self._Mktmp('error_log')
      self.access_log = access_log or self._Mktmp('access_log')
      self.pid_file = self._Mktmp('pid_file')
      self.process = None
  
    def _Mktmp(self, name):
      return os.path.join(self.temp_dir, name)
  
    @staticmethod
    def _GetRandomPort():
      # The ports of test server is arranged in constants.py.
      return random.randint(constants.LIGHTTPD_RANDOM_PORT_FIRST,
                            constants.LIGHTTPD_RANDOM_PORT_LAST)
  
    def StartupHttpServer(self):
      """Starts up a http server with specified document root and port."""
      # If we want a specific port, make sure no one else is listening on it.
      if self.fixed_port:
        self._KillProcessListeningOnPort(self.fixed_port)
      while True:
        if self.base_config_path:
          # Read the config
          with codecs.open(self.base_config_path, 'r', 'utf-8') as f:
            config_contents = f.read()
        else:
          config_contents = self._GetDefaultBaseConfig()
        if self.extra_config_contents:
          config_contents += self.extra_config_contents
        # Write out the config, filling in placeholders from the members of |self|
        with codecs.open(self.config_path, 'w', 'utf-8') as f:
          f.write(config_contents % self.__dict__)
        if (not os.path.exists(self.lighttpd_path) or
            not os.access(self.lighttpd_path, os.X_OK)):
          raise EnvironmentError(
              'Could not find lighttpd at %s.\n'
              'It may need to be installed (e.g. sudo apt-get install lighttpd)'
              % self.lighttpd_path)
        # pylint: disable=no-member
        self.process = pexpect.spawn(self.lighttpd_path,
                                     ['-D', '-f', self.config_path,
                                      '-m', self.lighttpd_module_path],
                                     cwd=self.temp_dir)
        client_error, server_error = self._TestServerConnection()
        if not client_error:
          assert int(open(self.pid_file, 'r').read()) == self.process.pid
          break
        self.process.close()
  
        if self.fixed_port or 'in use' not in server_error:
          print('Client error:', client_error)
          print('Server error:', server_error)
          return False
        self.port = self._GetRandomPort()
      return True
  
    def ShutdownHttpServer(self):
      """Shuts down our lighttpd processes."""
      if self.process:
        self.process.terminate()
      shutil.rmtree(self.temp_dir, ignore_errors=True)
  
    def _TestServerConnection(self):
      # Wait for server to start
      server_msg = ''
      for timeout in range(1, 5):
        client_error = None
        try:
          with contextlib.closing(
              http_client.HTTPConnection('127.0.0.1', self.port,
                                         timeout=timeout)) as http:
            http.set_debuglevel(timeout > 3)
            http.request('HEAD', '/')
            r = http.getresponse()
            r.read()
            if (r.status == 200 and r.reason == 'OK' and
                r.getheader('Server') == self.server_tag):
              return (None, server_msg)
            client_error = ('Bad response: %s %s version %s\n  ' %
                            (r.status, r.reason, r.version) +
                            '\n  '.join([': '.join(h) for h in r.getheaders()]))
        except (http_client.HTTPException, socket.error) as client_error:
          pass  # Probably too quick connecting: try again
        # Check for server startup error messages
        # pylint: disable=no-member
        ix = self.process.expect([pexpect.TIMEOUT, pexpect.EOF, '.+'],
                                 timeout=timeout)
        if ix == 2:  # stdout spew from the server
          server_msg += self.process.match.group(0) # pylint: disable=no-member
        elif ix == 1:  # EOF -- server has quit so giveup.
          client_error = client_error or 'Server exited'
          break
      return (client_error or 'Timeout', server_msg)
  
    @staticmethod
    def _KillProcessListeningOnPort(port):
      """Checks if there is a process listening on port number |port| and
      terminates it if found.
  
      Args:
        port: Port number to check.
      """
      if subprocess.call(['fuser', '-kv', '%d/tcp' % port]) == 0:
        # Give the process some time to terminate and check that it is gone.
        time.sleep(2)
        assert subprocess.call(['fuser', '-v', '%d/tcp' % port]) != 0, \
            'Unable to kill process listening on port %d.' % port
  
    @staticmethod
    def _GetDefaultBaseConfig():
      return """server.tag                  = "%(server_tag)s"
  server.modules              = ( "mod_access",
                                  "mod_accesslog",
                                  "mod_alias",
                                  "mod_cgi",
                                  "mod_rewrite" )
  
  # default document root required
  #server.document-root = "."
  
  # files to check for if .../ is requested
  index-file.names            = ( "index.php", "index.pl", "index.cgi",
                                  "index.html", "index.htm", "default.htm" )
  # mimetype mapping
  mimetype.assign             = (
    ".gif"          =>      "image/gif",
    ".jpg"          =>      "image/jpeg",
    ".jpeg"         =>      "image/jpeg",
    ".png"          =>      "image/png",
    ".svg"          =>      "image/svg+xml",
    ".css"          =>      "text/css",
    ".html"         =>      "text/html",
    ".htm"          =>      "text/html",
    ".xhtml"        =>      "application/xhtml+xml",
    ".xhtmlmp"      =>      "application/vnd.wap.xhtml+xml",
    ".js"           =>      "application/x-javascript",
    ".log"          =>      "text/plain",
    ".conf"         =>      "text/plain",
    ".text"         =>      "text/plain",
    ".txt"          =>      "text/plain",
    ".dtd"          =>      "text/xml",
    ".xml"          =>      "text/xml",
    ".manifest"     =>      "text/cache-manifest",
   )
  
  # Use the "Content-Type" extended attribute to obtain mime type if possible
  mimetype.use-xattr          = "enable"
  
  ##
  # which extensions should not be handle via static-file transfer
  #
  # .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi
  static-file.exclude-extensions = ( ".php", ".pl", ".cgi" )
  
  server.bind = "127.0.0.1"
  server.port = %(port)s
  
  ## virtual directory listings
  dir-listing.activate        = "enable"
  #dir-listing.encoding       = "iso-8859-2"
  #dir-listing.external-css   = "style/oldstyle.css"
  
  ## enable debugging
  #debug.log-request-header   = "enable"
  #debug.log-response-header  = "enable"
  #debug.log-request-handling = "enable"
  #debug.log-file-not-found   = "enable"
  
  #### SSL engine
  #ssl.engine                 = "enable"
  #ssl.pemfile                = "server.pem"
  
  # Autogenerated test-specific config follows.
  
  cgi.assign = ( ".cgi"  => "/usr/bin/env",
                 ".pl"   => "/usr/bin/env",
                 ".asis" => "/bin/cat",
                 ".php"  => "/usr/bin/php-cgi" )
  
  server.errorlog = "%(error_log)s"
  accesslog.filename = "%(access_log)s"
  server.upload-dirs = ( "/tmp" )
  server.pid-file = "%(pid_file)s"
  server.document-root = "%(document_root)s"
  
  """
  
  
  def main(argv):
    server = LighttpdServer(*argv[1:])
    try:
      if server.StartupHttpServer():
        input('Server running at http://127.0.0.1:%s -'
              ' press Enter to exit it.' % server.port)
      else:
        print('Server exit code:', server.process.exitstatus)
    finally:
      server.ShutdownHttpServer()
  
  
  if __name__ == '__main__':
    sys.exit(main(sys.argv))
  