media/webrtc/trunk/build/android/lighttpd_server.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/media/webrtc/trunk/build/android/lighttpd_server.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,253 @@
     1.4 +#!/usr/bin/env python
     1.5 +#
     1.6 +# Copyright (c) 2012 The Chromium Authors. All rights reserved.
     1.7 +# Use of this source code is governed by a BSD-style license that can be
     1.8 +# found in the LICENSE file.
     1.9 +
    1.10 +"""Provides a convenient wrapper for spawning a test lighttpd instance.
    1.11 +
    1.12 +Usage:
    1.13 +  lighttpd_server PATH_TO_DOC_ROOT
    1.14 +"""
    1.15 +
    1.16 +import codecs
    1.17 +import contextlib
    1.18 +import httplib
    1.19 +import os
    1.20 +import random
    1.21 +import shutil
    1.22 +import socket
    1.23 +import subprocess
    1.24 +import sys
    1.25 +import tempfile
    1.26 +import time
    1.27 +
    1.28 +from pylib import constants
    1.29 +from pylib import pexpect
    1.30 +
    1.31 +class LighttpdServer(object):
    1.32 +  """Wraps lighttpd server, providing robust startup.
    1.33 +
    1.34 +  Args:
    1.35 +    document_root: Path to root of this server's hosted files.
    1.36 +    port: TCP port on the _host_ machine that the server will listen on. If
    1.37 +        ommitted it will attempt to use 9000, or if unavailable it will find
    1.38 +        a free port from 8001 - 8999.
    1.39 +    lighttpd_path, lighttpd_module_path: Optional paths to lighttpd binaries.
    1.40 +    base_config_path: If supplied this file will replace the built-in default
    1.41 +        lighttpd config file.
    1.42 +    extra_config_contents: If specified, this string will be appended to the
    1.43 +        base config (default built-in, or from base_config_path).
    1.44 +    config_path, error_log, access_log: Optional paths where the class should
    1.45 +        place temprary files for this session.
    1.46 +  """
    1.47 +
    1.48 +  def __init__(self, document_root, port=None,
    1.49 +               lighttpd_path=None, lighttpd_module_path=None,
    1.50 +               base_config_path=None, extra_config_contents=None,
    1.51 +               config_path=None, error_log=None, access_log=None):
    1.52 +    self.temp_dir = tempfile.mkdtemp(prefix='lighttpd_for_chrome_android')
    1.53 +    self.document_root = os.path.abspath(document_root)
    1.54 +    self.fixed_port = port
    1.55 +    self.port = port or constants.LIGHTTPD_DEFAULT_PORT
    1.56 +    self.server_tag = 'LightTPD ' + str(random.randint(111111, 999999))
    1.57 +    self.lighttpd_path = lighttpd_path or '/usr/sbin/lighttpd'
    1.58 +    self.lighttpd_module_path = lighttpd_module_path or '/usr/lib/lighttpd'
    1.59 +    self.base_config_path = base_config_path
    1.60 +    self.extra_config_contents = extra_config_contents
    1.61 +    self.config_path = config_path or self._Mktmp('config')
    1.62 +    self.error_log = error_log or self._Mktmp('error_log')
    1.63 +    self.access_log = access_log or self._Mktmp('access_log')
    1.64 +    self.pid_file = self._Mktmp('pid_file')
    1.65 +    self.process = None
    1.66 +
    1.67 +  def _Mktmp(self, name):
    1.68 +    return os.path.join(self.temp_dir, name)
    1.69 +
    1.70 +  def _GetRandomPort(self):
    1.71 +    # The ports of test server is arranged in constants.py.
    1.72 +    return random.randint(constants.LIGHTTPD_RANDOM_PORT_FIRST,
    1.73 +                          constants.LIGHTTPD_RANDOM_PORT_LAST)
    1.74 +
    1.75 +  def StartupHttpServer(self):
    1.76 +    """Starts up a http server with specified document root and port."""
    1.77 +    # If we want a specific port, make sure no one else is listening on it.
    1.78 +    if self.fixed_port:
    1.79 +      self._KillProcessListeningOnPort(self.fixed_port)
    1.80 +    while True:
    1.81 +      if self.base_config_path:
    1.82 +        # Read the config
    1.83 +        with codecs.open(self.base_config_path, 'r', 'utf-8') as f:
    1.84 +          config_contents = f.read()
    1.85 +      else:
    1.86 +        config_contents = self._GetDefaultBaseConfig()
    1.87 +      if self.extra_config_contents:
    1.88 +        config_contents += self.extra_config_contents
    1.89 +      # Write out the config, filling in placeholders from the members of |self|
    1.90 +      with codecs.open(self.config_path, 'w', 'utf-8') as f:
    1.91 +        f.write(config_contents % self.__dict__)
    1.92 +      if (not os.path.exists(self.lighttpd_path) or
    1.93 +          not os.access(self.lighttpd_path, os.X_OK)):
    1.94 +        raise EnvironmentError(
    1.95 +            'Could not find lighttpd at %s.\n'
    1.96 +            'It may need to be installed (e.g. sudo apt-get install lighttpd)'
    1.97 +            % self.lighttpd_path)
    1.98 +      self.process = pexpect.spawn(self.lighttpd_path,
    1.99 +                                   ['-D', '-f', self.config_path,
   1.100 +                                    '-m', self.lighttpd_module_path],
   1.101 +                                   cwd=self.temp_dir)
   1.102 +      client_error, server_error = self._TestServerConnection()
   1.103 +      if not client_error:
   1.104 +        assert int(open(self.pid_file, 'r').read()) == self.process.pid
   1.105 +        break
   1.106 +      self.process.close()
   1.107 +
   1.108 +      if self.fixed_port or not 'in use' in server_error:
   1.109 +        print 'Client error:', client_error
   1.110 +        print 'Server error:', server_error
   1.111 +        return False
   1.112 +      self.port = self._GetRandomPort()
   1.113 +    return True
   1.114 +
   1.115 +  def ShutdownHttpServer(self):
   1.116 +    """Shuts down our lighttpd processes."""
   1.117 +    if self.process:
   1.118 +      self.process.terminate()
   1.119 +    shutil.rmtree(self.temp_dir, ignore_errors=True)
   1.120 +
   1.121 +  def _TestServerConnection(self):
   1.122 +    # Wait for server to start
   1.123 +    server_msg = ''
   1.124 +    for timeout in xrange(1, 5):
   1.125 +      client_error = None
   1.126 +      try:
   1.127 +        with contextlib.closing(httplib.HTTPConnection(
   1.128 +            '127.0.0.1', self.port, timeout=timeout)) as http:
   1.129 +          http.set_debuglevel(timeout > 3)
   1.130 +          http.request('HEAD', '/')
   1.131 +          r = http.getresponse()
   1.132 +          r.read()
   1.133 +          if (r.status == 200 and r.reason == 'OK' and
   1.134 +              r.getheader('Server') == self.server_tag):
   1.135 +            return (None, server_msg)
   1.136 +          client_error = ('Bad response: %s %s version %s\n  ' %
   1.137 +                          (r.status, r.reason, r.version) +
   1.138 +                          '\n  '.join([': '.join(h) for h in r.getheaders()]))
   1.139 +      except (httplib.HTTPException, socket.error) as client_error:
   1.140 +        pass  # Probably too quick connecting: try again
   1.141 +      # Check for server startup error messages
   1.142 +      ix = self.process.expect([pexpect.TIMEOUT, pexpect.EOF, '.+'],
   1.143 +                               timeout=timeout)
   1.144 +      if ix == 2:  # stdout spew from the server
   1.145 +        server_msg += self.process.match.group(0)
   1.146 +      elif ix == 1:  # EOF -- server has quit so giveup.
   1.147 +        client_error = client_error or 'Server exited'
   1.148 +        break
   1.149 +    return (client_error or 'Timeout', server_msg)
   1.150 +
   1.151 +  def _KillProcessListeningOnPort(self, port):
   1.152 +    """Checks if there is a process listening on port number |port| and
   1.153 +    terminates it if found.
   1.154 +
   1.155 +    Args:
   1.156 +      port: Port number to check.
   1.157 +    """
   1.158 +    if subprocess.call(['fuser', '-kv', '%d/tcp' % port]) == 0:
   1.159 +      # Give the process some time to terminate and check that it is gone.
   1.160 +      time.sleep(2)
   1.161 +      assert subprocess.call(['fuser', '-v', '%d/tcp' % port]) != 0, \
   1.162 +          'Unable to kill process listening on port %d.' % port
   1.163 +
   1.164 +  def _GetDefaultBaseConfig(self):
   1.165 +    return """server.tag                  = "%(server_tag)s"
   1.166 +server.modules              = ( "mod_access",
   1.167 +                                "mod_accesslog",
   1.168 +                                "mod_alias",
   1.169 +                                "mod_cgi",
   1.170 +                                "mod_rewrite" )
   1.171 +
   1.172 +# default document root required
   1.173 +#server.document-root = "."
   1.174 +
   1.175 +# files to check for if .../ is requested
   1.176 +index-file.names            = ( "index.php", "index.pl", "index.cgi",
   1.177 +                                "index.html", "index.htm", "default.htm" )
   1.178 +# mimetype mapping
   1.179 +mimetype.assign             = (
   1.180 +  ".gif"          =>      "image/gif",
   1.181 +  ".jpg"          =>      "image/jpeg",
   1.182 +  ".jpeg"         =>      "image/jpeg",
   1.183 +  ".png"          =>      "image/png",
   1.184 +  ".svg"          =>      "image/svg+xml",
   1.185 +  ".css"          =>      "text/css",
   1.186 +  ".html"         =>      "text/html",
   1.187 +  ".htm"          =>      "text/html",
   1.188 +  ".xhtml"        =>      "application/xhtml+xml",
   1.189 +  ".xhtmlmp"      =>      "application/vnd.wap.xhtml+xml",
   1.190 +  ".js"           =>      "application/x-javascript",
   1.191 +  ".log"          =>      "text/plain",
   1.192 +  ".conf"         =>      "text/plain",
   1.193 +  ".text"         =>      "text/plain",
   1.194 +  ".txt"          =>      "text/plain",
   1.195 +  ".dtd"          =>      "text/xml",
   1.196 +  ".xml"          =>      "text/xml",
   1.197 +  ".manifest"     =>      "text/cache-manifest",
   1.198 + )
   1.199 +
   1.200 +# Use the "Content-Type" extended attribute to obtain mime type if possible
   1.201 +mimetype.use-xattr          = "enable"
   1.202 +
   1.203 +##
   1.204 +# which extensions should not be handle via static-file transfer
   1.205 +#
   1.206 +# .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi
   1.207 +static-file.exclude-extensions = ( ".php", ".pl", ".cgi" )
   1.208 +
   1.209 +server.bind = "127.0.0.1"
   1.210 +server.port = %(port)s
   1.211 +
   1.212 +## virtual directory listings
   1.213 +dir-listing.activate        = "enable"
   1.214 +#dir-listing.encoding       = "iso-8859-2"
   1.215 +#dir-listing.external-css   = "style/oldstyle.css"
   1.216 +
   1.217 +## enable debugging
   1.218 +#debug.log-request-header   = "enable"
   1.219 +#debug.log-response-header  = "enable"
   1.220 +#debug.log-request-handling = "enable"
   1.221 +#debug.log-file-not-found   = "enable"
   1.222 +
   1.223 +#### SSL engine
   1.224 +#ssl.engine                 = "enable"
   1.225 +#ssl.pemfile                = "server.pem"
   1.226 +
   1.227 +# Autogenerated test-specific config follows.
   1.228 +
   1.229 +cgi.assign = ( ".cgi"  => "/usr/bin/env",
   1.230 +               ".pl"   => "/usr/bin/env",
   1.231 +               ".asis" => "/bin/cat",
   1.232 +               ".php"  => "/usr/bin/php-cgi" )
   1.233 +
   1.234 +server.errorlog = "%(error_log)s"
   1.235 +accesslog.filename = "%(access_log)s"
   1.236 +server.upload-dirs = ( "/tmp" )
   1.237 +server.pid-file = "%(pid_file)s"
   1.238 +server.document-root = "%(document_root)s"
   1.239 +
   1.240 +"""
   1.241 +
   1.242 +
   1.243 +def main(argv):
   1.244 +  server = LighttpdServer(*argv[1:])
   1.245 +  try:
   1.246 +    if server.StartupHttpServer():
   1.247 +      raw_input('Server running at http://127.0.0.1:%s -'
   1.248 +                ' press Enter to exit it.' % server.port)
   1.249 +    else:
   1.250 +      print 'Server exit code:', server.process.exitstatus
   1.251 +  finally:
   1.252 +    server.ShutdownHttpServer()
   1.253 +
   1.254 +
   1.255 +if __name__ == '__main__':
   1.256 +  sys.exit(main(sys.argv))

mercurial