| #!/usr/bin/env python |
| # Copyright 2011 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """A class to help start/stop a local apache http server.""" |
| |
| from __future__ import print_function |
| |
| import logging |
| import optparse |
| import os |
| import subprocess |
| import sys |
| import time |
| import urllib |
| |
| import google.path_utils |
| import google.platform_utils |
| |
| class HttpdNotStarted(Exception): pass |
| |
| def UrlIsAlive(url): |
| """Checks to see if we get an http response from |url|. |
| We poll the url 5 times with a 1 second delay. If we don't |
| get a reply in that time, we give up and assume the httpd |
| didn't start properly. |
| |
| Args: |
| url: The URL to check. |
| Return: |
| True if the url is alive. |
| """ |
| wait_time = 5 |
| while wait_time > 0: |
| try: |
| response = urllib.urlopen(url) |
| # Server is up and responding. |
| return True |
| except IOError: |
| pass |
| wait_time -= 1 |
| # Wait a second and try again. |
| time.sleep(1) |
| |
| return False |
| |
| def ApacheConfigDir(start_dir): |
| """Returns a path to the directory holding the Apache config files.""" |
| return google.path_utils.FindUpward(start_dir, 'tools', 'python', |
| 'google', 'httpd_config') |
| |
| |
| def GetCygserverPath(start_dir, apache2=False): |
| """Returns the path to the directory holding cygserver.exe file.""" |
| cygserver_path = None |
| if apache2: |
| cygserver_path = google.path_utils.FindUpward(start_dir, 'third_party', |
| 'cygwin', 'usr', 'sbin') |
| return cygserver_path |
| |
| |
| def StartServer(document_root=None, output_dir=None, apache2=False): |
| """Starts a local server on port 8000 using the basic configuration files. |
| |
| Args: |
| document_root: If present, specifies the document root for the server; |
| otherwise, the filesystem's root (e.g., C:/ or /) will be used. |
| output_dir: If present, specifies where to put server logs; otherwise, |
| they'll be placed in the system's temp dir (e.g., $TEMP or /tmp). |
| apache2: boolean if true will cause this function to configure |
| for Apache 2.x as opposed to Apache 1.3.x |
| |
| Returns: the ApacheHttpd object that was created |
| """ |
| script_dir = google.path_utils.ScriptDir() |
| platform_util = google.platform_utils.PlatformUtility(script_dir) |
| if not output_dir: |
| output_dir = platform_util.GetTempDirectory() |
| if not document_root: |
| document_root = platform_util.GetFilesystemRoot() |
| apache_config_dir = ApacheConfigDir(script_dir) |
| if apache2: |
| httpd_conf_path = os.path.join(apache_config_dir, 'httpd2.conf') |
| else: |
| httpd_conf_path = os.path.join(apache_config_dir, 'httpd.conf') |
| mime_types_path = os.path.join(apache_config_dir, 'mime.types') |
| start_cmd = platform_util.GetStartHttpdCommand(output_dir, |
| httpd_conf_path, |
| mime_types_path, |
| document_root, |
| apache2=apache2) |
| stop_cmd = platform_util.GetStopHttpdCommand() |
| httpd = ApacheHttpd(start_cmd, stop_cmd, [8000], |
| cygserver_path=GetCygserverPath(script_dir, apache2)) |
| httpd.StartServer() |
| return httpd |
| |
| |
| def StopServers(apache2=False): |
| """Calls the platform's stop command on a newly created server, forcing it |
| to stop. |
| |
| The details depend on the behavior of the platform stop command. For example, |
| it's often implemented to kill all running httpd processes, as implied by |
| the name of this function. |
| |
| Args: |
| apache2: boolean if true will cause this function to configure |
| for Apache 2.x as opposed to Apache 1.3.x |
| """ |
| script_dir = google.path_utils.ScriptDir() |
| platform_util = google.platform_utils.PlatformUtility(script_dir) |
| httpd = ApacheHttpd('', platform_util.GetStopHttpdCommand(), [], |
| cygserver_path=GetCygserverPath(script_dir, apache2)) |
| httpd.StopServer(force=True) |
| |
| |
| class ApacheHttpd(object): |
| def __init__(self, start_command, stop_command, port_list, |
| cygserver_path=None): |
| """Args: |
| start_command: command list to call to start the httpd |
| stop_command: command list to call to stop the httpd if one has been |
| started. May kill all httpd processes running on the machine. |
| port_list: list of ports expected to respond on the local machine when |
| the server has been successfully started. |
| cygserver_path: Path to cygserver.exe. If specified, exe will be started |
| with server as well as stopped when server is stopped. |
| """ |
| self._http_server_proc = None |
| self._start_command = start_command |
| self._stop_command = stop_command |
| self._port_list = port_list |
| self._cygserver_path = cygserver_path |
| |
| def StartServer(self): |
| if self._http_server_proc: |
| return |
| if self._cygserver_path: |
| cygserver_exe = os.path.join(self._cygserver_path, "cygserver.exe") |
| cygbin = google.path_utils.FindUpward(cygserver_exe, 'third_party', |
| 'cygwin', 'bin') |
| env = os.environ |
| env['PATH'] += ";" + cygbin |
| subprocess.Popen(cygserver_exe, env=env) |
| logging.info('Starting http server') |
| self._http_server_proc = subprocess.Popen(self._start_command) |
| |
| # Ensure that the server is running on all the desired ports. |
| for port in self._port_list: |
| if not UrlIsAlive('http://127.0.0.1:%s/' % str(port)): |
| raise HttpdNotStarted('Failed to start httpd on port %s' % str(port)) |
| |
| def StopServer(self, force=False): |
| """If we started an httpd.exe process, or if force is True, call |
| self._stop_command (passed in on init so it can be platform-dependent). |
| This will presumably kill it, and may also kill any other httpd.exe |
| processes that are running. |
| """ |
| if force or self._http_server_proc: |
| logging.info('Stopping http server') |
| kill_proc = subprocess.Popen(self._stop_command, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| logging.info('%s\n%s' % (kill_proc.stdout.read(), |
| kill_proc.stderr.read())) |
| self._http_server_proc = None |
| if self._cygserver_path: |
| subprocess.Popen(["taskkill.exe", "/f", "/im", "cygserver.exe"], |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| |
| |
| def main(): |
| # Provide some command line params for starting/stopping the http server |
| # manually. |
| option_parser = optparse.OptionParser() |
| option_parser.add_option('-k', '--server', help='Server action (start|stop)') |
| option_parser.add_option('-r', '--root', help='Document root (optional)') |
| option_parser.add_option('-a', '--apache2', action='store_true', |
| default=False, help='Starts Apache 2 instead of Apache 1.3 (default). ' |
| 'Ignored on Mac (apache2 is used always)') |
| options, args = option_parser.parse_args() |
| |
| if not options.server: |
| print("Usage: %s -k {start|stop} [-r document_root] [--apache2]" % |
| sys.argv[0]) |
| return 1 |
| |
| document_root = None |
| if options.root: |
| document_root = options.root |
| |
| if 'start' == options.server: |
| StartServer(document_root, apache2=options.apache2) |
| else: |
| StopServers(apache2=options.apache2) |
| |
| |
| if '__main__' == __name__: |
| sys.exit(main()) |