201 lines
7.3 KiB
Python
201 lines
7.3 KiB
Python
|
#!/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())
|