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

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

Issue 155173003: checkpoint unix version of apache win32 patch. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: re-daemonize apache on unix 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 | Annotate | Revision Log
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
(...skipping 10 matching lines...) Expand all
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 """A class to start/stop the apache http server used by layout tests."""
30 30
31
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 http_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 LayoutTestApacheHttpd(http_server_base.HttpServerBase):
45 def __init__(self, port_obj, output_dir, additional_dirs=None, number_of_ser vers=None): 42 def __init__(self, port_obj, output_dir, additional_dirs=None, number_of_ser vers=None):
46 """Args: 43 """Args:
(...skipping 14 matching lines...) Expand all
61 58
62 test_dir = self._port_obj.layout_tests_dir() 59 test_dir = self._port_obj.layout_tests_dir()
63 js_test_resources_dir = self._filesystem.join(test_dir, "resources") 60 js_test_resources_dir = self._filesystem.join(test_dir, "resources")
64 media_resources_dir = self._filesystem.join(test_dir, "media") 61 media_resources_dir = self._filesystem.join(test_dir, "media")
65 mime_types_path = self._filesystem.join(test_dir, "http", "conf", "mime. types") 62 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") 63 cert_file = self._filesystem.join(test_dir, "http", "conf", "webkit-http d.pem")
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") 66 document_root = self._filesystem.join(test_dir, "http", "tests")
70 67
71 # FIXME: We shouldn't be calling a protected method of _port_obj! 68 self._is_win = self._port_obj.host.platform.is_win()
72 executable = self._port_obj._path_to_apache() 69
70 executable = self._port_obj.path_to_apache()
71
72 server_root = self._filesystem.dirname(self._filesystem.dirname(executab le))
73 73
74 start_cmd = [executable, 74 start_cmd = [executable,
75 '-f', "\"%s\"" % self._get_apache_config_file_path(test_dir, output_ dir), 75 '-f', "%s" % self._port_obj.path_to_apache_config_file(),
76 '-C', "\'DocumentRoot \"%s\"\'" % document_root, 76 '-C', "ServerRoot %s" % server_root,
77 '-c', "\'Alias /js-test-resources \"%s\"'" % js_test_resources_dir, 77 '-C', "DocumentRoot %s" % document_root,
78 '-c', "\'Alias /media-resources \"%s\"'" % media_resources_dir, 78 '-c', "Alias /js-test-resources %s" % js_test_resources_dir,
79 '-c', "\'TypesConfig \"%s\"\'" % mime_types_path, 79 '-c', "Alias /media-resources %s" % media_resources_dir,
80 '-c', "\'CustomLog \"%s\" common\'" % access_log, 80 '-c', "TypesConfig %s" % mime_types_path,
81 '-c', "\'ErrorLog \"%s\"\'" % error_log, 81 '-c', "CustomLog %s common" % access_log,
82 '-C', "\'User \"%s\"\'" % os.environ.get("USERNAME", os.environ.get( "USER", "")), 82 '-c', "ErrorLog %s" % error_log,
83 '-c', "\'PidFile %s'" % self._pid_file, 83 '-c', "PidFile %s" % self._pid_file,
84 '-k', "start"] 84 ]
85
85 86
86 enable_ipv6 = self._port_obj.http_server_supports_ipv6() 87 enable_ipv6 = self._port_obj.http_server_supports_ipv6()
87 # Perform part of the checks Apache's APR does when trying to listen to 88 # 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 89 # 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 90 # IPV6 addresses when it fails on Apache. APR itself tries to call
90 # getaddrinfo() again without AI_ADDRCONFIG if the first call fails 91 # getaddrinfo() again without AI_ADDRCONFIG if the first call fails
91 # with EBADFLAGS, but that is not how it normally fails in our use 92 # with EBADFLAGS, but that is not how it normally fails in our use
92 # cases, so ignore that for now. 93 # cases, so ignore that for now.
93 # See https://bugs.webkit.org/show_bug.cgi?id=98602#c7 94 # See https://bugs.webkit.org/show_bug.cgi?id=98602#c7
94 try: 95 try:
95 socket.getaddrinfo('::1', 0, 0, 0, 0, socket.AI_ADDRCONFIG) 96 socket.getaddrinfo('::1', 0, 0, 0, 0, socket.AI_ADDRCONFIG)
96 except: 97 except:
97 enable_ipv6 = False 98 enable_ipv6 = False
98 99
99 for mapping in self._mappings: 100 for mapping in self._mappings:
100 port = mapping['port'] 101 port = mapping['port']
101 102
102 start_cmd += ['-C', "\'Listen 127.0.0.1:%d\'" % port] 103 start_cmd += ['-C', "Listen 127.0.0.1:%d" % port]
103 104
104 # We listen to both IPv4 and IPv6 loop-back addresses, but ignore 105 # We listen to both IPv4 and IPv6 loop-back addresses, but ignore
105 # requests to 8000 from random users on network. 106 # requests to 8000 from random users on network.
106 # See https://bugs.webkit.org/show_bug.cgi?id=37104 107 # See https://bugs.webkit.org/show_bug.cgi?id=37104
107 if enable_ipv6: 108 if enable_ipv6:
108 start_cmd += ['-C', "\'Listen [::1]:%d\'" % port] 109 start_cmd += ['-C', "Listen [::1]:%d" % port]
109 110
110 if additional_dirs: 111 if additional_dirs:
111 for alias, path in additional_dirs.iteritems(): 112 for alias, path in additional_dirs.iteritems():
112 start_cmd += ['-c', "\'Alias %s \"%s\"\'" % (alias, path), 113 start_cmd += ['-c', "'Alias %s \"%s\"\'" % (alias, path),
113 # Disable CGI handler for additional dirs. 114 # Disable CGI handler for additional dirs.
114 '-c', "\'<Location %s>\'" % alias, 115 '-c', "<Location %s>" % alias,
115 '-c', "\'RemoveHandler .cgi .pl\'", 116 '-c', "RemoveHandler .cgi .pl",
116 '-c', "\'</Location>\'"] 117 '-c', "</Location>"]
117 118
118 if self._number_of_servers: 119 if self._number_of_servers:
119 start_cmd += ['-c', "\'StartServers %d\'" % self._number_of_servers, 120 if self._is_win:
120 '-c', "\'MinSpareServers %d\'" % self._number_of_serve rs, 121 start_cmd += ['-c', "ThreadsPerChild %d" % (self._number_of_serv ers * 8)]
121 '-c', "\'MaxSpareServers %d\'" % self._number_of_serve rs] 122 else:
123 start_cmd += ['-c', "StartServers %d" % (self._number_of_servers * 4),
124 '-c', "MinSpareServers %d" % (self._number_of_serv ers * 4),
125 '-c', "MaxSpareServers %d" % (self._number_of_serv ers * 4)]
122 126
123 stop_cmd = [executable, 127 start_cmd.extend(['-c', "SSLCertificateFile %s" % cert_file])
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
128 start_cmd.extend(['-c', "\'SSLCertificateFile %s\'" % cert_file]) 129 if not self._is_win:
129 # Join the string here so that Cygwin/Windows and Mac/Linux 130 start_cmd.extend(['-C', 'User "%s"' % os.environ.get('USERNAME', os. environ.get('USER', '')),
130 # can use the same code. Otherwise, we could remove the single 131 '-k', 'start'])
131 # quotes above and keep cmd as a sequence. 132 self._start_cmd = start_cmd
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 133
151 def _spawn_process(self): 134 def _spawn_process(self):
152 _log.debug('Starting %s server, cmd="%s"' % (self._name, str(self._start _cmd))) 135 _log.debug('Starting %s server, cmd="%s"' % (self._name, str(self._start _cmd)))
153 retval, err = self._run(self._start_cmd) 136 self._process = self._executive.popen(self._start_cmd)
154 if retval or len(err): 137 # FIXME: Figure out how to detect startup logging and print it ...
155 raise http_server_base.ServerError('Failed to start %s: %s' % (self. _name, err)) 138 if self._process.returncode is not None:
139 retval = self._process.returncode
140 err = self._process.stderr.read()
141 if retval or len(err):
142 raise http_server_base.ServerError('Failed to start %s: %s' % (s elf._name, err))
156 143
157 # For some reason apache isn't guaranteed to have created the pid file b efore 144 # 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. 145 # 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)): 146 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) 147 raise http_server_base.ServerError('Failed to start %s: no pid file found' % self._name)
161 148
162 return int(self._filesystem.read_text_file(self._pid_file)) 149 return int(self._filesystem.read_text_file(self._pid_file))
163 150
151 def stop(self):
152 self._stop_running_server()
153
164 def _stop_running_server(self): 154 def _stop_running_server(self):
165 # If apache was forcefully killed, the pid file will not have been delet ed, so check 155 # 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. 156 # 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): 157 if self._pid and not self._executive.check_running_pid(self._pid):
168 self._filesystem.remove(self._pid_file) 158 self._filesystem.remove(self._pid_file)
169 return 159 return
170 160
171 retval, err = self._run(self._stop_cmd) 161 if self._is_win:
162 self._executive.kill_process(self._pid)
163 return
164
165 proc = self._executive.popen([self._port_obj.path_to_apache(),
166 '-f', self._port_obj.path_to_apache_config _file(),
167 '-c', 'PidFile "%s"' % self._pid_file,
168 '-k', 'stop'], stderr=self._executive.PIPE )
169 proc.wait()
170 retval = proc.returncode
171 err = proc.stderr.read()
172 if retval or len(err): 172 if retval or len(err):
173 raise http_server_base.ServerError('Failed to stop %s: %s' % (self._ name, err)) 173 raise http_server_base.ServerError('Failed to stop %s: %s' % (self._ name, err))
174 174
175 # For some reason apache isn't guaranteed to have actually stopped after 175 # 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 176 # the stop command returns, so we wait a little while longer for the
177 # pid file to be removed. 177 # pid file to be removed.
178 if not self._wait_for_action(lambda: not self._filesystem.exists(self._p id_file)): 178 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) 179 raise http_server_base.ServerError('Failed to stop %s: pid file stil l exists' % 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