1ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch# Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import BaseHTTPServer
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import os
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import threading
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class Responder(object):
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Sends a HTTP response. Used with TestWebServer."""
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __init__(self, handler):
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._handler = handler
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def SendResponse(self, body):
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Sends OK response with body."""
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.SendHeaders(len(body))
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.SendBody(body)
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def SendResponseFromFile(self, path):
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Sends OK response with the given file as the body."""
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    with open(path, 'r') as f:
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.SendResponse(f.read())
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def SendHeaders(self, content_length=None):
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Sends headers for OK response."""
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._handler.send_response(200)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if content_length:
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._handler.send_header('Content-Length', content_length)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._handler.end_headers()
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def SendError(self, code):
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Sends response for the given HTTP error code."""
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._handler.send_error(code)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def SendBody(self, body):
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Just sends the body, no headers."""
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._handler.wfile.write(body)
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class Request(object):
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """An HTTP request."""
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __init__(self, handler):
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._handler = handler
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def GetPath(self):
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._handler.path
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  def GetHeader(self, name):
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return self._handler.headers.getheader(name)
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class _BaseServer(BaseHTTPServer.HTTPServer):
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Internal server that throws if timed out waiting for a request."""
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __init__(self, on_request, server_cert_and_key_path=None):
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Starts the server.
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    It is an HTTP server if parameter server_cert_and_key_path is not provided.
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Otherwise, it is an HTTPS server.
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Args:
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      server_cert_and_key_path: path to a PEM file containing the cert and key.
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                if it is None, start the server as an HTTP one.
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    class _Handler(BaseHTTPServer.BaseHTTPRequestHandler):
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      """Internal handler that just asks the server to handle the request."""
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      def do_GET(self):
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if self.path.endswith('favicon.ico'):
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          self.send_error(404)
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        on_request(Request(self), Responder(self))
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      def log_message(self, *args, **kwargs):
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        """Overriddes base class method to disable logging."""
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        pass
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BaseHTTPServer.HTTPServer.__init__(self, ('127.0.0.1', 0), _Handler)
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if server_cert_and_key_path is not None:
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._is_https_enabled = True
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._server.socket = ssl.wrap_socket(
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          self._server.socket, certfile=server_cert_and_key_path,
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          server_side=True)
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._is_https_enabled = False
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def handle_timeout(self):
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Overridden from SocketServer."""
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    raise RuntimeError('Timed out waiting for http request')
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def GetUrl(self):
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Returns the base URL of the server."""
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    postfix = '://127.0.0.1:%s' % self.server_port
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self._is_https_enabled:
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return 'https' + postfix
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 'http' + postfix
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class WebServer(object):
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """An HTTP or HTTPS server that serves on its own thread.
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Serves files from given directory but may use custom data for specific paths.
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __init__(self, root_dir, server_cert_and_key_path=None):
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Starts the server.
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    It is an HTTP server if parameter server_cert_and_key_path is not provided.
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Otherwise, it is an HTTPS server.
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Args:
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      root_dir: root path to serve files from. This parameter is required.
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      server_cert_and_key_path: path to a PEM file containing the cert and key.
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                if it is None, start the server as an HTTP one.
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._root_dir = os.path.abspath(root_dir)
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._server = _BaseServer(self._OnRequest, server_cert_and_key_path)
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._thread = threading.Thread(target=self._server.serve_forever)
123424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    self._thread.daemon = True
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._thread.start()
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._path_data_map = {}
126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    self._path_callback_map = {}
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    self._path_maps_lock = threading.Lock()
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _OnRequest(self, request, responder):
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    path = request.GetPath().split('?')[0]
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    # Serve from path -> callback and data maps.
133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    self._path_maps_lock.acquire()
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    try:
135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      if path in self._path_callback_map:
136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        body = self._path_callback_map[path](request)
137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        if body:
138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          responder.SendResponse(body)
139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        else:
140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          responder.SendError(503)
141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        return
142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if path in self._path_data_map:
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        responder.SendResponse(self._path_data_map[path])
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    finally:
147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      self._path_maps_lock.release()
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Serve from file.
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    path = os.path.normpath(
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        os.path.join(self._root_dir, *path.split('/')))
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not path.startswith(self._root_dir):
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      responder.SendError(403)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not os.path.exists(path):
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      responder.SendError(404)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    responder.SendResponseFromFile(path)
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def SetDataForPath(self, path, data):
161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    self._path_maps_lock.acquire()
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    try:
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._path_data_map[path] = data
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    finally:
165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      self._path_maps_lock.release()
166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  def SetCallbackForPath(self, path, func):
168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    self._path_maps_lock.acquire()
169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    try:
170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      self._path_callback_map[path] = func
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    finally:
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      self._path_maps_lock.release()
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def GetUrl(self):
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Returns the base URL of the server."""
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._server.GetUrl()
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def Shutdown(self):
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Shuts down the server synchronously."""
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._server.shutdown()
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._thread.join()
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class SyncWebServer(object):
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """WebServer for testing.
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Incoming requests are blocked until explicitly handled.
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  This was designed for single thread use. All requests should be handled on
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  the same thread.
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __init__(self):
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._server = _BaseServer(self._OnRequest)
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Recognized by SocketServer.
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._server.timeout = 10
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._on_request = None
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _OnRequest(self, request, responder):
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._on_request(responder)
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._on_request = None
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def Respond(self, on_request):
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Blocks until request comes in, then calls given handler function.
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Args:
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      on_request: Function that handles the request. Invoked with single
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          parameter, an instance of Responder.
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self._on_request:
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      raise RuntimeError('Must handle 1 request at a time.')
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._on_request = on_request
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    while self._on_request:
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Don't use handle_one_request, because it won't work with the timeout.
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._server.handle_request()
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def RespondWithContent(self, content):
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Blocks until request comes in, then handles it with the given content."""
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    def SendContent(responder):
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      responder.SendResponse(content)
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.Respond(SendContent)
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def GetUrl(self):
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._server.GetUrl()
226