15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import multiprocessing
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import SimpleHTTPServer
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LocalHTTPServer(object):
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Class to start a local HTTP server as a child process."""
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, serve_dir):
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parent_conn, child_conn = multiprocessing.Pipe()
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.process = multiprocessing.Process(target=_HTTPServerProcess,
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           args=(child_conn, serve_dir))
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.process.start()
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if parent_conn.poll(10):  # wait 10 seconds
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.port = parent_conn.recv()
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise Exception('Unable to launch HTTP server.')
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.conn = parent_conn
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Shutdown(self):
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Send a message to the child HTTP server process and wait for it to
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        finish."""
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.conn.send(False)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.process.join()
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetURL(self, rel_url):
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Get the full url for a file on the local HTTP server.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rel_url: A URL fragment to convert to a full URL. For example,
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetURL('foobar.baz') -> 'http://localhost:1234/foobar.baz'
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 'http://localhost:%d/%s' % (self.port, rel_url)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class QuietHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def log_message(self, msg_format, *args):
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pass
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _HTTPServerProcess(conn, serve_dir):
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Run a local httpserver with a randomly-chosen port.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This function assumes it is run as a child process using multiprocessing.
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    conn: A connection to the parent process. The child process sends
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        the local port, and waits for a message from the parent to
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        stop serving.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    serve_dir: The directory to serve. All files are accessible through
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       http://localhost:<port>/path/to/filename.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import BaseHTTPServer
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  os.chdir(serve_dir)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  httpd = BaseHTTPServer.HTTPServer(('', 0), QuietHTTPRequestHandler)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  conn.send(httpd.server_address[1])  # the chosen port number
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  httpd.timeout = 0.5  # seconds
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  running = True
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while running:
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    httpd.handle_request()
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if conn.poll():
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      running = conn.recv()
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  conn.close()
70