|  | #!/usr/bin/env python | 
|  | # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 
|  | # 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.""" | 
|  |  | 
|  | 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()) |