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

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

Issue 146173004: Fix run-blink-httpd to work w/ Apache and refactor webkitpy.layout_tests.servers. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: add cli_wrapper and update run-* scripts 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
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
71 executable = self._port_obj.path_to_apache()
72 66
73 start_cmd = [executable, 67 start_cmd = [executable,
74 '-f', "\"%s\"" % self._port_obj.path_to_apache_config_file(), 68 '-f', '%s' % self._port_obj.path_to_apache_config_file(),
75 '-C', "\'DocumentRoot \"%s\"\'" % document_root, 69 '-C', 'ServerRoot "%s"' % server_root,
76 '-c', "\'Alias /js-test-resources \"%s\"'" % js_test_resources_dir, 70 '-C', 'DocumentRoot "%s"' % document_root,
77 '-c', "\'Alias /media-resources \"%s\"'" % media_resources_dir, 71 '-c', 'Alias /js-test-resources "%s"' % js_test_resources_dir,
78 '-c', "\'TypesConfig \"%s\"\'" % mime_types_path, 72 '-c', 'Alias /media-resources "%s"' % media_resources_dir,
79 '-c', "\'CustomLog \"%s\" common\'" % access_log, 73 '-c', 'TypesConfig "%s"' % mime_types_path,
80 '-c', "\'ErrorLog \"%s\"\'" % error_log, 74 '-c', 'CustomLog "%s" common' % access_log,
81 '-C', "\'User \"%s\"\'" % os.environ.get("USERNAME", os.environ.get( "USER", "")), 75 '-c', 'ErrorLog "%s"' % error_log,
82 '-c', "\'PidFile %s'" % self._pid_file, 76 '-C', 'User "%s"' % os.environ.get("USERNAME", os.environ.get("USER" , "")),
83 '-k', "start"] 77 '-c', 'PidFile %s' % self._pid_file,
78 '-c', 'SSLCertificateFile "%s"' % cert_file,
79 '-c', 'StartServers %d' % number_of_servers,
80 '-c', 'MinSpareServers %d' % number_of_servers,
81 '-c', 'MaxSpareServers %d' % number_of_servers,
82 '-k', 'start']
84 83
85 enable_ipv6 = self._port_obj.http_server_supports_ipv6() 84 enable_ipv6 = self._port_obj.http_server_supports_ipv6()
86 # Perform part of the checks Apache's APR does when trying to listen to 85 # Perform part of the checks Apache's APR does when trying to listen to
87 # a specific host/port. This allows us to avoid trying to listen to 86 # a specific host/port. This allows us to avoid trying to listen to
88 # IPV6 addresses when it fails on Apache. APR itself tries to call 87 # IPV6 addresses when it fails on Apache. APR itself tries to call
89 # getaddrinfo() again without AI_ADDRCONFIG if the first call fails 88 # getaddrinfo() again without AI_ADDRCONFIG if the first call fails
90 # with EBADFLAGS, but that is not how it normally fails in our use 89 # with EBADFLAGS, but that is not how it normally fails in our use
91 # cases, so ignore that for now. 90 # cases, so ignore that for now.
92 # See https://bugs.webkit.org/show_bug.cgi?id=98602#c7 91 # See https://bugs.webkit.org/show_bug.cgi?id=98602#c7
93 try: 92 try:
94 socket.getaddrinfo('::1', 0, 0, 0, 0, socket.AI_ADDRCONFIG) 93 socket.getaddrinfo('::1', 0, 0, 0, 0, socket.AI_ADDRCONFIG)
95 except: 94 except:
96 enable_ipv6 = False 95 enable_ipv6 = False
97 96
98 for mapping in self._mappings: 97 for mapping in self._mappings:
99 port = mapping['port'] 98 port = mapping['port']
100 99
101 start_cmd += ['-C', "\'Listen 127.0.0.1:%d\'" % port] 100 start_cmd += ['-C', "Listen 127.0.0.1:%d" % port]
102 101
103 # We listen to both IPv4 and IPv6 loop-back addresses, but ignore 102 # We listen to both IPv4 and IPv6 loop-back addresses, but ignore
104 # requests to 8000 from random users on network. 103 # requests to 8000 from random users on network.
105 # See https://bugs.webkit.org/show_bug.cgi?id=37104 104 # See https://bugs.webkit.org/show_bug.cgi?id=37104
106 if enable_ipv6: 105 if enable_ipv6:
107 start_cmd += ['-C', "\'Listen [::1]:%d\'" % port] 106 start_cmd += ['-C', "Listen [::1]:%d" % port]
108 107
109 if additional_dirs: 108 self._start_cmd = start_cmd
110 for alias, path in additional_dirs.iteritems():
111 start_cmd += ['-c', "\'Alias %s \"%s\"\'" % (alias, path),
112 # Disable CGI handler for additional dirs.
113 '-c', "\'<Location %s>\'" % alias,
114 '-c', "\'RemoveHandler .cgi .pl\'",
115 '-c', "\'</Location>\'"]
116
117 if self._number_of_servers:
118 start_cmd += ['-c', "\'StartServers %d\'" % self._number_of_servers,
119 '-c', "\'MinSpareServers %d\'" % self._number_of_serve rs,
120 '-c', "\'MaxSpareServers %d\'" % self._number_of_serve rs]
121
122 stop_cmd = [executable,
123 '-f', "\"%s\"" % self._port_obj.path_to_apache_config_file(),
124 '-c', "\'PidFile %s'" % self._pid_file,
125 '-k', "stop"]
126
127 start_cmd.extend(['-c', "\'SSLCertificateFile %s\'" % cert_file])
128 # Join the string here so that Cygwin/Windows and Mac/Linux
129 # can use the same code. Otherwise, we could remove the single
130 # quotes above and keep cmd as a sequence.
131 # FIXME: It's unclear if this is still needed.
132 self._start_cmd = " ".join(start_cmd)
133 self._stop_cmd = " ".join(stop_cmd)
134 109
135 def _spawn_process(self): 110 def _spawn_process(self):
136 _log.debug('Starting %s server, cmd="%s"' % (self._name, str(self._start _cmd))) 111 _log.debug('Starting %s server, cmd="%s"' % (self._name, str(self._start _cmd)))
137 retval, err = self._run(self._start_cmd) 112 self._process = self._executive.popen(self._start_cmd)
138 if retval or len(err): 113 if self._process.returncode is not None:
139 raise http_server_base.ServerError('Failed to start %s: %s' % (self. _name, err)) 114 retval = self._process.returncode
115 err = self._process.stderr.read()
116 if retval or len(err):
117 raise server_base.ServerError('Failed to start %s: %s' % (self._ name, err))
140 118
141 # For some reason apache isn't guaranteed to have created the pid file b efore 119 # For some reason apache isn't guaranteed to have created the pid file b efore
142 # the process exits, so we wait a little while longer. 120 # the process exits, so we wait a little while longer.
143 if not self._wait_for_action(lambda: self._filesystem.exists(self._pid_f ile)): 121 if not self._wait_for_action(lambda: self._filesystem.exists(self._pid_f ile)):
144 raise http_server_base.ServerError('Failed to start %s: no pid file found' % self._name) 122 raise server_base.ServerError('Failed to start %s: no pid file found ' % self._name)
145 123
146 return int(self._filesystem.read_text_file(self._pid_file)) 124 return int(self._filesystem.read_text_file(self._pid_file))
147 125
126 def stop(self):
127 self._stop_running_server()
128
148 def _stop_running_server(self): 129 def _stop_running_server(self):
149 # If apache was forcefully killed, the pid file will not have been delet ed, so check 130 # If apache was forcefully killed, the pid file will not have been delet ed, so check
150 # that the process specified by the pid_file no longer exists before del eting the file. 131 # that the process specified by the pid_file no longer exists before del eting the file.
151 if self._pid and not self._executive.check_running_pid(self._pid): 132 if self._pid and not self._executive.check_running_pid(self._pid):
152 self._filesystem.remove(self._pid_file) 133 self._filesystem.remove(self._pid_file)
153 return 134 return
154 135
155 retval, err = self._run(self._stop_cmd) 136 proc = self._executive.popen([self._port_obj.path_to_apache(),
137 '-f', self._port_obj.path_to_apache_config _file(),
138 '-c', 'PidFile "%s"' % self._pid_file,
139 '-k', 'stop'], stderr=self._executive.PIPE )
140 proc.wait()
141 retval = proc.returncode
142 err = proc.stderr.read()
156 if retval or len(err): 143 if retval or len(err):
157 raise http_server_base.ServerError('Failed to stop %s: %s' % (self._ name, err)) 144 raise server_base.ServerError('Failed to stop %s: %s' % (self._name, err))
158 145
159 # For some reason apache isn't guaranteed to have actually stopped after 146 # For some reason apache isn't guaranteed to have actually stopped after
160 # the stop command returns, so we wait a little while longer for the 147 # the stop command returns, so we wait a little while longer for the
161 # pid file to be removed. 148 # pid file to be removed.
162 if not self._wait_for_action(lambda: not self._filesystem.exists(self._p id_file)): 149 if not self._wait_for_action(lambda: not self._filesystem.exists(self._p id_file)):
163 raise http_server_base.ServerError('Failed to stop %s: pid file stil l exists' % self._name) 150 raise server_base.ServerError('Failed to stop %s: pid file still exi sts' % self._name)
164
165 def _run(self, cmd):
166 # Use shell=True because we join the arguments into a string for
167 # the sake of Window/Cygwin and it needs quoting that breaks
168 # shell=False.
169 # FIXME: We should not need to be joining shell arguments into strings.
170 # shell=True is a trail of tears.
171 # Note: Not thread safe: http://bugs.python.org/issue2320
172 process = self._executive.popen(cmd, shell=True, stderr=self._executive. PIPE)
173 process.wait()
174 retval = process.returncode
175 err = process.stderr.read()
176 return (retval, err)
OLDNEW
« no previous file with comments | « Tools/Scripts/webkitpy/layout_tests/port/test.py ('k') | Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698