summaryrefslogtreecommitdiffstats
path: root/tools/python/google/httpd_utils.py
blob: 0281228779916e9cd65114cd12b2e0a5637c46e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/bin/env python
# Copyright (c) 2006-2008 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 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 self._UrlIsAlive('http://127.0.0.1:%s/' % str(port)):
        raise HttpdNotStarted('Failed to start httpd on port %s' % str(port))

  def _UrlIsAlive(self, 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 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)

if '__main__' == __name__:
  # 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).')
  options, args = option_parser.parse_args()

  if not options.server:
    print ("Usage: %s -k {start|stop} [-r document_root] [--apache2]" %
           sys.argv[0])
    sys.exit(0)

  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)