1d0825bca7fe65beaee391d30da42e937db621564Steve Block#!/usr/bin/env python
25e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block# Copyright (C) 2010 Google Inc. All rights reserved.
3d0825bca7fe65beaee391d30da42e937db621564Steve Block#
4d0825bca7fe65beaee391d30da42e937db621564Steve Block# Redistribution and use in source and binary forms, with or without
5d0825bca7fe65beaee391d30da42e937db621564Steve Block# modification, are permitted provided that the following conditions are
6d0825bca7fe65beaee391d30da42e937db621564Steve Block# met:
7d0825bca7fe65beaee391d30da42e937db621564Steve Block#
8d0825bca7fe65beaee391d30da42e937db621564Steve Block#     * Redistributions of source code must retain the above copyright
9d0825bca7fe65beaee391d30da42e937db621564Steve Block# notice, this list of conditions and the following disclaimer.
10d0825bca7fe65beaee391d30da42e937db621564Steve Block#     * Redistributions in binary form must reproduce the above
11d0825bca7fe65beaee391d30da42e937db621564Steve Block# copyright notice, this list of conditions and the following disclaimer
12d0825bca7fe65beaee391d30da42e937db621564Steve Block# in the documentation and/or other materials provided with the
13d0825bca7fe65beaee391d30da42e937db621564Steve Block# distribution.
145e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block#     * Neither the name of Google Inc. nor the names of its
15d0825bca7fe65beaee391d30da42e937db621564Steve Block# contributors may be used to endorse or promote products derived from
16d0825bca7fe65beaee391d30da42e937db621564Steve Block# this software without specific prior written permission.
17d0825bca7fe65beaee391d30da42e937db621564Steve Block#
18d0825bca7fe65beaee391d30da42e937db621564Steve Block# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19d0825bca7fe65beaee391d30da42e937db621564Steve Block# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20d0825bca7fe65beaee391d30da42e937db621564Steve Block# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21d0825bca7fe65beaee391d30da42e937db621564Steve Block# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22d0825bca7fe65beaee391d30da42e937db621564Steve Block# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23d0825bca7fe65beaee391d30da42e937db621564Steve Block# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24d0825bca7fe65beaee391d30da42e937db621564Steve Block# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25d0825bca7fe65beaee391d30da42e937db621564Steve Block# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26d0825bca7fe65beaee391d30da42e937db621564Steve Block# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27d0825bca7fe65beaee391d30da42e937db621564Steve Block# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28d0825bca7fe65beaee391d30da42e937db621564Steve Block# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29d0825bca7fe65beaee391d30da42e937db621564Steve Block
30d0825bca7fe65beaee391d30da42e937db621564Steve Block"""A class to help start/stop the lighttpd server used by layout tests."""
31d0825bca7fe65beaee391d30da42e937db621564Steve Block
3221939df44de1705786c545cd1bf519d47250322dBen Murdochfrom __future__ import with_statement
33d0825bca7fe65beaee391d30da42e937db621564Steve Block
3421939df44de1705786c545cd1bf519d47250322dBen Murdochimport codecs
35d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport logging
36d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport optparse
37d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport os
38d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport shutil
39d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport subprocess
40d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport sys
41d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport tempfile
42d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport time
43d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport urllib
44d0825bca7fe65beaee391d30da42e937db621564Steve Block
45dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockimport factory
46d0825bca7fe65beaee391d30da42e937db621564Steve Blockimport http_server_base
47d0825bca7fe65beaee391d30da42e937db621564Steve Block
48dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block_log = logging.getLogger("webkitpy.layout_tests.port.http_server")
49dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
50d0825bca7fe65beaee391d30da42e937db621564Steve Block
518a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Blockclass HttpdNotStarted(Exception):
528a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    pass
53d0825bca7fe65beaee391d30da42e937db621564Steve Block
54d0825bca7fe65beaee391d30da42e937db621564Steve Block
55d0825bca7fe65beaee391d30da42e937db621564Steve Blockclass Lighttpd(http_server_base.HttpServerBase):
568a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block
578a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    def __init__(self, port_obj, output_dir, background=False, port=None,
582bde8e466a4451c7319e3a072d118917957d6554Steve Block                 root=None, run_background=None, layout_tests_dir=None):
59d0825bca7fe65beaee391d30da42e937db621564Steve Block        """Args:
60d0825bca7fe65beaee391d30da42e937db621564Steve Block          output_dir: the absolute path to the layout test result directory
61d0825bca7fe65beaee391d30da42e937db621564Steve Block        """
628a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        # Webkit tests
638a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        http_server_base.HttpServerBase.__init__(self, port_obj)
64d0825bca7fe65beaee391d30da42e937db621564Steve Block        self._output_dir = output_dir
65d0825bca7fe65beaee391d30da42e937db621564Steve Block        self._process = None
66d0825bca7fe65beaee391d30da42e937db621564Steve Block        self._port = port
67d0825bca7fe65beaee391d30da42e937db621564Steve Block        self._root = root
68d0825bca7fe65beaee391d30da42e937db621564Steve Block        self._run_background = run_background
692bde8e466a4451c7319e3a072d118917957d6554Steve Block        self._layout_tests_dir = layout_tests_dir
702bde8e466a4451c7319e3a072d118917957d6554Steve Block
71d0825bca7fe65beaee391d30da42e937db621564Steve Block        if self._port:
72d0825bca7fe65beaee391d30da42e937db621564Steve Block            self._port = int(self._port)
73d0825bca7fe65beaee391d30da42e937db621564Steve Block
742bde8e466a4451c7319e3a072d118917957d6554Steve Block        if not self._layout_tests_dir:
752bde8e466a4451c7319e3a072d118917957d6554Steve Block            self._layout_tests_dir = self._port_obj.layout_tests_dir()
762bde8e466a4451c7319e3a072d118917957d6554Steve Block
778a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        try:
788a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block            self._webkit_tests = os.path.join(
792bde8e466a4451c7319e3a072d118917957d6554Steve Block                self._layout_tests_dir, 'http', 'tests')
808a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block            self._js_test_resource = os.path.join(
812bde8e466a4451c7319e3a072d118917957d6554Steve Block                self._layout_tests_dir, 'fast', 'js', 'resources')
8281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            self._media_resource = os.path.join(
832bde8e466a4451c7319e3a072d118917957d6554Steve Block                self._layout_tests_dir, 'media')
8481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
858a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        except:
868a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block            self._webkit_tests = None
878a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block            self._js_test_resource = None
8881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            self._media_resource = None
898a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block
908a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        # Self generated certificate for SSL server (for client cert get
918a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        # <base-path>\chrome\test\data\ssl\certs\root_ca_cert.crt)
928a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        self._pem_file = os.path.join(
938a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block            os.path.dirname(os.path.abspath(__file__)), 'httpd2.pem')
948a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block
958a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        # One mapping where we can get to everything
968a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        self.VIRTUALCONFIG = []
978a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block
988a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        if self._webkit_tests:
998a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block            self.VIRTUALCONFIG.extend(
1008a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block               # Three mappings (one with SSL) for LayoutTests http tests
1018a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block               [{'port': 8000, 'docroot': self._webkit_tests},
1028a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block                {'port': 8080, 'docroot': self._webkit_tests},
1038a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block                {'port': 8443, 'docroot': self._webkit_tests,
1048a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block                 'sslcert': self._pem_file}])
1058a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block
106d0825bca7fe65beaee391d30da42e937db621564Steve Block    def is_running(self):
107d0825bca7fe65beaee391d30da42e937db621564Steve Block        return self._process != None
108d0825bca7fe65beaee391d30da42e937db621564Steve Block
109d0825bca7fe65beaee391d30da42e937db621564Steve Block    def start(self):
110d0825bca7fe65beaee391d30da42e937db621564Steve Block        if self.is_running():
111d0825bca7fe65beaee391d30da42e937db621564Steve Block            raise 'Lighttpd already running'
112d0825bca7fe65beaee391d30da42e937db621564Steve Block
113f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        base_conf_file = self._port_obj.path_from_webkit_base('Tools',
1148a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block            'Scripts', 'webkitpy', 'layout_tests', 'port', 'lighttpd.conf')
115d0825bca7fe65beaee391d30da42e937db621564Steve Block        out_conf_file = os.path.join(self._output_dir, 'lighttpd.conf')
116d0825bca7fe65beaee391d30da42e937db621564Steve Block        time_str = time.strftime("%d%b%Y-%H%M%S")
117d0825bca7fe65beaee391d30da42e937db621564Steve Block        access_file_name = "access.log-" + time_str + ".txt"
118d0825bca7fe65beaee391d30da42e937db621564Steve Block        access_log = os.path.join(self._output_dir, access_file_name)
119d0825bca7fe65beaee391d30da42e937db621564Steve Block        log_file_name = "error.log-" + time_str + ".txt"
120d0825bca7fe65beaee391d30da42e937db621564Steve Block        error_log = os.path.join(self._output_dir, log_file_name)
121d0825bca7fe65beaee391d30da42e937db621564Steve Block
122d0825bca7fe65beaee391d30da42e937db621564Steve Block        # Remove old log files. We only need to keep the last ones.
1238a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        self.remove_log_files(self._output_dir, "access.log-")
1248a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        self.remove_log_files(self._output_dir, "error.log-")
125d0825bca7fe65beaee391d30da42e937db621564Steve Block
126d0825bca7fe65beaee391d30da42e937db621564Steve Block        # Write out the config
12721939df44de1705786c545cd1bf519d47250322dBen Murdoch        with codecs.open(base_conf_file, "r", "utf-8") as file:
12821939df44de1705786c545cd1bf519d47250322dBen Murdoch            base_conf = file.read()
12921939df44de1705786c545cd1bf519d47250322dBen Murdoch
13021939df44de1705786c545cd1bf519d47250322dBen Murdoch        # FIXME: This should be re-worked so that this block can
13121939df44de1705786c545cd1bf519d47250322dBen Murdoch        # use with open() instead of a manual file.close() call.
13221939df44de1705786c545cd1bf519d47250322dBen Murdoch        # lighttpd.conf files seem to be UTF-8 without BOM:
13321939df44de1705786c545cd1bf519d47250322dBen Murdoch        # http://redmine.lighttpd.net/issues/992
13421939df44de1705786c545cd1bf519d47250322dBen Murdoch        f = codecs.open(out_conf_file, "w", "utf-8")
135d0825bca7fe65beaee391d30da42e937db621564Steve Block        f.write(base_conf)
136d0825bca7fe65beaee391d30da42e937db621564Steve Block
137d0825bca7fe65beaee391d30da42e937db621564Steve Block        # Write out our cgi handlers.  Run perl through env so that it
138d0825bca7fe65beaee391d30da42e937db621564Steve Block        # processes the #! line and runs perl with the proper command
139d0825bca7fe65beaee391d30da42e937db621564Steve Block        # line arguments. Emulate apache's mod_asis with a cat cgi handler.
140d0825bca7fe65beaee391d30da42e937db621564Steve Block        f.write(('cgi.assign = ( ".cgi"  => "/usr/bin/env",\n'
141d0825bca7fe65beaee391d30da42e937db621564Steve Block                 '               ".pl"   => "/usr/bin/env",\n'
142d0825bca7fe65beaee391d30da42e937db621564Steve Block                 '               ".asis" => "/bin/cat",\n'
143d0825bca7fe65beaee391d30da42e937db621564Steve Block                 '               ".php"  => "%s" )\n\n') %
1448a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block                                     self._port_obj._path_to_lighttpd_php())
145d0825bca7fe65beaee391d30da42e937db621564Steve Block
146d0825bca7fe65beaee391d30da42e937db621564Steve Block        # Setup log files
147d0825bca7fe65beaee391d30da42e937db621564Steve Block        f.write(('server.errorlog = "%s"\n'
148d0825bca7fe65beaee391d30da42e937db621564Steve Block                 'accesslog.filename = "%s"\n\n') % (error_log, access_log))
149d0825bca7fe65beaee391d30da42e937db621564Steve Block
150d0825bca7fe65beaee391d30da42e937db621564Steve Block        # Setup upload folders. Upload folder is to hold temporary upload files
151d0825bca7fe65beaee391d30da42e937db621564Steve Block        # and also POST data. This is used to support XHR layout tests that
152d0825bca7fe65beaee391d30da42e937db621564Steve Block        # does POST.
153d0825bca7fe65beaee391d30da42e937db621564Steve Block        f.write(('server.upload-dirs = ( "%s" )\n\n') % (self._output_dir))
154d0825bca7fe65beaee391d30da42e937db621564Steve Block
155d0825bca7fe65beaee391d30da42e937db621564Steve Block        # Setup a link to where the js test templates are stored
156d0825bca7fe65beaee391d30da42e937db621564Steve Block        f.write(('alias.url = ( "/js-test-resources" => "%s" )\n\n') %
157d0825bca7fe65beaee391d30da42e937db621564Steve Block                    (self._js_test_resource))
158d0825bca7fe65beaee391d30da42e937db621564Steve Block
15981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        # Setup a link to where the media resources are stored.
16081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        f.write(('alias.url += ( "/media-resources" => "%s" )\n\n') %
16181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch                    (self._media_resource))
16281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
163d0825bca7fe65beaee391d30da42e937db621564Steve Block        # dump out of virtual host config at the bottom.
164d0825bca7fe65beaee391d30da42e937db621564Steve Block        if self._root:
165d0825bca7fe65beaee391d30da42e937db621564Steve Block            if self._port:
166d0825bca7fe65beaee391d30da42e937db621564Steve Block                # Have both port and root dir.
167d0825bca7fe65beaee391d30da42e937db621564Steve Block                mappings = [{'port': self._port, 'docroot': self._root}]
168d0825bca7fe65beaee391d30da42e937db621564Steve Block            else:
169d0825bca7fe65beaee391d30da42e937db621564Steve Block                # Have only a root dir - set the ports as for LayoutTests.
170d0825bca7fe65beaee391d30da42e937db621564Steve Block                # This is used in ui_tests to run http tests against a browser.
171d0825bca7fe65beaee391d30da42e937db621564Steve Block
172d0825bca7fe65beaee391d30da42e937db621564Steve Block                # default set of ports as for LayoutTests but with a
173d0825bca7fe65beaee391d30da42e937db621564Steve Block                # specified root.
174d0825bca7fe65beaee391d30da42e937db621564Steve Block                mappings = [{'port': 8000, 'docroot': self._root},
175d0825bca7fe65beaee391d30da42e937db621564Steve Block                            {'port': 8080, 'docroot': self._root},
176d0825bca7fe65beaee391d30da42e937db621564Steve Block                            {'port': 8443, 'docroot': self._root,
1778a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block                             'sslcert': self._pem_file}]
178d0825bca7fe65beaee391d30da42e937db621564Steve Block        else:
179d0825bca7fe65beaee391d30da42e937db621564Steve Block            mappings = self.VIRTUALCONFIG
180d0825bca7fe65beaee391d30da42e937db621564Steve Block        for mapping in mappings:
181d0825bca7fe65beaee391d30da42e937db621564Steve Block            ssl_setup = ''
182d0825bca7fe65beaee391d30da42e937db621564Steve Block            if 'sslcert' in mapping:
183d0825bca7fe65beaee391d30da42e937db621564Steve Block                ssl_setup = ('  ssl.engine = "enable"\n'
184d0825bca7fe65beaee391d30da42e937db621564Steve Block                             '  ssl.pemfile = "%s"\n' % mapping['sslcert'])
185d0825bca7fe65beaee391d30da42e937db621564Steve Block
186d0825bca7fe65beaee391d30da42e937db621564Steve Block            f.write(('$SERVER["socket"] == "127.0.0.1:%d" {\n'
187d0825bca7fe65beaee391d30da42e937db621564Steve Block                     '  server.document-root = "%s"\n' +
188d0825bca7fe65beaee391d30da42e937db621564Steve Block                     ssl_setup +
189d0825bca7fe65beaee391d30da42e937db621564Steve Block                     '}\n\n') % (mapping['port'], mapping['docroot']))
190d0825bca7fe65beaee391d30da42e937db621564Steve Block        f.close()
191d0825bca7fe65beaee391d30da42e937db621564Steve Block
1928a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        executable = self._port_obj._path_to_lighttpd()
1938a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        module_path = self._port_obj._path_to_lighttpd_modules()
194d0825bca7fe65beaee391d30da42e937db621564Steve Block        start_cmd = [executable,
195d0825bca7fe65beaee391d30da42e937db621564Steve Block                     # Newly written config file
1968a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block                     '-f', os.path.join(self._output_dir, 'lighttpd.conf'),
197d0825bca7fe65beaee391d30da42e937db621564Steve Block                     # Where it can find its module dynamic libraries
198d0825bca7fe65beaee391d30da42e937db621564Steve Block                     '-m', module_path]
199d0825bca7fe65beaee391d30da42e937db621564Steve Block
200d0825bca7fe65beaee391d30da42e937db621564Steve Block        if not self._run_background:
201d0825bca7fe65beaee391d30da42e937db621564Steve Block            start_cmd.append(# Don't background
202d0825bca7fe65beaee391d30da42e937db621564Steve Block                             '-D')
203d0825bca7fe65beaee391d30da42e937db621564Steve Block
204d0825bca7fe65beaee391d30da42e937db621564Steve Block        # Copy liblightcomp.dylib to /tmp/lighttpd/lib to work around the
205d0825bca7fe65beaee391d30da42e937db621564Steve Block        # bug that mod_alias.so loads it from the hard coded path.
206d0825bca7fe65beaee391d30da42e937db621564Steve Block        if sys.platform == 'darwin':
207d0825bca7fe65beaee391d30da42e937db621564Steve Block            tmp_module_path = '/tmp/lighttpd/lib'
208d0825bca7fe65beaee391d30da42e937db621564Steve Block            if not os.path.exists(tmp_module_path):
209d0825bca7fe65beaee391d30da42e937db621564Steve Block                os.makedirs(tmp_module_path)
210d0825bca7fe65beaee391d30da42e937db621564Steve Block            lib_file = 'liblightcomp.dylib'
211d0825bca7fe65beaee391d30da42e937db621564Steve Block            shutil.copyfile(os.path.join(module_path, lib_file),
212d0825bca7fe65beaee391d30da42e937db621564Steve Block                            os.path.join(tmp_module_path, lib_file))
213d0825bca7fe65beaee391d30da42e937db621564Steve Block
2146c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen        env = self._port_obj.setup_environ_for_server()
21581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        _log.debug('Starting http server, cmd="%s"' % str(start_cmd))
21621939df44de1705786c545cd1bf519d47250322dBen Murdoch        # FIXME: Should use Executive.run_command
2172daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        self._process = subprocess.Popen(start_cmd, env=env, stdin=subprocess.PIPE)
218d0825bca7fe65beaee391d30da42e937db621564Steve Block
219d0825bca7fe65beaee391d30da42e937db621564Steve Block        # Wait for server to start.
220d0825bca7fe65beaee391d30da42e937db621564Steve Block        self.mappings = mappings
221d0825bca7fe65beaee391d30da42e937db621564Steve Block        server_started = self.wait_for_action(
222d0825bca7fe65beaee391d30da42e937db621564Steve Block            self.is_server_running_on_all_ports)
223d0825bca7fe65beaee391d30da42e937db621564Steve Block
224d0825bca7fe65beaee391d30da42e937db621564Steve Block        # Our process terminated already
225d0825bca7fe65beaee391d30da42e937db621564Steve Block        if not server_started or self._process.returncode != None:
226d0825bca7fe65beaee391d30da42e937db621564Steve Block            raise google.httpd_utils.HttpdNotStarted('Failed to start httpd.')
227d0825bca7fe65beaee391d30da42e937db621564Steve Block
228dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        _log.debug("Server successfully started")
229d0825bca7fe65beaee391d30da42e937db621564Steve Block
230d0825bca7fe65beaee391d30da42e937db621564Steve Block    # TODO(deanm): Find a nicer way to shutdown cleanly.  Our log files are
231d0825bca7fe65beaee391d30da42e937db621564Steve Block    # probably not being flushed, etc... why doesn't our python have os.kill ?
232d0825bca7fe65beaee391d30da42e937db621564Steve Block
233d0825bca7fe65beaee391d30da42e937db621564Steve Block    def stop(self, force=False):
234d0825bca7fe65beaee391d30da42e937db621564Steve Block        if not force and not self.is_running():
235d0825bca7fe65beaee391d30da42e937db621564Steve Block            return
236d0825bca7fe65beaee391d30da42e937db621564Steve Block
237d0825bca7fe65beaee391d30da42e937db621564Steve Block        httpd_pid = None
238d0825bca7fe65beaee391d30da42e937db621564Steve Block        if self._process:
239d0825bca7fe65beaee391d30da42e937db621564Steve Block            httpd_pid = self._process.pid
2408a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block        self._port_obj._shut_down_http_server(httpd_pid)
241d0825bca7fe65beaee391d30da42e937db621564Steve Block
242d0825bca7fe65beaee391d30da42e937db621564Steve Block        if self._process:
24321939df44de1705786c545cd1bf519d47250322dBen Murdoch            # wait() is not threadsafe and can throw OSError due to:
24421939df44de1705786c545cd1bf519d47250322dBen Murdoch            # http://bugs.python.org/issue1731717
245d0825bca7fe65beaee391d30da42e937db621564Steve Block            self._process.wait()
246d0825bca7fe65beaee391d30da42e937db621564Steve Block            self._process = None
247