1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import multiprocessing
6import os
7import SimpleHTTPServer
8
9
10class LocalHTTPServer(object):
11  """Class to start a local HTTP server as a child process."""
12
13  def __init__(self, serve_dir):
14    parent_conn, child_conn = multiprocessing.Pipe()
15    self.process = multiprocessing.Process(target=_HTTPServerProcess,
16                                           args=(child_conn, serve_dir))
17    self.process.start()
18    if parent_conn.poll(10):  # wait 10 seconds
19      self.port = parent_conn.recv()
20    else:
21      raise Exception('Unable to launch HTTP server.')
22
23    self.conn = parent_conn
24
25  def Shutdown(self):
26    """Send a message to the child HTTP server process and wait for it to
27        finish."""
28    self.conn.send(False)
29    self.process.join()
30
31  def GetURL(self, rel_url):
32    """Get the full url for a file on the local HTTP server.
33
34    Args:
35      rel_url: A URL fragment to convert to a full URL. For example,
36          GetURL('foobar.baz') -> 'http://localhost:1234/foobar.baz'
37    """
38    return 'http://localhost:%d/%s' % (self.port, rel_url)
39
40
41class QuietHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
42  def log_message(self, msg_format, *args):
43    pass
44
45
46def _HTTPServerProcess(conn, serve_dir):
47  """Run a local httpserver with a randomly-chosen port.
48
49  This function assumes it is run as a child process using multiprocessing.
50
51  Args:
52    conn: A connection to the parent process. The child process sends
53        the local port, and waits for a message from the parent to
54        stop serving.
55    serve_dir: The directory to serve. All files are accessible through
56       http://localhost:<port>/path/to/filename.
57  """
58  import BaseHTTPServer
59
60  os.chdir(serve_dir)
61  httpd = BaseHTTPServer.HTTPServer(('', 0), QuietHTTPRequestHandler)
62  conn.send(httpd.server_address[1])  # the chosen port number
63  httpd.timeout = 0.5  # seconds
64  running = True
65  while running:
66    httpd.handle_request()
67    if conn.poll():
68      running = conn.recv()
69  conn.close()
70