| OLD | NEW |
| (Empty) |
| 1 # Copyright (C) 2011 Google Inc. All rights reserved. | |
| 2 # | |
| 3 # Redistribution and use in source and binary forms, with or without | |
| 4 # modification, are permitted provided that the following conditions are | |
| 5 # met: | |
| 6 # | |
| 7 # * Redistributions of source code must retain the above copyright | |
| 8 # notice, this list of conditions and the following disclaimer. | |
| 9 # * Redistributions in binary form must reproduce the above | |
| 10 # copyright notice, this list of conditions and the following disclaimer | |
| 11 # in the documentation and/or other materials provided with the | |
| 12 # distribution. | |
| 13 # * Neither the name of Google Inc. nor the names of its | |
| 14 # contributors may be used to endorse or promote products derived from | |
| 15 # this software without specific prior written permission. | |
| 16 # | |
| 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 28 | |
| 29 """A class to help start/stop the lighttpd server used by layout tests.""" | |
| 30 | |
| 31 import logging | |
| 32 import os | |
| 33 import time | |
| 34 | |
| 35 from webkitpy.layout_tests.servers import http_server_base | |
| 36 | |
| 37 | |
| 38 _log = logging.getLogger(__name__) | |
| 39 | |
| 40 | |
| 41 class Lighttpd(http_server_base.HttpServerBase): | |
| 42 | |
| 43 def __init__(self, port_obj, output_dir, background=False, port=None, | |
| 44 root=None, run_background=None, additional_dirs=None, | |
| 45 layout_tests_dir=None, number_of_servers=None): | |
| 46 """Args: | |
| 47 output_dir: the absolute path to the layout test result directory | |
| 48 """ | |
| 49 # Webkit tests | |
| 50 http_server_base.HttpServerBase.__init__(self, port_obj, number_of_serve
rs) | |
| 51 self._name = 'lighttpd' | |
| 52 self._output_dir = output_dir | |
| 53 self._port = port | |
| 54 self._root = root | |
| 55 self._run_background = run_background | |
| 56 self._additional_dirs = additional_dirs | |
| 57 self._layout_tests_dir = layout_tests_dir | |
| 58 | |
| 59 self._pid_file = self._filesystem.join(self._runtime_path, '%s.pid' % se
lf._name) | |
| 60 | |
| 61 if self._port: | |
| 62 self._port = int(self._port) | |
| 63 | |
| 64 if not self._layout_tests_dir: | |
| 65 self._layout_tests_dir = self._port_obj.layout_tests_dir() | |
| 66 | |
| 67 self._webkit_tests = os.path.join(self._layout_tests_dir, 'http', 'tests
') | |
| 68 self._js_test_resource = os.path.join(self._layout_tests_dir, 'resources
') | |
| 69 self._media_resource = os.path.join(self._layout_tests_dir, 'media') | |
| 70 | |
| 71 # Self generated certificate for SSL server (for client cert get | |
| 72 # <base-path>\chrome\test\data\ssl\certs\root_ca_cert.crt) | |
| 73 self._pem_file = os.path.join( | |
| 74 os.path.dirname(os.path.abspath(__file__)), 'httpd2.pem') | |
| 75 | |
| 76 # One mapping where we can get to everything | |
| 77 self.VIRTUALCONFIG = [] | |
| 78 | |
| 79 if self._webkit_tests: | |
| 80 self.VIRTUALCONFIG.extend( | |
| 81 # Three mappings (one with SSL) for LayoutTests http tests | |
| 82 [{'port': 8000, 'docroot': self._webkit_tests}, | |
| 83 {'port': 8080, 'docroot': self._webkit_tests}, | |
| 84 {'port': 8443, 'docroot': self._webkit_tests, | |
| 85 'sslcert': self._pem_file}]) | |
| 86 | |
| 87 def _prepare_config(self): | |
| 88 base_conf_file = self._port_obj.path_from_webkit_base('Tools', | |
| 89 'Scripts', 'webkitpy', 'layout_tests', 'servers', 'lighttpd.conf') | |
| 90 out_conf_file = os.path.join(self._output_dir, 'lighttpd.conf') | |
| 91 time_str = time.strftime("%d%b%Y-%H%M%S") | |
| 92 access_file_name = "access.log-" + time_str + ".txt" | |
| 93 access_log = os.path.join(self._output_dir, access_file_name) | |
| 94 log_file_name = "error.log-" + time_str + ".txt" | |
| 95 error_log = os.path.join(self._output_dir, log_file_name) | |
| 96 | |
| 97 # Write out the config | |
| 98 base_conf = self._filesystem.read_text_file(base_conf_file) | |
| 99 | |
| 100 # FIXME: This should be re-worked so that this block can | |
| 101 # use with open() instead of a manual file.close() call. | |
| 102 f = self._filesystem.open_text_file_for_writing(out_conf_file) | |
| 103 f.write(base_conf) | |
| 104 | |
| 105 # Write out our cgi handlers. Run perl through env so that it | |
| 106 # processes the #! line and runs perl with the proper command | |
| 107 # line arguments. Emulate apache's mod_asis with a cat cgi handler. | |
| 108 f.write(('cgi.assign = ( ".cgi" => "/usr/bin/env",\n' | |
| 109 ' ".pl" => "/usr/bin/env",\n' | |
| 110 ' ".asis" => "/bin/cat",\n' | |
| 111 ' ".php" => "%s" )\n\n') % | |
| 112 self._port_obj.path_to_lighttpd_php()) | |
| 113 | |
| 114 # Setup log files | |
| 115 f.write(('server.errorlog = "%s"\n' | |
| 116 'accesslog.filename = "%s"\n\n') % (error_log, access_log)) | |
| 117 | |
| 118 # Setup upload folders. Upload folder is to hold temporary upload files | |
| 119 # and also POST data. This is used to support XHR layout tests that | |
| 120 # does POST. | |
| 121 f.write(('server.upload-dirs = ( "%s" )\n\n') % (self._output_dir)) | |
| 122 | |
| 123 # Setup a link to where the js test templates are stored | |
| 124 f.write(('alias.url = ( "/js-test-resources" => "%s" )\n\n') % | |
| 125 (self._js_test_resource)) | |
| 126 | |
| 127 if self._additional_dirs: | |
| 128 for alias, path in self._additional_dirs.iteritems(): | |
| 129 f.write(('alias.url += ( "%s" => "%s" )\n\n') % (alias, path)) | |
| 130 | |
| 131 # Setup a link to where the media resources are stored. | |
| 132 f.write(('alias.url += ( "/media-resources" => "%s" )\n\n') % | |
| 133 (self._media_resource)) | |
| 134 | |
| 135 # dump out of virtual host config at the bottom. | |
| 136 if self._root: | |
| 137 if self._port: | |
| 138 # Have both port and root dir. | |
| 139 mappings = [{'port': self._port, 'docroot': self._root}] | |
| 140 else: | |
| 141 # Have only a root dir - set the ports as for LayoutTests. | |
| 142 # This is used in ui_tests to run http tests against a browser. | |
| 143 | |
| 144 # default set of ports as for LayoutTests but with a | |
| 145 # specified root. | |
| 146 mappings = [{'port': 8000, 'docroot': self._root}, | |
| 147 {'port': 8080, 'docroot': self._root}, | |
| 148 {'port': 8443, 'docroot': self._root, | |
| 149 'sslcert': self._pem_file}] | |
| 150 else: | |
| 151 mappings = self.VIRTUALCONFIG | |
| 152 for mapping in mappings: | |
| 153 ssl_setup = '' | |
| 154 if 'sslcert' in mapping: | |
| 155 ssl_setup = (' ssl.engine = "enable"\n' | |
| 156 ' ssl.pemfile = "%s"\n' % mapping['sslcert']) | |
| 157 | |
| 158 f.write(('$SERVER["socket"] == "127.0.0.1:%d" {\n' | |
| 159 ' server.document-root = "%s"\n' + | |
| 160 ssl_setup + | |
| 161 '}\n\n') % (mapping['port'], mapping['docroot'])) | |
| 162 f.close() | |
| 163 | |
| 164 executable = self._port_obj.path_to_lighttpd() | |
| 165 module_path = self._port_obj.path_to_lighttpd_modules() | |
| 166 start_cmd = [executable, | |
| 167 # Newly written config file | |
| 168 '-f', os.path.join(self._output_dir, 'lighttpd.conf'), | |
| 169 # Where it can find its module dynamic libraries | |
| 170 '-m', module_path] | |
| 171 | |
| 172 if not self._run_background: | |
| 173 start_cmd.append(# Don't background | |
| 174 '-D') | |
| 175 | |
| 176 # Copy liblightcomp.dylib to /tmp/lighttpd/lib to work around the | |
| 177 # bug that mod_alias.so loads it from the hard coded path. | |
| 178 if self._port_obj.host.platform.is_mac(): | |
| 179 tmp_module_path = '/tmp/lighttpd/lib' | |
| 180 if not self._filesystem.exists(tmp_module_path): | |
| 181 self._filesystem.maybe_make_directory(tmp_module_path) | |
| 182 lib_file = 'liblightcomp.dylib' | |
| 183 self._filesystem.copyfile(self._filesystem.join(module_path, lib_fil
e), | |
| 184 self._filesystem.join(tmp_module_path, lib
_file)) | |
| 185 | |
| 186 self._start_cmd = start_cmd | |
| 187 self._env = self._port_obj.setup_environ_for_server('lighttpd') | |
| 188 self._mappings = mappings | |
| 189 | |
| 190 def _remove_stale_logs(self): | |
| 191 # Sometimes logs are open in other processes but they should clear event
ually. | |
| 192 for log_prefix in ('access.log-', 'error.log-'): | |
| 193 try: | |
| 194 self._remove_log_files(self._output_dir, log_prefix) | |
| 195 except OSError, e: | |
| 196 _log.warning('Failed to remove old %s %s files' % (self._name, l
og_prefix)) | |
| 197 | |
| 198 def _spawn_process(self): | |
| 199 _log.debug('Starting %s server, cmd="%s"' % (self._name, self._start_cmd
)) | |
| 200 process = self._executive.popen(self._start_cmd, env=self._env, shell=Fa
lse, stderr=self._executive.PIPE) | |
| 201 pid = process.pid | |
| 202 self._filesystem.write_text_file(self._pid_file, str(pid)) | |
| 203 return pid | |
| 204 | |
| 205 def _stop_running_server(self): | |
| 206 # FIXME: It would be nice if we had a cleaner way of killing this proces
s. | |
| 207 # Currently we throw away the process object created in _spawn_process, | |
| 208 # since there doesn't appear to be any way to kill the server any more | |
| 209 # cleanly using it than just killing the pid, and we need to support | |
| 210 # killing a pid directly anyway for run-webkit-httpd and run-webkit-webs
ocketserver. | |
| 211 self._wait_for_action(self._check_and_kill) | |
| 212 if self._filesystem.exists(self._pid_file): | |
| 213 self._filesystem.remove(self._pid_file) | |
| 214 | |
| 215 def _check_and_kill(self): | |
| 216 if self._executive.check_running_pid(self._pid): | |
| 217 host = self._port_obj.host | |
| 218 if host.platform.is_win() and not host.platform.is_cygwin(): | |
| 219 # FIXME: https://bugs.webkit.org/show_bug.cgi?id=106838 | |
| 220 # We need to kill all of the child processes as well as the | |
| 221 # parent, so we can't use executive.kill_process(). | |
| 222 # | |
| 223 # If this is actually working, we should figure out a clean API. | |
| 224 self._executive.run_command(["taskkill.exe", "/f", "/t", "/pid",
self._pid], error_handler=self._executive.ignore_error) | |
| 225 else: | |
| 226 self._executive.kill_process(self._pid) | |
| 227 return False | |
| 228 return True | |
| OLD | NEW |