| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """A class to start/stop the apache http server used by layout tests.""" | |
| 6 | |
| 7 import logging | |
| 8 import optparse | |
| 9 import os | |
| 10 import re | |
| 11 import subprocess | |
| 12 import sys | |
| 13 | |
| 14 import http_server_base | |
| 15 import path_utils | |
| 16 import platform_utils | |
| 17 | |
| 18 | |
| 19 class LayoutTestApacheHttpd(http_server_base.HttpServerBase): | |
| 20 | |
| 21 def __init__(self, output_dir): | |
| 22 """Args: | |
| 23 output_dir: the absolute path to the layout test result directory | |
| 24 """ | |
| 25 self._output_dir = output_dir | |
| 26 self._httpd_proc = None | |
| 27 path_utils.MaybeMakeDirectory(output_dir) | |
| 28 | |
| 29 self.mappings = [{'port': 8000}, | |
| 30 {'port': 8080}, | |
| 31 {'port': 8081}, | |
| 32 {'port': 8443, 'sslcert': True}] | |
| 33 | |
| 34 # The upstream .conf file assumed the existence of /tmp/WebKit for | |
| 35 # placing apache files like the lock file there. | |
| 36 self._runtime_path = os.path.join("/tmp", "WebKit") | |
| 37 path_utils.MaybeMakeDirectory(self._runtime_path) | |
| 38 | |
| 39 # The PID returned when Apache is started goes away (due to dropping | |
| 40 # privileges?). The proper controlling PID is written to a file in the | |
| 41 # apache runtime directory. | |
| 42 self._pid_file = os.path.join(self._runtime_path, 'httpd.pid') | |
| 43 | |
| 44 test_dir = path_utils.PathFromBase('third_party', 'WebKit', | |
| 45 'LayoutTests') | |
| 46 js_test_resources_dir = self._CygwinSafeJoin(test_dir, "fast", "js", | |
| 47 "resources") | |
| 48 mime_types_path = self._CygwinSafeJoin(test_dir, "http", "conf", | |
| 49 "mime.types") | |
| 50 cert_file = self._CygwinSafeJoin(test_dir, "http", "conf", | |
| 51 "webkit-httpd.pem") | |
| 52 access_log = self._CygwinSafeJoin(output_dir, "access_log.txt") | |
| 53 error_log = self._CygwinSafeJoin(output_dir, "error_log.txt") | |
| 54 document_root = self._CygwinSafeJoin(test_dir, "http", "tests") | |
| 55 | |
| 56 executable = platform_utils.ApacheExecutablePath() | |
| 57 if self._IsCygwin(): | |
| 58 executable = self._GetCygwinPath(executable) | |
| 59 | |
| 60 cmd = [executable, | |
| 61 '-f', self._GetApacheConfigFilePath(test_dir, output_dir), | |
| 62 '-C', "\'DocumentRoot %s\'" % document_root, | |
| 63 '-c', "\'Alias /js-test-resources %s\'" % js_test_resources_dir, | |
| 64 '-C', "\'Listen %s\'" % "127.0.0.1:8000", | |
| 65 '-C', "\'Listen %s\'" % "127.0.0.1:8081", | |
| 66 '-c', "\'TypesConfig \"%s\"\'" % mime_types_path, | |
| 67 '-c', "\'CustomLog \"%s\" common\'" % access_log, | |
| 68 '-c', "\'ErrorLog \"%s\"\'" % error_log, | |
| 69 '-C', "\'User \"%s\"\'" % os.environ.get("USERNAME", | |
| 70 os.environ.get("USER", ""))] | |
| 71 | |
| 72 if self._IsCygwin(): | |
| 73 cygbin = path_utils.PathFromBase('third_party', 'cygwin', 'bin') | |
| 74 # Not entirely sure why, but from cygwin we need to run the | |
| 75 # httpd command through bash. | |
| 76 self._start_cmd = [ | |
| 77 os.path.join(cygbin, 'bash.exe'), | |
| 78 '-c', | |
| 79 'PATH=%s %s' % (self._GetCygwinPath(cygbin), " ".join(cmd)), | |
| 80 ] | |
| 81 else: | |
| 82 # TODO(ojan): When we get cygwin using Apache 2, use set the | |
| 83 # cert file for cygwin as well. | |
| 84 cmd.extend(['-c', "\'SSLCertificateFile %s\'" % cert_file]) | |
| 85 # Join the string here so that Cygwin/Windows and Mac/Linux | |
| 86 # can use the same code. Otherwise, we could remove the single | |
| 87 # quotes above and keep cmd as a sequence. | |
| 88 self._start_cmd = " ".join(cmd) | |
| 89 | |
| 90 def _IsCygwin(self): | |
| 91 return sys.platform in ("win32", "cygwin") | |
| 92 | |
| 93 def _CygwinSafeJoin(self, *parts): | |
| 94 """Returns a platform appropriate path.""" | |
| 95 path = os.path.join(*parts) | |
| 96 if self._IsCygwin(): | |
| 97 return self._GetCygwinPath(path) | |
| 98 return path | |
| 99 | |
| 100 def _GetCygwinPath(self, path): | |
| 101 """Convert a Windows path to a cygwin path. | |
| 102 | |
| 103 The cygpath utility insists on converting paths that it thinks are | |
| 104 Cygwin root paths to what it thinks the correct roots are. So paths | |
| 105 such as "C:\b\slave\webkit-release\build\third_party\cygwin\bin" | |
| 106 are converted to plain "/usr/bin". To avoid this, we | |
| 107 do the conversion manually. | |
| 108 | |
| 109 The path is expected to be an absolute path, on any drive. | |
| 110 """ | |
| 111 drive_regexp = re.compile(r'([a-z]):[/\\]', re.IGNORECASE) | |
| 112 | |
| 113 def LowerDrive(matchobj): | |
| 114 return '/cygdrive/%s/' % matchobj.group(1).lower() | |
| 115 path = drive_regexp.sub(LowerDrive, path) | |
| 116 return path.replace('\\', '/') | |
| 117 | |
| 118 def _GetApacheConfigFilePath(self, test_dir, output_dir): | |
| 119 """Returns the path to the apache config file to use. | |
| 120 Args: | |
| 121 test_dir: absolute path to the LayoutTests directory. | |
| 122 output_dir: absolute path to the layout test results directory. | |
| 123 """ | |
| 124 httpd_config = platform_utils.ApacheConfigFilePath() | |
| 125 httpd_config_copy = os.path.join(output_dir, "httpd.conf") | |
| 126 httpd_conf = open(httpd_config).read() | |
| 127 if self._IsCygwin(): | |
| 128 # This is a gross hack, but it lets us use the upstream .conf file | |
| 129 # and our checked in cygwin. This tells the server the root | |
| 130 # directory to look in for .so modules. It will use this path | |
| 131 # plus the relative paths to the .so files listed in the .conf | |
| 132 # file. We have apache/cygwin checked into our tree so | |
| 133 # people don't have to install it into their cygwin. | |
| 134 cygusr = path_utils.PathFromBase('third_party', 'cygwin', 'usr') | |
| 135 httpd_conf = httpd_conf.replace('ServerRoot "/usr"', | |
| 136 'ServerRoot "%s"' % self._GetCygwinPath(cygusr)) | |
| 137 | |
| 138 # TODO(ojan): Instead of writing an extra file, checkin a conf file | |
| 139 # upstream. Or, even better, upstream/delete all our chrome http | |
| 140 # tests so we don't need this special-cased DocumentRoot and then | |
| 141 # just use the upstream | |
| 142 # conf file. | |
| 143 chrome_document_root = path_utils.PathFromBase('webkit', 'data', | |
| 144 'layout_tests') | |
| 145 if self._IsCygwin(): | |
| 146 chrome_document_root = self._GetCygwinPath(chrome_document_root) | |
| 147 httpd_conf = (httpd_conf + | |
| 148 self._GetVirtualHostConfig(chrome_document_root, 8081)) | |
| 149 | |
| 150 f = open(httpd_config_copy, 'wb') | |
| 151 f.write(httpd_conf) | |
| 152 f.close() | |
| 153 | |
| 154 if self._IsCygwin(): | |
| 155 return self._GetCygwinPath(httpd_config_copy) | |
| 156 return httpd_config_copy | |
| 157 | |
| 158 def _GetVirtualHostConfig(self, document_root, port, ssl=False): | |
| 159 """Returns a <VirtualHost> directive block for an httpd.conf file. | |
| 160 It will listen to 127.0.0.1 on each of the given port. | |
| 161 """ | |
| 162 return '\n'.join(('<VirtualHost 127.0.0.1:%s>' % port, | |
| 163 'DocumentRoot %s' % document_root, | |
| 164 ssl and 'SSLEngine On' or '', | |
| 165 '</VirtualHost>', '')) | |
| 166 | |
| 167 def _StartHttpdProcess(self): | |
| 168 """Starts the httpd process and returns whether there were errors.""" | |
| 169 # Use shell=True because we join the arguments into a string for | |
| 170 # the sake of Window/Cygwin and it needs quoting that breaks | |
| 171 # shell=False. | |
| 172 self._httpd_proc = subprocess.Popen(self._start_cmd, | |
| 173 stderr=subprocess.PIPE, | |
| 174 shell=True) | |
| 175 err = self._httpd_proc.stderr.read() | |
| 176 if len(err): | |
| 177 logging.debug(err) | |
| 178 return False | |
| 179 return True | |
| 180 | |
| 181 def Start(self): | |
| 182 """Starts the apache http server.""" | |
| 183 # Stop any currently running servers. | |
| 184 self.Stop() | |
| 185 | |
| 186 logging.debug("Starting apache http server") | |
| 187 server_started = self.WaitForAction(self._StartHttpdProcess) | |
| 188 if server_started: | |
| 189 logging.debug("Apache started. Testing ports") | |
| 190 server_started = self.WaitForAction(self.IsServerRunningOnAllPorts) | |
| 191 | |
| 192 if server_started: | |
| 193 logging.debug("Server successfully started") | |
| 194 else: | |
| 195 raise Exception('Failed to start http server') | |
| 196 | |
| 197 def Stop(self): | |
| 198 """Stops the apache http server.""" | |
| 199 logging.debug("Shutting down any running http servers") | |
| 200 httpd_pid = None | |
| 201 if os.path.exists(self._pid_file): | |
| 202 httpd_pid = int(open(self._pid_file).readline()) | |
| 203 path_utils.ShutDownHTTPServer(httpd_pid) | |
| OLD | NEW |