12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Copyright 2013 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)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import BaseHTTPServer
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import errno
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import json
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import re
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import socket
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import SocketServer
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import struct
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import warnings
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochimport tlslite.errors
18a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Ignore deprecation warnings, they make our output more cluttered.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)warnings.filterwarnings("ignore", category=DeprecationWarning)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if sys.platform == 'win32':
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import msvcrt
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Using debug() seems to cause hangs on XP: see http://crbug.com/64515.
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)debug_output = sys.stderr
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def debug(string):
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  debug_output.write(string + "\n")
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  debug_output.flush()
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Error(Exception):
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Error class for this module."""
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class OptionError(Error):
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Error for bad command line options."""
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class FileMultiplexer(object):
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, fd1, fd2) :
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.__fd1 = fd1
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.__fd2 = fd2
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __del__(self) :
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.__fd1 != sys.stdout and self.__fd1 != sys.stderr:
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.__fd1.close()
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.__fd2 != sys.stdout and self.__fd2 != sys.stderr:
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.__fd2.close()
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def write(self, text) :
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.__fd1.write(text)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.__fd2.write(text)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def flush(self) :
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.__fd1.flush()
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.__fd2.flush()
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class ClientRestrictingServerMixIn:
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Implements verify_request to limit connections to our configured IP
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  address."""
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def verify_request(self, _request, client_address):
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return client_address[0] == self.server_address[0]
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class BrokenPipeHandlerMixIn:
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Allows the server to deal with "broken pipe" errors (which happen if the
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  browser quits with outstanding requests, like for the favicon). This mix-in
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  requires the class to derive from SocketServer.BaseServer and not override its
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  handle_error() method. """
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def handle_error(self, request, client_address):
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    value = sys.exc_info()[1]
76a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    if isinstance(value, tlslite.errors.TLSClosedConnectionError):
77a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      print "testserver.py: Closed connection"
78a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      return
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if isinstance(value, socket.error):
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      err = value.args[0]
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if sys.platform in ('win32', 'cygwin'):
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # "An established connection was aborted by the software in your host."
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        pipe_err = 10053
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      else:
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        pipe_err = errno.EPIPE
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if err == pipe_err:
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        print "testserver.py: Broken pipe"
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return
89a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      if err == errno.ECONNRESET:
90a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch        print "testserver.py: Connection reset by peer"
91a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch        return
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SocketServer.BaseServer.handle_error(self, request, client_address)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class StoppableHTTPServer(BaseHTTPServer.HTTPServer):
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """This is a specialization of BaseHTTPServer to allow it
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  to be exited cleanly (by setting its "stop" member to True)."""
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def serve_forever(self):
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.stop = False
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.nonce_time = None
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    while not self.stop:
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.handle_request()
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.socket.close()
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def MultiplexerHack(std_fd, log_fd):
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Creates a FileMultiplexer that will write to both specified files.
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  When running on Windows XP bots, stdout and stderr will be invalid file
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  handles, so log_fd will be returned directly.  (This does not occur if you
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  run the test suite directly from a console, but only if the output of the
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  test executable is redirected.)
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if std_fd.fileno() <= 0:
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return log_fd
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return FileMultiplexer(std_fd, log_fd)
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class BasePageHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __init__(self, request, client_address, socket_server,
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               connect_handlers, get_handlers, head_handlers, post_handlers,
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               put_handlers):
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._connect_handlers = connect_handlers
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._get_handlers = get_handlers
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._head_handlers = head_handlers
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._post_handlers = post_handlers
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._put_handlers = put_handlers
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BaseHTTPServer.BaseHTTPRequestHandler.__init__(
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self, request, client_address, socket_server)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def log_request(self, *args, **kwargs):
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Disable request logging to declutter test log output.
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    pass
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _ShouldHandleRequest(self, handler_name):
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Determines if the path can be handled by the handler.
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    We consider a handler valid if the path begins with the
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    handler name. It can optionally be followed by "?*", "/*".
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    pattern = re.compile('%s($|\?|/).*' % handler_name)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return pattern.match(self.path)
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def do_CONNECT(self):
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for handler in self._connect_handlers:
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if handler():
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def do_GET(self):
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for handler in self._get_handlers:
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if handler():
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def do_HEAD(self):
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for handler in self._head_handlers:
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if handler():
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def do_POST(self):
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for handler in self._post_handlers:
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if handler():
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def do_PUT(self):
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for handler in self._put_handlers:
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if handler():
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class TestServerRunner(object):
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Runs a test server and communicates with the controlling C++ test code.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Subclasses should override the create_server method to create their server
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  object, and the add_options method to add their own options.
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser = optparse.OptionParser()
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.add_options()
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def main(self):
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.options, self.args = self.option_parser.parse_args()
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    logfile = open(self.options.log_file, 'w')
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sys.stderr = MultiplexerHack(sys.stderr, logfile)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.log_to_console:
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      sys.stdout = MultiplexerHack(sys.stdout, logfile)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sys.stdout = logfile
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    server_data = {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'host': self.options.host,
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server = self.create_server(server_data)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._notify_startup_complete(server_data)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.run_server()
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def create_server(self, server_data):
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Creates a server object and returns it.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Must populate server_data['port'], and can set additional server_data
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elements if desired."""
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError()
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def run_server(self):
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.server.serve_forever()
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except KeyboardInterrupt:
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'shutting down server'
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.server.stop = True
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def add_options(self):
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--startup-pipe', type='int',
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='startup_pipe',
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='File handle of pipe to parent process')
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--log-to-console', action='store_const',
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=True, default=False,
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='log_to_console',
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Enables or disables sys.stdout logging '
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'to the console.')
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.option_parser.add_option('--log-file', default='testserver.log',
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  dest='log_file',
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  help='The name of the server log file.')
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--port', default=0, type='int',
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Port used by the server. If '
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'unspecified, the server will listen on an '
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'ephemeral port.')
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--host', default='127.0.0.1',
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='host',
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Hostname or IP upon which the server '
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'will listen. Client connections will also '
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'only be allowed from this address.')
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.option_parser.add_option('--data-dir', dest='data_dir',
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  help='Directory from which to read the '
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  'files.')
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _notify_startup_complete(self, server_data):
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Notify the parent that we've started. (BaseServer subclasses
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # bind their sockets on construction.)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.startup_pipe is not None:
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data_json = json.dumps(server_data)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data_len = len(server_data_json)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'sending server_data: %s (%d bytes)' % (
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        server_data_json, server_data_len)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if sys.platform == 'win32':
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        fd = msvcrt.open_osfhandle(self.options.startup_pipe, 0)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        fd = self.options.startup_pipe
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      startup_pipe = os.fdopen(fd, "w")
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # First write the data length as an unsigned 4-byte value.  This
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # is _not_ using network byte ordering since the other end of the
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # pipe is on the same machine.
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      startup_pipe.write(struct.pack('=L', server_data_len))
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      startup_pipe.write(server_data_json)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      startup_pipe.close()
259