Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | #!/usr/bin/env python |
michael@0 | 2 | # |
michael@0 | 3 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
michael@0 | 4 | # Use of this source code is governed by a BSD-style license that can be |
michael@0 | 5 | # found in the LICENSE file. |
michael@0 | 6 | |
michael@0 | 7 | """Provides a convenient wrapper for spawning a test lighttpd instance. |
michael@0 | 8 | |
michael@0 | 9 | Usage: |
michael@0 | 10 | lighttpd_server PATH_TO_DOC_ROOT |
michael@0 | 11 | """ |
michael@0 | 12 | |
michael@0 | 13 | import codecs |
michael@0 | 14 | import contextlib |
michael@0 | 15 | import httplib |
michael@0 | 16 | import os |
michael@0 | 17 | import random |
michael@0 | 18 | import shutil |
michael@0 | 19 | import socket |
michael@0 | 20 | import subprocess |
michael@0 | 21 | import sys |
michael@0 | 22 | import tempfile |
michael@0 | 23 | import time |
michael@0 | 24 | |
michael@0 | 25 | from pylib import constants |
michael@0 | 26 | from pylib import pexpect |
michael@0 | 27 | |
michael@0 | 28 | class LighttpdServer(object): |
michael@0 | 29 | """Wraps lighttpd server, providing robust startup. |
michael@0 | 30 | |
michael@0 | 31 | Args: |
michael@0 | 32 | document_root: Path to root of this server's hosted files. |
michael@0 | 33 | port: TCP port on the _host_ machine that the server will listen on. If |
michael@0 | 34 | ommitted it will attempt to use 9000, or if unavailable it will find |
michael@0 | 35 | a free port from 8001 - 8999. |
michael@0 | 36 | lighttpd_path, lighttpd_module_path: Optional paths to lighttpd binaries. |
michael@0 | 37 | base_config_path: If supplied this file will replace the built-in default |
michael@0 | 38 | lighttpd config file. |
michael@0 | 39 | extra_config_contents: If specified, this string will be appended to the |
michael@0 | 40 | base config (default built-in, or from base_config_path). |
michael@0 | 41 | config_path, error_log, access_log: Optional paths where the class should |
michael@0 | 42 | place temprary files for this session. |
michael@0 | 43 | """ |
michael@0 | 44 | |
michael@0 | 45 | def __init__(self, document_root, port=None, |
michael@0 | 46 | lighttpd_path=None, lighttpd_module_path=None, |
michael@0 | 47 | base_config_path=None, extra_config_contents=None, |
michael@0 | 48 | config_path=None, error_log=None, access_log=None): |
michael@0 | 49 | self.temp_dir = tempfile.mkdtemp(prefix='lighttpd_for_chrome_android') |
michael@0 | 50 | self.document_root = os.path.abspath(document_root) |
michael@0 | 51 | self.fixed_port = port |
michael@0 | 52 | self.port = port or constants.LIGHTTPD_DEFAULT_PORT |
michael@0 | 53 | self.server_tag = 'LightTPD ' + str(random.randint(111111, 999999)) |
michael@0 | 54 | self.lighttpd_path = lighttpd_path or '/usr/sbin/lighttpd' |
michael@0 | 55 | self.lighttpd_module_path = lighttpd_module_path or '/usr/lib/lighttpd' |
michael@0 | 56 | self.base_config_path = base_config_path |
michael@0 | 57 | self.extra_config_contents = extra_config_contents |
michael@0 | 58 | self.config_path = config_path or self._Mktmp('config') |
michael@0 | 59 | self.error_log = error_log or self._Mktmp('error_log') |
michael@0 | 60 | self.access_log = access_log or self._Mktmp('access_log') |
michael@0 | 61 | self.pid_file = self._Mktmp('pid_file') |
michael@0 | 62 | self.process = None |
michael@0 | 63 | |
michael@0 | 64 | def _Mktmp(self, name): |
michael@0 | 65 | return os.path.join(self.temp_dir, name) |
michael@0 | 66 | |
michael@0 | 67 | def _GetRandomPort(self): |
michael@0 | 68 | # The ports of test server is arranged in constants.py. |
michael@0 | 69 | return random.randint(constants.LIGHTTPD_RANDOM_PORT_FIRST, |
michael@0 | 70 | constants.LIGHTTPD_RANDOM_PORT_LAST) |
michael@0 | 71 | |
michael@0 | 72 | def StartupHttpServer(self): |
michael@0 | 73 | """Starts up a http server with specified document root and port.""" |
michael@0 | 74 | # If we want a specific port, make sure no one else is listening on it. |
michael@0 | 75 | if self.fixed_port: |
michael@0 | 76 | self._KillProcessListeningOnPort(self.fixed_port) |
michael@0 | 77 | while True: |
michael@0 | 78 | if self.base_config_path: |
michael@0 | 79 | # Read the config |
michael@0 | 80 | with codecs.open(self.base_config_path, 'r', 'utf-8') as f: |
michael@0 | 81 | config_contents = f.read() |
michael@0 | 82 | else: |
michael@0 | 83 | config_contents = self._GetDefaultBaseConfig() |
michael@0 | 84 | if self.extra_config_contents: |
michael@0 | 85 | config_contents += self.extra_config_contents |
michael@0 | 86 | # Write out the config, filling in placeholders from the members of |self| |
michael@0 | 87 | with codecs.open(self.config_path, 'w', 'utf-8') as f: |
michael@0 | 88 | f.write(config_contents % self.__dict__) |
michael@0 | 89 | if (not os.path.exists(self.lighttpd_path) or |
michael@0 | 90 | not os.access(self.lighttpd_path, os.X_OK)): |
michael@0 | 91 | raise EnvironmentError( |
michael@0 | 92 | 'Could not find lighttpd at %s.\n' |
michael@0 | 93 | 'It may need to be installed (e.g. sudo apt-get install lighttpd)' |
michael@0 | 94 | % self.lighttpd_path) |
michael@0 | 95 | self.process = pexpect.spawn(self.lighttpd_path, |
michael@0 | 96 | ['-D', '-f', self.config_path, |
michael@0 | 97 | '-m', self.lighttpd_module_path], |
michael@0 | 98 | cwd=self.temp_dir) |
michael@0 | 99 | client_error, server_error = self._TestServerConnection() |
michael@0 | 100 | if not client_error: |
michael@0 | 101 | assert int(open(self.pid_file, 'r').read()) == self.process.pid |
michael@0 | 102 | break |
michael@0 | 103 | self.process.close() |
michael@0 | 104 | |
michael@0 | 105 | if self.fixed_port or not 'in use' in server_error: |
michael@0 | 106 | print 'Client error:', client_error |
michael@0 | 107 | print 'Server error:', server_error |
michael@0 | 108 | return False |
michael@0 | 109 | self.port = self._GetRandomPort() |
michael@0 | 110 | return True |
michael@0 | 111 | |
michael@0 | 112 | def ShutdownHttpServer(self): |
michael@0 | 113 | """Shuts down our lighttpd processes.""" |
michael@0 | 114 | if self.process: |
michael@0 | 115 | self.process.terminate() |
michael@0 | 116 | shutil.rmtree(self.temp_dir, ignore_errors=True) |
michael@0 | 117 | |
michael@0 | 118 | def _TestServerConnection(self): |
michael@0 | 119 | # Wait for server to start |
michael@0 | 120 | server_msg = '' |
michael@0 | 121 | for timeout in xrange(1, 5): |
michael@0 | 122 | client_error = None |
michael@0 | 123 | try: |
michael@0 | 124 | with contextlib.closing(httplib.HTTPConnection( |
michael@0 | 125 | '127.0.0.1', self.port, timeout=timeout)) as http: |
michael@0 | 126 | http.set_debuglevel(timeout > 3) |
michael@0 | 127 | http.request('HEAD', '/') |
michael@0 | 128 | r = http.getresponse() |
michael@0 | 129 | r.read() |
michael@0 | 130 | if (r.status == 200 and r.reason == 'OK' and |
michael@0 | 131 | r.getheader('Server') == self.server_tag): |
michael@0 | 132 | return (None, server_msg) |
michael@0 | 133 | client_error = ('Bad response: %s %s version %s\n ' % |
michael@0 | 134 | (r.status, r.reason, r.version) + |
michael@0 | 135 | '\n '.join([': '.join(h) for h in r.getheaders()])) |
michael@0 | 136 | except (httplib.HTTPException, socket.error) as client_error: |
michael@0 | 137 | pass # Probably too quick connecting: try again |
michael@0 | 138 | # Check for server startup error messages |
michael@0 | 139 | ix = self.process.expect([pexpect.TIMEOUT, pexpect.EOF, '.+'], |
michael@0 | 140 | timeout=timeout) |
michael@0 | 141 | if ix == 2: # stdout spew from the server |
michael@0 | 142 | server_msg += self.process.match.group(0) |
michael@0 | 143 | elif ix == 1: # EOF -- server has quit so giveup. |
michael@0 | 144 | client_error = client_error or 'Server exited' |
michael@0 | 145 | break |
michael@0 | 146 | return (client_error or 'Timeout', server_msg) |
michael@0 | 147 | |
michael@0 | 148 | def _KillProcessListeningOnPort(self, port): |
michael@0 | 149 | """Checks if there is a process listening on port number |port| and |
michael@0 | 150 | terminates it if found. |
michael@0 | 151 | |
michael@0 | 152 | Args: |
michael@0 | 153 | port: Port number to check. |
michael@0 | 154 | """ |
michael@0 | 155 | if subprocess.call(['fuser', '-kv', '%d/tcp' % port]) == 0: |
michael@0 | 156 | # Give the process some time to terminate and check that it is gone. |
michael@0 | 157 | time.sleep(2) |
michael@0 | 158 | assert subprocess.call(['fuser', '-v', '%d/tcp' % port]) != 0, \ |
michael@0 | 159 | 'Unable to kill process listening on port %d.' % port |
michael@0 | 160 | |
michael@0 | 161 | def _GetDefaultBaseConfig(self): |
michael@0 | 162 | return """server.tag = "%(server_tag)s" |
michael@0 | 163 | server.modules = ( "mod_access", |
michael@0 | 164 | "mod_accesslog", |
michael@0 | 165 | "mod_alias", |
michael@0 | 166 | "mod_cgi", |
michael@0 | 167 | "mod_rewrite" ) |
michael@0 | 168 | |
michael@0 | 169 | # default document root required |
michael@0 | 170 | #server.document-root = "." |
michael@0 | 171 | |
michael@0 | 172 | # files to check for if .../ is requested |
michael@0 | 173 | index-file.names = ( "index.php", "index.pl", "index.cgi", |
michael@0 | 174 | "index.html", "index.htm", "default.htm" ) |
michael@0 | 175 | # mimetype mapping |
michael@0 | 176 | mimetype.assign = ( |
michael@0 | 177 | ".gif" => "image/gif", |
michael@0 | 178 | ".jpg" => "image/jpeg", |
michael@0 | 179 | ".jpeg" => "image/jpeg", |
michael@0 | 180 | ".png" => "image/png", |
michael@0 | 181 | ".svg" => "image/svg+xml", |
michael@0 | 182 | ".css" => "text/css", |
michael@0 | 183 | ".html" => "text/html", |
michael@0 | 184 | ".htm" => "text/html", |
michael@0 | 185 | ".xhtml" => "application/xhtml+xml", |
michael@0 | 186 | ".xhtmlmp" => "application/vnd.wap.xhtml+xml", |
michael@0 | 187 | ".js" => "application/x-javascript", |
michael@0 | 188 | ".log" => "text/plain", |
michael@0 | 189 | ".conf" => "text/plain", |
michael@0 | 190 | ".text" => "text/plain", |
michael@0 | 191 | ".txt" => "text/plain", |
michael@0 | 192 | ".dtd" => "text/xml", |
michael@0 | 193 | ".xml" => "text/xml", |
michael@0 | 194 | ".manifest" => "text/cache-manifest", |
michael@0 | 195 | ) |
michael@0 | 196 | |
michael@0 | 197 | # Use the "Content-Type" extended attribute to obtain mime type if possible |
michael@0 | 198 | mimetype.use-xattr = "enable" |
michael@0 | 199 | |
michael@0 | 200 | ## |
michael@0 | 201 | # which extensions should not be handle via static-file transfer |
michael@0 | 202 | # |
michael@0 | 203 | # .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi |
michael@0 | 204 | static-file.exclude-extensions = ( ".php", ".pl", ".cgi" ) |
michael@0 | 205 | |
michael@0 | 206 | server.bind = "127.0.0.1" |
michael@0 | 207 | server.port = %(port)s |
michael@0 | 208 | |
michael@0 | 209 | ## virtual directory listings |
michael@0 | 210 | dir-listing.activate = "enable" |
michael@0 | 211 | #dir-listing.encoding = "iso-8859-2" |
michael@0 | 212 | #dir-listing.external-css = "style/oldstyle.css" |
michael@0 | 213 | |
michael@0 | 214 | ## enable debugging |
michael@0 | 215 | #debug.log-request-header = "enable" |
michael@0 | 216 | #debug.log-response-header = "enable" |
michael@0 | 217 | #debug.log-request-handling = "enable" |
michael@0 | 218 | #debug.log-file-not-found = "enable" |
michael@0 | 219 | |
michael@0 | 220 | #### SSL engine |
michael@0 | 221 | #ssl.engine = "enable" |
michael@0 | 222 | #ssl.pemfile = "server.pem" |
michael@0 | 223 | |
michael@0 | 224 | # Autogenerated test-specific config follows. |
michael@0 | 225 | |
michael@0 | 226 | cgi.assign = ( ".cgi" => "/usr/bin/env", |
michael@0 | 227 | ".pl" => "/usr/bin/env", |
michael@0 | 228 | ".asis" => "/bin/cat", |
michael@0 | 229 | ".php" => "/usr/bin/php-cgi" ) |
michael@0 | 230 | |
michael@0 | 231 | server.errorlog = "%(error_log)s" |
michael@0 | 232 | accesslog.filename = "%(access_log)s" |
michael@0 | 233 | server.upload-dirs = ( "/tmp" ) |
michael@0 | 234 | server.pid-file = "%(pid_file)s" |
michael@0 | 235 | server.document-root = "%(document_root)s" |
michael@0 | 236 | |
michael@0 | 237 | """ |
michael@0 | 238 | |
michael@0 | 239 | |
michael@0 | 240 | def main(argv): |
michael@0 | 241 | server = LighttpdServer(*argv[1:]) |
michael@0 | 242 | try: |
michael@0 | 243 | if server.StartupHttpServer(): |
michael@0 | 244 | raw_input('Server running at http://127.0.0.1:%s -' |
michael@0 | 245 | ' press Enter to exit it.' % server.port) |
michael@0 | 246 | else: |
michael@0 | 247 | print 'Server exit code:', server.process.exitstatus |
michael@0 | 248 | finally: |
michael@0 | 249 | server.ShutdownHttpServer() |
michael@0 | 250 | |
michael@0 | 251 | |
michael@0 | 252 | if __name__ == '__main__': |
michael@0 | 253 | sys.exit(main(sys.argv)) |