Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: Tools/Scripts/webkitpy/layout_tests/servers/apache_http.py

Issue 135583003: checkpoint Blink-side work to use Apache on Windows (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: new rollup patch Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright (C) 2011 Google Inc. All rights reserved. 1 # Copyright (C) 2011 Google Inc. All rights reserved.
2 # 2 #
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are 4 # modification, are permitted provided that the following conditions are
5 # met: 5 # met:
6 # 6 #
7 # * Redistributions of source code must retain the above copyright 7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer. 8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above 9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer 10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the 11 # in the documentation and/or other materials provided with the
12 # distribution. 12 # distribution.
13 # * Neither the name of Google Inc. nor the names of its 13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from 14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission. 15 # this software without specific prior written permission.
16 # 16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28
29 """A class to start/stop the apache http server used by layout tests.""" 29 """Start and stop the Apache HTTP server as it is used by the layout tests."""
30
31 30
32 import logging 31 import logging
33 import os 32 import os
34 import re
35 import socket 33 import socket
36 import sys
37 34
38 from webkitpy.layout_tests.servers import http_server_base 35 from webkitpy.layout_tests.servers import server_base
39 36
40 37
41 _log = logging.getLogger(__name__) 38 _log = logging.getLogger(__name__)
42 39
43 40
44 class LayoutTestApacheHttpd(http_server_base.HttpServerBase): 41 class ApacheHttpd(server_base.ServerBase):
45 def __init__(self, port_obj, output_dir, additional_dirs=None, number_of_ser vers=None): 42 def __init__(self, port_obj, output_dir, number_of_servers):
46 """Args: 43 super(ApacheHttpd, self).__init__(port_obj, output_dir)
47 port_obj: handle to the platform-specific routines
48 output_dir: the absolute path to the layout test result directory
49 """
50 http_server_base.HttpServerBase.__init__(self, port_obj, number_of_serve rs)
51 # We use the name "httpd" instead of "apache" to make our paths (e.g. th e pid file: /tmp/WebKit/httpd.pid) 44 # We use the name "httpd" instead of "apache" to make our paths (e.g. th e pid file: /tmp/WebKit/httpd.pid)
52 # match old-run-webkit-tests: https://bugs.webkit.org/show_bug.cgi?id=63 956 45 # match old-run-webkit-tests: https://bugs.webkit.org/show_bug.cgi?id=63 956
53 self._name = 'httpd' 46 self._name = 'httpd'
54 self._mappings = [{'port': 8000}, 47 self._mappings = [{'port': 8000},
55 {'port': 8080}, 48 {'port': 8080},
56 {'port': 8443, 'sslcert': True}] 49 {'port': 8443, 'sslcert': True}]
57 self._output_dir = output_dir 50 self._number_of_servers = number_of_servers
58 self._filesystem.maybe_make_directory(output_dir)
59 51
60 self._pid_file = self._filesystem.join(self._runtime_path, '%s.pid' % se lf._name) 52 self._pid_file = self._filesystem.join(self._runtime_path, '%s.pid' % se lf._name)
61 53
54 executable = self._port_obj.path_to_apache()
55 server_root = self._filesystem.dirname(self._filesystem.dirname(executab le))
56
62 test_dir = self._port_obj.layout_tests_dir() 57 test_dir = self._port_obj.layout_tests_dir()
58 document_root = self._filesystem.join(test_dir, "http", "tests")
63 js_test_resources_dir = self._filesystem.join(test_dir, "resources") 59 js_test_resources_dir = self._filesystem.join(test_dir, "resources")
64 media_resources_dir = self._filesystem.join(test_dir, "media") 60 media_resources_dir = self._filesystem.join(test_dir, "media")
65 mime_types_path = self._filesystem.join(test_dir, "http", "conf", "mime. types") 61 mime_types_path = self._filesystem.join(test_dir, "http", "conf", "mime. types")
66 cert_file = self._filesystem.join(test_dir, "http", "conf", "webkit-http d.pem") 62 cert_file = self._filesystem.join(test_dir, "http", "conf", "webkit-http d.pem")
63
67 access_log = self._filesystem.join(output_dir, "access_log.txt") 64 access_log = self._filesystem.join(output_dir, "access_log.txt")
68 error_log = self._filesystem.join(output_dir, "error_log.txt") 65 error_log = self._filesystem.join(output_dir, "error_log.txt")
69 document_root = self._filesystem.join(test_dir, "http", "tests")
70 66
71 # FIXME: We shouldn't be calling a protected method of _port_obj! 67 self._is_win = self._port_obj.host.platform.is_win()
72 executable = self._port_obj._path_to_apache()
73 68
74 start_cmd = [executable, 69 start_cmd = [executable,
75 '-f', "\"%s\"" % self._get_apache_config_file_path(test_dir, output_ dir), 70 '-f', '%s' % self._port_obj.path_to_apache_config_file(),
76 '-C', "\'DocumentRoot \"%s\"\'" % document_root, 71 '-C', 'ServerRoot "%s"' % server_root,
77 '-c', "\'Alias /js-test-resources \"%s\"'" % js_test_resources_dir, 72 '-C', 'DocumentRoot "%s"' % document_root,
78 '-c', "\'Alias /media-resources \"%s\"'" % media_resources_dir, 73 '-c', 'Alias /js-test-resources "%s"' % js_test_resources_dir,
79 '-c', "\'TypesConfig \"%s\"\'" % mime_types_path, 74 '-c', 'Alias /media-resources "%s"' % media_resources_dir,
80 '-c', "\'CustomLog \"%s\" common\'" % access_log, 75 '-c', 'TypesConfig "%s"' % mime_types_path,
81 '-c', "\'ErrorLog \"%s\"\'" % error_log, 76 '-c', 'CustomLog "%s" common' % access_log,
82 '-C', "\'User \"%s\"\'" % os.environ.get("USERNAME", os.environ.get( "USER", "")), 77 '-c', 'ErrorLog "%s"' % error_log,
83 '-c', "\'PidFile %s'" % self._pid_file, 78 '-c', 'PidFile %s' % self._pid_file,
84 '-k', "start"] 79 '-c', 'SSLCertificateFile "%s"' % cert_file,
80 ]
81
82 if self._is_win:
83 start_cmd += ['-c', "ThreadsPerChild %d" % (self._number_of_servers * 2)]
84 else:
85 start_cmd += ['-c', "StartServers %d" % self._number_of_servers,
86 '-c', "MinSpareServers %d" % self._number_of_servers,
87 '-c', "MaxSpareServers %d" % self._number_of_servers,
88 '-C', 'User "%s"' % os.environ.get('USERNAME', os.envi ron.get('USER', '')),
89 '-k', 'start']
85 90
86 enable_ipv6 = self._port_obj.http_server_supports_ipv6() 91 enable_ipv6 = self._port_obj.http_server_supports_ipv6()
87 # Perform part of the checks Apache's APR does when trying to listen to 92 # Perform part of the checks Apache's APR does when trying to listen to
88 # a specific host/port. This allows us to avoid trying to listen to 93 # a specific host/port. This allows us to avoid trying to listen to
89 # IPV6 addresses when it fails on Apache. APR itself tries to call 94 # IPV6 addresses when it fails on Apache. APR itself tries to call
90 # getaddrinfo() again without AI_ADDRCONFIG if the first call fails 95 # getaddrinfo() again without AI_ADDRCONFIG if the first call fails
91 # with EBADFLAGS, but that is not how it normally fails in our use 96 # with EBADFLAGS, but that is not how it normally fails in our use
92 # cases, so ignore that for now. 97 # cases, so ignore that for now.
93 # See https://bugs.webkit.org/show_bug.cgi?id=98602#c7 98 # See https://bugs.webkit.org/show_bug.cgi?id=98602#c7
94 try: 99 try:
95 socket.getaddrinfo('::1', 0, 0, 0, 0, socket.AI_ADDRCONFIG) 100 socket.getaddrinfo('::1', 0, 0, 0, 0, socket.AI_ADDRCONFIG)
96 except: 101 except:
97 enable_ipv6 = False 102 enable_ipv6 = False
98 103
99 for mapping in self._mappings: 104 for mapping in self._mappings:
100 port = mapping['port'] 105 port = mapping['port']
101 106
102 start_cmd += ['-C', "\'Listen 127.0.0.1:%d\'" % port] 107 start_cmd += ['-C', "Listen 127.0.0.1:%d" % port]
103 108
104 # We listen to both IPv4 and IPv6 loop-back addresses, but ignore 109 # We listen to both IPv4 and IPv6 loop-back addresses, but ignore
105 # requests to 8000 from random users on network. 110 # requests to 8000 from random users on network.
106 # See https://bugs.webkit.org/show_bug.cgi?id=37104 111 # See https://bugs.webkit.org/show_bug.cgi?id=37104
107 if enable_ipv6: 112 if enable_ipv6:
108 start_cmd += ['-C', "\'Listen [::1]:%d\'" % port] 113 start_cmd += ['-C', "Listen [::1]:%d" % port]
109 114
110 if additional_dirs: 115 self._start_cmd = start_cmd
111 for alias, path in additional_dirs.iteritems():
112 start_cmd += ['-c', "\'Alias %s \"%s\"\'" % (alias, path),
113 # Disable CGI handler for additional dirs.
114 '-c', "\'<Location %s>\'" % alias,
115 '-c', "\'RemoveHandler .cgi .pl\'",
116 '-c', "\'</Location>\'"]
117
118 if self._number_of_servers:
119 start_cmd += ['-c', "\'StartServers %d\'" % self._number_of_servers,
120 '-c', "\'MinSpareServers %d\'" % self._number_of_serve rs,
121 '-c', "\'MaxSpareServers %d\'" % self._number_of_serve rs]
122
123 stop_cmd = [executable,
124 '-f', "\"%s\"" % self._get_apache_config_file_path(test_dir, output_ dir),
125 '-c', "\'PidFile %s'" % self._pid_file,
126 '-k', "stop"]
127
128 start_cmd.extend(['-c', "\'SSLCertificateFile %s\'" % cert_file])
129 # Join the string here so that Cygwin/Windows and Mac/Linux
130 # can use the same code. Otherwise, we could remove the single
131 # quotes above and keep cmd as a sequence.
132 # FIXME: It's unclear if this is still needed.
133 self._start_cmd = " ".join(start_cmd)
134 self._stop_cmd = " ".join(stop_cmd)
135
136 def _get_apache_config_file_path(self, test_dir, output_dir):
137 """Returns the path to the apache config file to use.
138 Args:
139 test_dir: absolute path to the LayoutTests directory.
140 output_dir: absolute path to the layout test results directory.
141 """
142 httpd_config = self._port_obj._path_to_apache_config_file()
143 httpd_config_copy = os.path.join(output_dir, "httpd.conf")
144 httpd_conf = self._filesystem.read_text_file(httpd_config)
145
146 # FIXME: Why do we need to copy the config file since we're not modifyin g it?
147 self._filesystem.write_text_file(httpd_config_copy, httpd_conf)
148
149 return httpd_config_copy
150 116
151 def _spawn_process(self): 117 def _spawn_process(self):
152 _log.debug('Starting %s server, cmd="%s"' % (self._name, str(self._start _cmd))) 118 _log.debug('Starting %s server, cmd="%s"' % (self._name, str(self._start _cmd)))
153 retval, err = self._run(self._start_cmd) 119 self._process = self._executive.popen(self._start_cmd)
154 if retval or len(err): 120 if self._process.returncode is not None:
155 raise http_server_base.ServerError('Failed to start %s: %s' % (self. _name, err)) 121 retval = self._process.returncode
122 err = self._process.stderr.read()
123 if retval or len(err):
124 raise server_base.ServerError('Failed to start %s: %s' % (self._ name, err))
156 125
157 # For some reason apache isn't guaranteed to have created the pid file b efore 126 # For some reason apache isn't guaranteed to have created the pid file b efore
158 # the process exits, so we wait a little while longer. 127 # the process exits, so we wait a little while longer.
159 if not self._wait_for_action(lambda: self._filesystem.exists(self._pid_f ile)): 128 if not self._wait_for_action(lambda: self._filesystem.exists(self._pid_f ile)):
160 raise http_server_base.ServerError('Failed to start %s: no pid file found' % self._name) 129 raise server_base.ServerError('Failed to start %s: no pid file found ' % self._name)
161 130
162 return int(self._filesystem.read_text_file(self._pid_file)) 131 return int(self._filesystem.read_text_file(self._pid_file))
163 132
133 def stop(self):
134 self._stop_running_server()
135
164 def _stop_running_server(self): 136 def _stop_running_server(self):
165 # If apache was forcefully killed, the pid file will not have been delet ed, so check 137 # If apache was forcefully killed, the pid file will not have been delet ed, so check
166 # that the process specified by the pid_file no longer exists before del eting the file. 138 # that the process specified by the pid_file no longer exists before del eting the file.
167 if self._pid and not self._executive.check_running_pid(self._pid): 139 if self._pid and not self._executive.check_running_pid(self._pid):
168 self._filesystem.remove(self._pid_file) 140 self._filesystem.remove(self._pid_file)
169 return 141 return
170 142
171 retval, err = self._run(self._stop_cmd) 143 if self._is_win:
144 self._executive.kill_process(self._pid)
145 return
146
147 proc = self._executive.popen([self._port_obj.path_to_apache(),
148 '-f', self._port_obj.path_to_apache_config _file(),
149 '-c', 'PidFile "%s"' % self._pid_file,
150 '-k', 'stop'], stderr=self._executive.PIPE )
151 proc.wait()
152 retval = proc.returncode
153 err = proc.stderr.read()
172 if retval or len(err): 154 if retval or len(err):
173 raise http_server_base.ServerError('Failed to stop %s: %s' % (self._ name, err)) 155 raise server_base.ServerError('Failed to stop %s: %s' % (self._name, err))
174 156
175 # For some reason apache isn't guaranteed to have actually stopped after 157 # For some reason apache isn't guaranteed to have actually stopped after
176 # the stop command returns, so we wait a little while longer for the 158 # the stop command returns, so we wait a little while longer for the
177 # pid file to be removed. 159 # pid file to be removed.
178 if not self._wait_for_action(lambda: not self._filesystem.exists(self._p id_file)): 160 if not self._wait_for_action(lambda: not self._filesystem.exists(self._p id_file)):
179 raise http_server_base.ServerError('Failed to stop %s: pid file stil l exists' % self._name) 161 raise server_base.ServerError('Failed to stop %s: pid file still exi sts' % self._name)
180
181 def _run(self, cmd):
182 # Use shell=True because we join the arguments into a string for
183 # the sake of Window/Cygwin and it needs quoting that breaks
184 # shell=False.
185 # FIXME: We should not need to be joining shell arguments into strings.
186 # shell=True is a trail of tears.
187 # Note: Not thread safe: http://bugs.python.org/issue2320
188 process = self._executive.popen(cmd, shell=True, stderr=self._executive. PIPE)
189 process.wait()
190 retval = process.returncode
191 err = process.stderr.read()
192 return (retval, err)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698