15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved. 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)"""A class to help start/stop a local apache http server.""" 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import urllib 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import google.path_utils 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import google.platform_utils 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class HttpdNotStarted(Exception): pass 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def UrlIsAlive(url): 2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) """Checks to see if we get an http response from |url|. 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) We poll the url 5 times with a 1 second delay. If we don't 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) get a reply in that time, we give up and assume the httpd 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) didn't start properly. 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) url: The URL to check. 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Return: 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) True if the url is alive. 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """ 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wait_time = 5 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while wait_time > 0: 34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) try: 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = urllib.urlopen(url) 36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # Server is up and responding. 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return True 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except IOError: 39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) pass 4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) wait_time -= 1 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Wait a second and try again. 4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) time.sleep(1) 431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return False 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ApacheConfigDir(start_dir): 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Returns a path to the directory holding the Apache config files.""" 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return google.path_utils.FindUpward(start_dir, 'tools', 'python', 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'google', 'httpd_config') 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 52010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)def GetCygserverPath(start_dir, apache2=False): 53010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) """Returns the path to the directory holding cygserver.exe file.""" 54010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) cygserver_path = None 55010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if apache2: 56010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) cygserver_path = google.path_utils.FindUpward(start_dir, 'third_party', 57010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 'cygwin', 'usr', 'sbin') 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cygserver_path 591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 61868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)def StartServer(document_root=None, output_dir=None, apache2=False): 62868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) """Starts a local server on port 8000 using the basic configuration files. 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document_root: If present, specifies the document root for the server; 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) otherwise, the filesystem's root (e.g., C:/ or /) will be used. 67010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) output_dir: If present, specifies where to put server logs; otherwise, 68010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) they'll be placed in the system's temp dir (e.g., $TEMP or /tmp). 69010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) apache2: boolean if true will cause this function to configure 70010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) for Apache 2.x as opposed to Apache 1.3.x 71010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 72010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) Returns: the ApacheHttpd object that was created 73010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) """ 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) script_dir = google.path_utils.ScriptDir() 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) platform_util = google.platform_utils.PlatformUtility(script_dir) 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if not output_dir: 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) output_dir = platform_util.GetTempDirectory() 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not document_root: 791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci document_root = platform_util.GetFilesystemRoot() 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) apache_config_dir = ApacheConfigDir(script_dir) 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if apache2: 822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) httpd_conf_path = os.path.join(apache_config_dir, 'httpd2.conf') 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) httpd_conf_path = os.path.join(apache_config_dir, 'httpd.conf') 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mime_types_path = os.path.join(apache_config_dir, 'mime.types') 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) start_cmd = platform_util.GetStartHttpdCommand(output_dir, 87a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch httpd_conf_path, 88a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch mime_types_path, 89a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch document_root, 90a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch apache2=apache2) 91a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch stop_cmd = platform_util.GetStopHttpdCommand() 92a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch httpd = ApacheHttpd(start_cmd, stop_cmd, [8000], 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cygserver_path=GetCygserverPath(script_dir, apache2)) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) httpd.StartServer() 95a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch return httpd 96a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 97a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 98a3f7b4e666c476898878fa745f637129375cd889Ben Murdochdef StopServers(apache2=False): 99a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch """Calls the platform's stop command on a newly created server, forcing it 100a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch to stop. 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The details depend on the behavior of the platform stop command. For example, 103a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch it's often implemented to kill all running httpd processes, as implied by 104a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch the name of this function. 1051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 10658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) Args: 107a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch apache2: boolean if true will cause this function to configure 10858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) for Apache 2.x as opposed to Apache 1.3.x 109a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch """ 11058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) script_dir = google.path_utils.ScriptDir() 111a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch platform_util = google.platform_utils.PlatformUtility(script_dir) 11290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) httpd = ApacheHttpd('', platform_util.GetStopHttpdCommand(), [], 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cygserver_path=GetCygserverPath(script_dir, apache2)) 114a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch httpd.StopServer(force=True) 11558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 11658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 11758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)class ApacheHttpd(object): 11858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) def __init__(self, start_command, stop_command, port_list, 11958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) cygserver_path=None): 12058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) """Args: 121a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch start_command: command list to call to start the httpd 122a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch stop_command: command list to call to stop the httpd if one has been 123a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch started. May kill all httpd processes running on the machine. 124010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) port_list: list of ports expected to respond on the local machine when 125010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) the server has been successfully started. 126010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) cygserver_path: Path to cygserver.exe. If specified, exe will be started 127010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) with server as well as stopped when server is stopped. 128010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) """ 129010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) self._http_server_proc = None 130010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) self._start_command = start_command 131010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) self._stop_command = stop_command 132a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch self._port_list = port_list 133a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch self._cygserver_path = cygserver_path 134a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 135a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch def StartServer(self): 136a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if self._http_server_proc: 137a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch return 138a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if self._cygserver_path: 139a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch cygserver_exe = os.path.join(self._cygserver_path, "cygserver.exe") 140a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch cygbin = google.path_utils.FindUpward(cygserver_exe, 'third_party', 141e4256316f8b5e8d1ec0df1f7762771622a53fa63Ben Murdoch 'cygwin', 'bin') 142a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch env = os.environ 143a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch env['PATH'] += ";" + cygbin 144e4256316f8b5e8d1ec0df1f7762771622a53fa63Ben Murdoch subprocess.Popen(cygserver_exe, env=env) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.info('Starting http server') 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._http_server_proc = subprocess.Popen(self._start_command) 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 148010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) # Ensure that the server is running on all the desired ports. 149010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) for port in self._port_list: 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if not UrlIsAlive('http://127.0.0.1:%s/' % str(port)): 151010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) raise HttpdNotStarted('Failed to start httpd on port %s' % str(port)) 152010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 153010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) def StopServer(self, force=False): 1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) """If we started an httpd.exe process, or if force is True, call 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._stop_command (passed in on init so it can be platform-dependent). 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) This will presumably kill it, and may also kill any other httpd.exe 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) processes that are running. 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if force or self._http_server_proc: 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.info('Stopping http server') 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) kill_proc = subprocess.Popen(self._stop_command, 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stdout=subprocess.PIPE, 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stderr=subprocess.PIPE) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.info('%s\n%s' % (kill_proc.stdout.read(), 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) kill_proc.stderr.read())) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._http_server_proc = None 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self._cygserver_path: 168eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch subprocess.Popen(["taskkill.exe", "/f", "/im", "cygserver.exe"], 169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch stdout=subprocess.PIPE, 170eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch stderr=subprocess.PIPE) 171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 172eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 1734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)def main(): 174eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch # Provide some command line params for starting/stopping the http server 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # manually. 176eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch option_parser = optparse.OptionParser() 177eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch option_parser.add_option('-k', '--server', help='Server action (start|stop)') 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) option_parser.add_option('-r', '--root', help='Document root (optional)') 179eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch option_parser.add_option('-a', '--apache2', action='store_true', 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) default=False, help='Starts Apache 2 instead of Apache 1.3 (default). ' 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Ignored on Mac (apache2 is used always)') 1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) options, args = option_parser.parse_args() 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 184eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if not options.server: 1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci print ("Usage: %s -k {start|stop} [-r document_root] [--apache2]" % 1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci sys.argv[0]) 187eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return 1 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document_root = None 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if options.root: 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document_root = options.root 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if 'start' == options.server: 1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) StartServer(document_root, apache2=options.apache2) 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StopServers(apache2=options.apache2) 19758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochif '__main__' == __name__: 200010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) sys.exit(main()) 2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)