1c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#!/usr/bin/python2.4
272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen# Copyright (c) 2011 The Chromium Authors. All rights reserved.
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott# Use of this source code is governed by a BSD-style license that can be
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott# found in the LICENSE file.
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott"""This is a simple HTTP server used for testing Chrome.
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottIt supports several test URLs, as specified by the handlers in TestPageHandler.
94a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochBy default, it listens on an ephemeral port and sends the port number back to
104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochthe originating process over a pipe. The originating process can specify an
114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochexplicit port if necessary.
12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottIt can use https if you specify the flag --https=CERT where CERT is the path
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottto a pem file containing the certificate and private key that should be used.
14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott"""
15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
16201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochimport asyncore
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport base64
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport BaseHTTPServer
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport cgi
20201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochimport errno
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport optparse
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport os
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport re
24201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochimport select
25201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochimport simplejson
26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport SocketServer
27201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochimport socket
28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport sys
294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochimport struct
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport time
31513209b27ff55e2841eac0e4120199c23acce758Ben Murdochimport urlparse
323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickimport warnings
333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick# Ignore deprecation warnings, they make our output more cluttered.
353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickwarnings.filterwarnings("ignore", category=DeprecationWarning)
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochimport pyftpdlib.ftpserver
38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport tlslite
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottimport tlslite.api
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttry:
42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  import hashlib
43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  _new_md5 = hashlib.md5
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottexcept ImportError:
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  import md5
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  _new_md5 = md5.new
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickif sys.platform == 'win32':
493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  import msvcrt
503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSERVER_HTTP = 0
52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSERVER_FTP = 1
534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochSERVER_SYNC = 2
54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
55201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch# Using debug() seems to cause hangs on XP: see http://crbug.com/64515 .
56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottdebug_output = sys.stderr
57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottdef debug(str):
58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  debug_output.write(str + "\n")
59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  debug_output.flush()
60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottclass StoppableHTTPServer(BaseHTTPServer.HTTPServer):
62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  """This is a specialization of of BaseHTTPServer to allow it
63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  to be exited cleanly (by setting its "stop" member to True)."""
64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def serve_forever(self):
66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.stop = False
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    self.nonce_time = None
68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    while not self.stop:
69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.handle_request()
70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.socket.close()
71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottclass HTTPSServer(tlslite.api.TLSSocketServerMixIn, StoppableHTTPServer):
73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  """This is a specialization of StoppableHTTPerver that add https support."""
74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  def __init__(self, server_address, request_hander_class, cert_path,
76513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch               ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers):
77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    s = open(cert_path).read()
78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    x509 = tlslite.api.X509()
79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    x509.parse(s)
80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.cert_chain = tlslite.api.X509CertChain([x509])
81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    s = open(cert_path).read()
82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.private_key = tlslite.api.parsePEMKey(s, private=True)
833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    self.ssl_client_auth = ssl_client_auth
843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    self.ssl_client_cas = []
853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    for ca_file in ssl_client_cas:
863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        s = open(ca_file).read()
873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        x509 = tlslite.api.X509()
883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        x509.parse(s)
893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        self.ssl_client_cas.append(x509.subject)
90513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    self.ssl_handshake_settings = tlslite.api.HandshakeSettings()
91513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    if ssl_bulk_ciphers is not None:
92513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers
93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.session_cache = tlslite.api.SessionCache()
95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    StoppableHTTPServer.__init__(self, server_address, request_hander_class)
96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def handshake(self, tlsConnection):
98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """Creates the SSL connection."""
99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    try:
100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      tlsConnection.handshakeServer(certChain=self.cert_chain,
101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                    privateKey=self.private_key,
1023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    sessionCache=self.session_cache,
1033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    reqCert=self.ssl_client_auth,
104513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                    settings=self.ssl_handshake_settings,
1053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    reqCAs=self.ssl_client_cas)
106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      tlsConnection.ignoreAbruptClose = True
107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return True
1083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    except tlslite.api.TLSAbruptCloseError:
1093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      # Ignore abrupt close.
1103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return True
111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    except tlslite.api.TLSError, error:
112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      print "Handshake failure:", str(error)
113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass SyncHTTPServer(StoppableHTTPServer):
1174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  """An HTTP server that handles sync commands."""
1184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def __init__(self, server_address, request_handler_class):
1204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    # We import here to avoid pulling in chromiumsync's dependencies
1214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    # unless strictly necessary.
1224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    import chromiumsync
123201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    import xmppserver
1244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    StoppableHTTPServer.__init__(self, server_address, request_handler_class)
125201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    self._sync_handler = chromiumsync.TestServer()
126201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    self._xmpp_socket_map = {}
127201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    self._xmpp_server = xmppserver.XmppServer(
128201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      self._xmpp_socket_map, ('localhost', 0))
129201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    self.xmpp_port = self._xmpp_server.getsockname()[1]
1304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def HandleCommand(self, query, raw_request):
1324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return self._sync_handler.HandleCommand(query, raw_request)
1334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
134201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  def HandleRequestNoBlock(self):
135201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    """Handles a single request.
136201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
137201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    Copied from SocketServer._handle_request_noblock().
138201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    """
139201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    try:
140201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      request, client_address = self.get_request()
141201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    except socket.error:
142201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      return
143201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    if self.verify_request(request, client_address):
144201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      try:
145201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        self.process_request(request, client_address)
146201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      except:
147201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        self.handle_error(request, client_address)
148201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        self.close_request(request)
149201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
150201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  def serve_forever(self):
151201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    """This is a merge of asyncore.loop() and SocketServer.serve_forever().
152201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    """
153201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
15421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    def HandleXmppSocket(fd, socket_map, handler):
15521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      """Runs the handler for the xmpp connection for fd.
156201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
157201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      Adapted from asyncore.read() et al.
158201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      """
15921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      xmpp_connection = socket_map.get(fd)
16021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      # This could happen if a previous handler call caused fd to get
16121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      # removed from socket_map.
16221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      if xmpp_connection is None:
16321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        return
164201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      try:
16521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        handler(xmpp_connection)
166201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      except (asyncore.ExitNow, KeyboardInterrupt, SystemExit):
167201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        raise
168201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      except:
16921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        xmpp_connection.handle_error()
170201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
171201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    while True:
172201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      read_fds = [ self.fileno() ]
173201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      write_fds = []
174201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      exceptional_fds = []
175201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
176201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      for fd, xmpp_connection in self._xmpp_socket_map.items():
177201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        is_r = xmpp_connection.readable()
178201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        is_w = xmpp_connection.writable()
179201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        if is_r:
180201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          read_fds.append(fd)
181201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        if is_w:
182201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          write_fds.append(fd)
183201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        if is_r or is_w:
184201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          exceptional_fds.append(fd)
185201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
186201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      try:
187201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        read_fds, write_fds, exceptional_fds = (
188201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          select.select(read_fds, write_fds, exceptional_fds))
189201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      except select.error, err:
190201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        if err.args[0] != errno.EINTR:
191201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          raise
192201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        else:
193201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          continue
194201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
195201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      for fd in read_fds:
196201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        if fd == self.fileno():
197201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          self.HandleRequestNoBlock()
198201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch          continue
19921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        HandleXmppSocket(fd, self._xmpp_socket_map,
20021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                         asyncore.dispatcher.handle_read_event)
201201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
202201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      for fd in write_fds:
20321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        HandleXmppSocket(fd, self._xmpp_socket_map,
20421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                         asyncore.dispatcher.handle_write_event)
205201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
206201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      for fd in exceptional_fds:
20721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        HandleXmppSocket(fd, self._xmpp_socket_map,
20821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                         asyncore.dispatcher.handle_expt_event)
209201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
2104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass BasePageHandler(BaseHTTPServer.BaseHTTPRequestHandler):
2124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def __init__(self, request, client_address, socket_server,
2144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch               connect_handlers, get_handlers, post_handlers, put_handlers):
2154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self._connect_handlers = connect_handlers
2164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self._get_handlers = get_handlers
2174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self._post_handlers = post_handlers
2184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self._put_handlers = put_handlers
2194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    BaseHTTPServer.BaseHTTPRequestHandler.__init__(
2204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      self, request, client_address, socket_server)
2214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def log_request(self, *args, **kwargs):
2234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    # Disable request logging to declutter test log output.
2244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    pass
2254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def _ShouldHandleRequest(self, handler_name):
2274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    """Determines if the path can be handled by the handler.
2284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    We consider a handler valid if the path begins with the
2304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    handler name. It can optionally be followed by "?*", "/*".
2314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    """
2324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    pattern = re.compile('%s($|\?|/).*' % handler_name)
2344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return pattern.match(self.path)
2354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def do_CONNECT(self):
2374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    for handler in self._connect_handlers:
2384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if handler():
2394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        return
2404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def do_GET(self):
2424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    for handler in self._get_handlers:
2434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if handler():
2444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        return
2454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def do_POST(self):
2474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    for handler in self._post_handlers:
2484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if handler():
2494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        return
2504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def do_PUT(self):
2524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    for handler in self._put_handlers:
2534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if handler():
2544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        return
2554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass TestPageHandler(BasePageHandler):
258c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
259c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def __init__(self, request, client_address, socket_server):
2604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    connect_handlers = [
261c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.RedirectConnectHandler,
262c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.ServerAuthConnectHandler,
263c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.DefaultConnectResponseHandler]
2644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    get_handlers = [
265c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.NoCacheMaxAgeTimeHandler,
266c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.NoCacheTimeHandler,
267c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CacheTimeHandler,
268c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CacheExpiresHandler,
269c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CacheProxyRevalidateHandler,
270c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CachePrivateHandler,
271c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CachePublicHandler,
272c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CacheSMaxAgeHandler,
273c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CacheMustRevalidateHandler,
274c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CacheMustRevalidateMaxAgeHandler,
275c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CacheNoStoreHandler,
276c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CacheNoStoreMaxAgeHandler,
277c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.CacheNoTransformHandler,
278c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.DownloadHandler,
279c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.DownloadFinishHandler,
280c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.EchoHeader,
281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      self.EchoHeaderCache,
282c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.EchoAllHandler,
283c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.FileHandler,
284c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.SetCookieHandler,
285c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.AuthBasicHandler,
286c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.AuthDigestHandler,
287c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.SlowServerHandler,
288c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.ContentTypeHandler,
289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      self.NoContentHandler,
290c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.ServerRedirectHandler,
291c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.ClientRedirectHandler,
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      self.MultipartHandler,
293c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.DefaultResponseHandler]
2944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    post_handlers = [
295c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.EchoTitleHandler,
296c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.EchoAllHandler,
2974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      self.EchoHandler,
2984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      self.DeviceManagementHandler] + get_handlers
2994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    put_handlers = [
300c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.EchoTitleHandler,
301c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.EchoAllHandler,
3024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      self.EchoHandler] + get_handlers
303c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
304c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self._mime_types = {
3053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      'crx' : 'application/x-chrome-extension',
30621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      'exe' : 'application/octet-stream',
307c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      'gif': 'image/gif',
308c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      'jpeg' : 'image/jpeg',
309c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      'jpg' : 'image/jpeg',
31021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      'pdf' : 'application/pdf',
31121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      'xml' : 'text/xml'
312c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
313c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self._default_mime_type = 'text/html'
314c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
3154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    BasePageHandler.__init__(self, request, client_address, socket_server,
3164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                             connect_handlers, get_handlers, post_handlers,
3174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                             put_handlers)
318c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
319c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def GetMIMETypeFromName(self, file_name):
320c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """Returns the mime type for the specified file_name. So far it only looks
321c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    at the file extension."""
322c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
3233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    (shortname, extension) = os.path.splitext(file_name.split("?")[0])
324c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if len(extension) == 0:
325c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # no extension.
326c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return self._default_mime_type
327c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
328c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # extension starts with a dot, so we need to remove it
329c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return self._mime_types.get(extension[1:], self._default_mime_type)
330c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
331c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def NoCacheMaxAgeTimeHandler(self):
332c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
333c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and no caching requested."""
334c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
335c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/nocachetime/maxage"):
336c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
337c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
338c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
339c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'max-age=0')
340c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
341c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
342c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
343c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
344c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
345c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
346c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
347c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
348c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def NoCacheTimeHandler(self):
349c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
350c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and no caching requested."""
351c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
352c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/nocachetime"):
353c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
354c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
355c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
356c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'no-cache')
357c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
358c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
359c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
360c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
361c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
362c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
363c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
364c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
365c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CacheTimeHandler(self):
366c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
367c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and allows caching for one minute."""
368c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
369c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cachetime"):
370c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
371c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
372c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
373c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'max-age=60')
374c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
375c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
376c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
377c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
378c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
379c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
380c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
381c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
382c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CacheExpiresHandler(self):
383c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
384c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and set the page to expire on 1 Jan 2099."""
385c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
386c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/expires"):
387c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
388c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
389c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
390c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Expires', 'Thu, 1 Jan 2099 00:00:00 GMT')
391c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
392c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
393c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
394c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
395c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
396c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
397c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
398c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
399c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CacheProxyRevalidateHandler(self):
400c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
401c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and allows caching for 60 seconds"""
402c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
403c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/proxy-revalidate"):
404c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
405c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
406c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
407c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
408c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'max-age=60, proxy-revalidate')
409c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
410c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
411c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
412c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
413c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
414c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
415c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
416c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CachePrivateHandler(self):
417c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
418c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and allows caching for 5 seconds."""
419c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
420c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/private"):
421c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
422c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
423c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
424c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
425c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'max-age=3, private')
426c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
427c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
428c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
429c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
430c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
431c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
432c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
433c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CachePublicHandler(self):
434c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
435c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and allows caching for 5 seconds."""
436c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
437c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/public"):
438c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
439c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
440c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
441c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
442c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'max-age=3, public')
443c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
444c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
445c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
446c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
447c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
448c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
449c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
450c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CacheSMaxAgeHandler(self):
451c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
452c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and does not allow for caching."""
453c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
454c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/s-maxage"):
455c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
456c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
457c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
458c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
459c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'public, s-maxage = 60, max-age = 0')
460c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
461c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
462c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
463c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
464c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
465c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
466c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
467c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CacheMustRevalidateHandler(self):
468c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
469c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and does not allow caching."""
470c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
471c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/must-revalidate"):
472c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
473c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
474c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
475c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
476c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'must-revalidate')
477c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
478c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
479c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
480c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
481c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
482c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
483c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
484c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CacheMustRevalidateMaxAgeHandler(self):
485c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
486c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and does not allow caching event though max-age of 60
487c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    seconds is specified."""
488c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
489c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/must-revalidate/max-age"):
490c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
491c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
492c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
493c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
494c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'max-age=60, must-revalidate')
495c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
496c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
497c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
498c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
499c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
500c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
501c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
502c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CacheNoStoreHandler(self):
503c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
504c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and does not allow the page to be stored."""
505c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
506c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/no-store"):
507c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
508c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
509c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
510c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
511c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'no-store')
512c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
513c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
514c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
515c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
516c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
517c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
518c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
519c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CacheNoStoreMaxAgeHandler(self):
520c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
521c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and does not allow the page to be stored even though max-age
522c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    of 60 seconds is specified."""
523c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
524c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/no-store/max-age"):
525c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
526c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
527c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
528c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
529c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'max-age=60, no-store')
530c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
531c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
532c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
533c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
534c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
535c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
536c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
537c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
538c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def CacheNoTransformHandler(self):
539c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This request handler yields a page with the title set to the current
540c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    system time, and does not allow the content to transformed during
541c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    user-agent caching"""
542c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
543c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/cache/no-transform"):
544c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
545c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
546c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
547c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
548c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'no-transform')
549c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
550c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
551c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>%s</title></head></html>' %
552c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     time.time())
553c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
554c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
555c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
556c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def EchoHeader(self):
557c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This handler echoes back the value of a specific request header."""
558c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return self.EchoHeaderHelper("/echoheader")
559c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
560ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    """This function echoes back the value of a specific request header"""
561ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    """while allowing caching for 16 hours."""
562ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  def EchoHeaderCache(self):
563ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return self.EchoHeaderHelper("/echoheadercache")
564c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
565c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def EchoHeaderHelper(self, echo_header):
566c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This function echoes back the value of the request header passed in."""
567c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest(echo_header):
568c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
569c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
570c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    query_char = self.path.find('?')
571c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if query_char != -1:
572c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      header_name = self.path[query_char+1:]
573c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
574c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
575c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/plain')
576ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if echo_header == '/echoheadercache':
577ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      self.send_header('Cache-control', 'max-age=60000')
578ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    else:
579ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      self.send_header('Cache-control', 'no-cache')
580c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # insert a vary header to properly indicate that the cachability of this
581c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # request is subject to value of the request header being echoed.
582c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if len(header_name) > 0:
583c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('Vary', header_name)
584c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
585c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
586c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if len(header_name) > 0:
587c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write(self.headers.getheader(header_name))
588c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
589c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
590c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
59172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  def ReadRequestBody(self):
59272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    """This function reads the body of the current HTTP request, handling
59372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    both plain and chunked transfer encoded requests."""
59472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
59572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if self.headers.getheader('transfer-encoding') != 'chunked':
59672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      length = int(self.headers.getheader('content-length'))
59772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return self.rfile.read(length)
59872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
59972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    # Read the request body as chunks.
60072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    body = ""
60172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    while True:
60272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      line = self.rfile.readline()
60372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      length = int(line, 16)
60472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if length == 0:
60572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        self.rfile.readline()
60672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        break
60772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      body += self.rfile.read(length)
60872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      self.rfile.read(2)
60972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return body
61072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
611c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def EchoHandler(self):
612c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This handler just echoes back the payload of the request, for testing
613c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    form submission."""
614c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
615c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/echo"):
616c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
617c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
618c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
619c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
620c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
62172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    self.wfile.write(self.ReadRequestBody())
622c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
623c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
624c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def EchoTitleHandler(self):
625c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This handler is like Echo, but sets the page title to the request."""
626c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
627c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/echotitle"):
628c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
629c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
630c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
631c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
632c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
63372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    request = self.ReadRequestBody()
634c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><title>')
635c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write(request)
636c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('</title></head></html>')
637c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
638c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
639c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def EchoAllHandler(self):
640c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This handler yields a (more) human-readable page listing information
641c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    about the request header & contents."""
642c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
643c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/echoall"):
644c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
645c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
646c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
647c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
648c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
649c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head><style>'
650c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      'pre { border: 1px solid black; margin: 5px; padding: 5px }'
651c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      '</style></head><body>'
652c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      '<div style="float: right">'
6534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      '<a href="/echo">back to referring page</a></div>'
654c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      '<h1>Request Body:</h1><pre>')
655c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
656c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if self.command == 'POST' or self.command == 'PUT':
65772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      qs = self.ReadRequestBody()
658c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      params = cgi.parse_qs(qs, keep_blank_values=1)
659c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
660c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      for param in params:
661c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        self.wfile.write('%s=%s\n' % (param, params[param][0]))
662c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
663c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('</pre>')
664c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
665c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<h1>Request Headers:</h1><pre>%s</pre>' % self.headers)
666c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
667c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('</body></html>')
668c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
669c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
670c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def DownloadHandler(self):
671c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This handler sends a downloadable file with or without reporting
672c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    the size (6K)."""
673c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
674c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if self.path.startswith("/download-unknown-size"):
675c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      send_length = False
676c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    elif self.path.startswith("/download-known-size"):
677c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      send_length = True
678c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    else:
679c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
680c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
681c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    #
682c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # The test which uses this functionality is attempting to send
683c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # small chunks of data to the client.  Use a fairly large buffer
684c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # so that we'll fill chrome's IO buffer enough to force it to
685c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # actually write the data.
686c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # See also the comments in the client-side of this test in
687c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # download_uitest.cc
688c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    #
689c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_chunk1 = 35*1024
690c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_chunk2 = 10*1024
691c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
692c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
693c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'application/octet-stream')
694c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'max-age=0')
695c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if send_length:
696c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('Content-Length', size_chunk1 + size_chunk2)
697c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
698c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
699c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # First chunk of data:
700c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write("*" * size_chunk1)
701c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.flush()
702c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
703c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # handle requests until one of them clears this flag.
704c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.server.waitForDownload = True
705c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    while self.server.waitForDownload:
706c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.server.handle_request()
707c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
708c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Second chunk of data:
709c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write("*" * size_chunk2)
710c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
711c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
712c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def DownloadFinishHandler(self):
713c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This handler just tells the server to finish the current download."""
714c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
715c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/download-finish"):
716c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
717c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
718c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.server.waitForDownload = False
719c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
720c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
721c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Cache-Control', 'max-age=0')
722c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
723c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
724c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
725513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  def _ReplaceFileData(self, data, query_parameters):
726513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    """Replaces matching substrings in a file.
727513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
7284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    If the 'replace_text' URL query parameter is present, it is expected to be
7294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    of the form old_text:new_text, which indicates that any old_text strings in
7304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    the file are replaced with new_text. Multiple 'replace_text' parameters may
7314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    be specified.
732513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
733513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    If the parameters are not present, |data| is returned.
734513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    """
735513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    query_dict = cgi.parse_qs(query_parameters)
7364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    replace_text_values = query_dict.get('replace_text', [])
7374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    for replace_text_value in replace_text_values:
7384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      replace_text_args = replace_text_value.split(':')
7394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if len(replace_text_args) != 2:
7404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        raise ValueError(
7414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          'replace_text must be of form old_text:new_text. Actual value: %s' %
7424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          replace_text_value)
7434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      old_text_b64, new_text_b64 = replace_text_args
7444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      old_text = base64.urlsafe_b64decode(old_text_b64)
7454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      new_text = base64.urlsafe_b64decode(new_text_b64)
7464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      data = data.replace(old_text, new_text)
7474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return data
748513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
749c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def FileHandler(self):
750c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This handler sends the contents of the requested file.  Wow, it's like
751c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    a real webserver!"""
752c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
753c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    prefix = self.server.file_root_url
754c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self.path.startswith(prefix):
755c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
756c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
757c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Consume a request body if present.
758c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if self.command == 'POST' or self.command == 'PUT' :
75972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      self.ReadRequestBody()
760c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
761513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
762513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    sub_path = url_path[len(prefix):]
763513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    entries = sub_path.split('/')
764513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    file_path = os.path.join(self.server.data_dir, *entries)
765513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    if os.path.isdir(file_path):
766513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      file_path = os.path.join(file_path, 'index.html')
767513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
768513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    if not os.path.isfile(file_path):
769513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      print "File not found " + sub_path + " full path:" + file_path
770c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_error(404)
771c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return True
772c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
773513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    f = open(file_path, "rb")
774c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    data = f.read()
775c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    f.close()
776c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
777513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    data = self._ReplaceFileData(data, query)
778513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
779c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # If file.mock-http-headers exists, it contains the headers we
780c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # should send.  Read them in and parse them.
781513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    headers_path = file_path + '.mock-http-headers'
782c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if os.path.isfile(headers_path):
783c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      f = open(headers_path, "r")
784c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
785c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # "HTTP/1.1 200 OK"
786c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      response = f.readline()
787c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      status_code = re.findall('HTTP/\d+.\d+ (\d+)', response)[0]
788c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_response(int(status_code))
789c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
790c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      for line in f:
791c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        header_values = re.findall('(\S+):\s*(.*)', line)
792c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        if len(header_values) > 0:
793c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          # "name: value"
794c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          name, value = header_values[0]
795c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          self.send_header(name, value)
796c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      f.close()
797c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    else:
798c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # Could be more generic once we support mime-type sniffing, but for
799c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # now we need to set it explicitly.
8004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
8014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      range = self.headers.get('Range')
8024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if range and range.startswith('bytes='):
8034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        # Note this doesn't handle all valid byte range values (i.e. open ended
8044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        # ones), just enough for what we needed so far.
8054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        range = range[6:].split('-')
8064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        start = int(range[0])
8074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        end = int(range[1])
8084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
8094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        self.send_response(206)
8104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        content_range = 'bytes ' + str(start) + '-' + str(end) + '/' + \
8114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                        str(len(data))
8124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        self.send_header('Content-Range', content_range)
8134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        data = data[start: end + 1]
8144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      else:
8154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        self.send_response(200)
8164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
817513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      self.send_header('Content-type', self.GetMIMETypeFromName(file_path))
8184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      self.send_header('Accept-Ranges', 'bytes')
819c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('Content-Length', len(data))
8204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      self.send_header('ETag', '\'' + file_path + '\'')
821c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
822c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
823c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write(data)
824c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
825c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
826c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
827c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def SetCookieHandler(self):
828c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This handler just sets a cookie, for testing cookie handling."""
829c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
830c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/set-cookie"):
831c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
832c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
833c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    query_char = self.path.find('?')
834c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if query_char != -1:
835c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      cookie_values = self.path[query_char + 1:].split('&')
836c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    else:
837c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      cookie_values = ("",)
838c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
839c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
840c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    for cookie_value in cookie_values:
841c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('Set-Cookie', '%s' % cookie_value)
842c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
843c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    for cookie_value in cookie_values:
844c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('%s' % cookie_value)
845c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
846c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
847c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def AuthBasicHandler(self):
848c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This handler tests 'Basic' authentication.  It just sends a page with
849c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    title 'user/pass' if you succeed."""
850c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
851c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/auth-basic"):
852c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
853c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
854c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    username = userpass = password = b64str = ""
8553f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    expected_password = 'secret'
8563f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    realm = 'testrealm'
8573f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    set_cookie_if_challenged = False
858c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
8593f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
8603f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    query_params = cgi.parse_qs(query, True)
8613f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    if 'set-cookie-if-challenged' in query_params:
8623f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      set_cookie_if_challenged = True
8633f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    if 'password' in query_params:
8643f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      expected_password = query_params['password'][0]
8653f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    if 'realm' in query_params:
8663f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      realm = query_params['realm'][0]
867c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
868c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    auth = self.headers.getheader('authorization')
869c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    try:
870c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if not auth:
871c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        raise Exception('no auth')
872c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      b64str = re.findall(r'Basic (\S+)', auth)[0]
873c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      userpass = base64.b64decode(b64str)
874c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      username, password = re.findall(r'([^:]+):(\S+)', userpass)[0]
8753f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      if password != expected_password:
876c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        raise Exception('wrong password')
877c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    except Exception, e:
878c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # Authentication failed.
879c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_response(401)
8803f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      self.send_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
881c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('Content-type', 'text/html')
882c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if set_cookie_if_challenged:
883c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        self.send_header('Set-Cookie', 'got_challenged=true')
884c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.end_headers()
885c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('<html><head>')
886c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('<title>Denied: %s</title>' % e)
887c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('</head><body>')
888c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('auth=%s<p>' % auth)
889c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('b64str=%s<p>' % b64str)
890c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('username: %s<p>' % username)
891c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('userpass: %s<p>' % userpass)
892c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('password: %s<p>' % password)
893c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('You sent:<br>%s<p>' % self.headers)
894c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('</body></html>')
895c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return True
896c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
897c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Authentication successful.  (Return a cachable response to allow for
898c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # testing cached pages that require authentication.)
899c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if_none_match = self.headers.getheader('if-none-match')
900c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if if_none_match == "abc":
901c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_response(304)
902c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.end_headers()
9033f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    elif url_path.endswith(".gif"):
9043f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      # Using chrome/test/data/google/logo.gif as the test image
9053f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      test_image_path = ['google', 'logo.gif']
9063f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      gif_path = os.path.join(self.server.data_dir, *test_image_path)
9073f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      if not os.path.isfile(gif_path):
9083f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen        self.send_error(404)
9093f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen        return True
9103f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
9113f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      f = open(gif_path, "rb")
9123f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      data = f.read()
9133f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      f.close()
9143f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
9153f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      self.send_response(200)
9163f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      self.send_header('Content-type', 'image/gif')
9173f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      self.send_header('Cache-control', 'max-age=60000')
9183f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      self.send_header('Etag', 'abc')
9193f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      self.end_headers()
9203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      self.wfile.write(data)
921c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    else:
922c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_response(200)
923c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('Content-type', 'text/html')
924c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('Cache-control', 'max-age=60000')
925c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('Etag', 'abc')
926c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.end_headers()
927c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('<html><head>')
928c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('<title>%s/%s</title>' % (username, password))
929c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('</head><body>')
930c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('auth=%s<p>' % auth)
931c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('You sent:<br>%s<p>' % self.headers)
932c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('</body></html>')
933c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
934c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
935c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
936c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  def GetNonce(self, force_reset=False):
937c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   """Returns a nonce that's stable per request path for the server's lifetime.
938c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
939c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   This is a fake implementation. A real implementation would only use a given
940c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   nonce a single time (hence the name n-once). However, for the purposes of
941c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   unittesting, we don't care about the security of the nonce.
942c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
943c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   Args:
944c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     force_reset: Iff set, the nonce will be changed. Useful for testing the
945c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         "stale" response.
946c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   """
947c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   if force_reset or not self.server.nonce_time:
948c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     self.server.nonce_time = time.time()
949c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   return _new_md5('privatekey%s%d' %
950c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   (self.path, self.server.nonce_time)).hexdigest()
951c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
952c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def AuthDigestHandler(self):
953c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    """This handler tests 'Digest' authentication.
954c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
955c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    It just sends a page with title 'user/pass' if you succeed.
956c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
957c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    A stale response is sent iff "stale" is present in the request path.
958c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    """
959c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/auth-digest"):
960c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
961c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
962c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stale = 'stale' in self.path
963c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    nonce = self.GetNonce(force_reset=stale)
964c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    opaque = _new_md5('opaque').hexdigest()
965c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    password = 'secret'
966c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    realm = 'testrealm'
967c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
968c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    auth = self.headers.getheader('authorization')
969c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    pairs = {}
970c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    try:
971c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if not auth:
972c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        raise Exception('no auth')
973c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if not auth.startswith('Digest'):
974c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        raise Exception('not digest')
975c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # Pull out all the name="value" pairs as a dictionary.
976c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      pairs = dict(re.findall(r'(\b[^ ,=]+)="?([^",]+)"?', auth))
977c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
978c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # Make sure it's all valid.
979c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if pairs['nonce'] != nonce:
980c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        raise Exception('wrong nonce')
981c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if pairs['opaque'] != opaque:
982c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        raise Exception('wrong opaque')
983c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
984c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # Check the 'response' value and make sure it matches our magic hash.
985c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # See http://www.ietf.org/rfc/rfc2617.txt
986c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      hash_a1 = _new_md5(
987c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          ':'.join([pairs['username'], realm, password])).hexdigest()
988c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      hash_a2 = _new_md5(':'.join([self.command, pairs['uri']])).hexdigest()
989c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if 'qop' in pairs and 'nc' in pairs and 'cnonce' in pairs:
990c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        response = _new_md5(':'.join([hash_a1, nonce, pairs['nc'],
991c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott            pairs['cnonce'], pairs['qop'], hash_a2])).hexdigest()
992c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      else:
993c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        response = _new_md5(':'.join([hash_a1, nonce, hash_a2])).hexdigest()
994c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
995c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if pairs['response'] != response:
996c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        raise Exception('wrong password')
997c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    except Exception, e:
998c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # Authentication failed.
999c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_response(401)
1000c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      hdr = ('Digest '
1001c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott             'realm="%s", '
1002c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott             'domain="/", '
1003c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott             'qop="auth", '
1004c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott             'algorithm=MD5, '
1005c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott             'nonce="%s", '
1006c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott             'opaque="%s"') % (realm, nonce, opaque)
1007c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if stale:
1008c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        hdr += ', stale="TRUE"'
1009c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('WWW-Authenticate', hdr)
1010c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.send_header('Content-type', 'text/html')
1011c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.end_headers()
1012c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('<html><head>')
1013c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('<title>Denied: %s</title>' % e)
1014c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('</head><body>')
1015c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('auth=%s<p>' % auth)
1016c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('pairs=%s<p>' % pairs)
1017c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('You sent:<br>%s<p>' % self.headers)
1018c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('We are replying:<br>%s<p>' % hdr)
1019c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.wfile.write('</body></html>')
1020c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return True
1021c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1022c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Authentication successful.
1023c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
1024c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
1025c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1026c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head>')
1027c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<title>%s/%s</title>' % (pairs['username'], password))
1028c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('</head><body>')
1029c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('auth=%s<p>' % auth)
1030c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('pairs=%s<p>' % pairs)
1031c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('</body></html>')
1032c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1033c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
1034c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1035c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def SlowServerHandler(self):
1036c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """Wait for the user suggested time before responding. The syntax is
1037c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    /slow?0.5 to wait for half a second."""
1038c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/slow"):
1039c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
1040c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    query_char = self.path.find('?')
1041c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    wait_sec = 1.0
1042c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if query_char >= 0:
1043c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      try:
1044c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        wait_sec = int(self.path[query_char + 1:])
1045c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      except ValueError:
1046c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        pass
1047c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    time.sleep(wait_sec)
1048c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
1049c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/plain')
1050c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1051c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write("waited %d seconds" % wait_sec)
1052c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
1053c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1054c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def ContentTypeHandler(self):
1055c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """Returns a string of html with the given content type.  E.g.,
1056c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    /contenttype?text/css returns an html file with the Content-Type
1057c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    header set to text/css."""
1058c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest("/contenttype"):
1059c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
1060c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    query_char = self.path.find('?')
1061c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    content_type = self.path[query_char + 1:].strip()
1062c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not content_type:
1063c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      content_type = 'text/html'
1064c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
1065c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-Type', content_type)
1066c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1067c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write("<html>\n<body>\n<p>HTML text</p>\n</body>\n</html>\n");
1068c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
1069c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1070ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  def NoContentHandler(self):
1071ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    """Returns a 204 No Content response."""
1072ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if not self._ShouldHandleRequest("/nocontent"):
1073ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return False
1074ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    self.send_response(204)
1075ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    self.end_headers()
1076ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return True
1077ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
1078c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def ServerRedirectHandler(self):
1079c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """Sends a server redirect to the given URL. The syntax is
1080c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    '/server-redirect?http://foo.bar/asdf' to redirect to
1081c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    'http://foo.bar/asdf'"""
1082c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1083c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    test_name = "/server-redirect"
1084c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest(test_name):
1085c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
1086c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1087c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    query_char = self.path.find('?')
1088c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if query_char < 0 or len(self.path) <= query_char + 1:
1089c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.sendRedirectHelp(test_name)
1090c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return True
1091c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    dest = self.path[query_char + 1:]
1092c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1093c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(301)  # moved permanently
1094c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Location', dest)
1095c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
1096c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1097c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head>')
1098c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
1099c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
1101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1102c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def ClientRedirectHandler(self):
1103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """Sends a client redirect to the given URL. The syntax is
1104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    '/client-redirect?http://foo.bar/asdf' to redirect to
1105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    'http://foo.bar/asdf'"""
1106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    test_name = "/client-redirect"
1108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not self._ShouldHandleRequest(test_name):
1109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
1110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    query_char = self.path.find('?');
1112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if query_char < 0 or len(self.path) <= query_char + 1:
1113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      self.sendRedirectHelp(test_name)
1114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return True
1115c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    dest = self.path[query_char + 1:]
1116c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
1118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
1119c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><head>')
1121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<meta http-equiv="refresh" content="0;url=%s">' % dest)
1122c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
1123c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1124c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
1125c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  def MultipartHandler(self):
1127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    """Send a multipart response (10 text/html pages)."""
1128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    test_name = "/multipart"
1129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if not self._ShouldHandleRequest(test_name):
1130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return False
1131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    num_frames = 10
1133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bound = '12345'
1134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    self.send_response(200)
1135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    self.send_header('Content-type',
1136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                     'multipart/x-mixed-replace;boundary=' + bound)
1137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    self.end_headers()
1138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for i in xrange(num_frames):
1140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      self.wfile.write('--' + bound + '\r\n')
1141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      self.wfile.write('Content-type: text/html\r\n\r\n')
1142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      self.wfile.write('<title>page ' + str(i) + '</title>')
1143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      self.wfile.write('page ' + str(i))
1144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    self.wfile.write('--' + bound + '--')
1146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return True
1147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def DefaultResponseHandler(self):
1149c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This is the catch-all response handler for requests that aren't handled
1150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    by one of the special handlers above.
1151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    Note that we specify the content-length as without it the https connection
1152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    is not closed properly (and the browser keeps expecting data)."""
1153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    contents = "Default response given for path: " + self.path
1155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
1156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
1157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header("Content-Length", len(contents))
1158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write(contents)
1160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
1161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def RedirectConnectHandler(self):
1163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """Sends a redirect to the CONNECT request for www.redirect.com. This
1164c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    response is not specified by the RFC, so the browser should not follow
1165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    the redirect."""
1166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1167c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (self.path.find("www.redirect.com") < 0):
1168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
1169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1170c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    dest = "http://www.destination.com/foo.js"
1171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1172c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(302)  # moved temporarily
1173c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Location', dest)
1174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Connection', 'close')
1175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1176c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
1177c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def ServerAuthConnectHandler(self):
1179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """Sends a 401 to the CONNECT request for www.server-auth.com. This
1180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    response doesn't make sense because the proxy server cannot request
1181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    server authentication."""
1182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (self.path.find("www.server-auth.com") < 0):
1184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return False
1185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    challenge = 'Basic realm="WallyWorld"'
1187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(401)  # unauthorized
1189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('WWW-Authenticate', challenge)
1190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Connection', 'close')
1191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1192c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
1193c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def DefaultConnectResponseHandler(self):
1195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    """This is the catch-all response handler for CONNECT requests that aren't
1196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    handled by one of the special handlers above.  Real Web servers respond
1197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    with 400 to CONNECT requests."""
1198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    contents = "Your client has issued a malformed or illegal request."
1200c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(400)  # bad request
1201c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
1202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header("Content-Length", len(contents))
1203c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1204c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write(contents)
1205c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return True
1206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
12074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def DeviceManagementHandler(self):
12084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    """Delegates to the device management service used for cloud policy."""
12094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if not self._ShouldHandleRequest("/device_management"):
12104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      return False
1211c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
121272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    raw_request = self.ReadRequestBody()
1213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
12144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if not self.server._device_management_handler:
12154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      import device_management
12164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      policy_path = os.path.join(self.server.data_dir, 'device_management')
12174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      self.server._device_management_handler = (
121872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          device_management.TestServer(policy_path,
1219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                       self.server.policy_keys,
1220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                       self.server.policy_user))
1221c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
12224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    http_response, raw_reply = (
12234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        self.server._device_management_handler.HandleRequest(self.path,
12244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                                             self.headers,
12254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                                             raw_request))
12264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self.send_response(http_response)
12274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self.end_headers()
12284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self.wfile.write(raw_reply)
12294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return True
1230c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1231c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  # called by the redirect handling function when there is no parameter
1232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  def sendRedirectHelp(self, redirect_name):
1233c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_response(200)
1234c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.send_header('Content-type', 'text/html')
1235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.end_headers()
1236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('<html><body><h1>Error: no redirect destination</h1>')
1237c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('Use <pre>%s?http://dest...</pre>' % redirect_name)
1238c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    self.wfile.write('</body></html>')
1239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
12404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass SyncPageHandler(BasePageHandler):
12424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  """Handler for the main HTTP sync server."""
12434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def __init__(self, request, client_address, sync_http_server):
12454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    get_handlers = [self.ChromiumSyncTimeHandler]
12464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    post_handlers = [self.ChromiumSyncCommandHandler]
12474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    BasePageHandler.__init__(self, request, client_address,
12484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                             sync_http_server, [], get_handlers,
12494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                             post_handlers, [])
12504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def ChromiumSyncTimeHandler(self):
12524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    """Handle Chromium sync .../time requests.
12534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    The syncer sometimes checks server reachability by examining /time.
12554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    """
12564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    test_name = "/chromiumsync/time"
12574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if not self._ShouldHandleRequest(test_name):
12584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      return False
12594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self.send_response(200)
12614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self.send_header('Content-type', 'text/html')
12624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self.end_headers()
12634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return True
12644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  def ChromiumSyncCommandHandler(self):
12664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    """Handle a chromiumsync command arriving via http.
12674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    This covers all sync protocol commands: authentication, getupdates, and
12694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    commit.
12704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    """
12714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    test_name = "/chromiumsync/command"
12724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if not self._ShouldHandleRequest(test_name):
12734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      return False
12744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    length = int(self.headers.getheader('content-length'))
12764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    raw_request = self.rfile.read(length)
12774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    http_response, raw_reply = self.server.HandleCommand(
12794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        self.path, raw_request)
12804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self.send_response(http_response)
12814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self.end_headers()
12824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    self.wfile.write(raw_reply)
12834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return True
12844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
12854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1286c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottdef MakeDataDir():
1287c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if options.data_dir:
1288c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if not os.path.isdir(options.data_dir):
1289c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      print 'specified data dir not found: ' + options.data_dir + ' exiting...'
1290c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return None
1291c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    my_data_dir = options.data_dir
1292c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  else:
1293c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Create the default path to our data dir, relative to the exe dir.
1294c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    my_data_dir = os.path.dirname(sys.argv[0])
1295c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    my_data_dir = os.path.join(my_data_dir, "..", "..", "..", "..",
1296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               "test", "data")
1297c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1298c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    #TODO(ibrar): Must use Find* funtion defined in google\tools
1299c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    #i.e my_data_dir = FindUpward(my_data_dir, "test", "data")
1300c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1301c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return my_data_dir
1302c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
13033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickclass FileMultiplexer:
13043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  def __init__(self, fd1, fd2) :
13053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    self.__fd1 = fd1
13063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    self.__fd2 = fd2
13073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
13083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  def __del__(self) :
13093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if self.__fd1 != sys.stdout and self.__fd1 != sys.stderr:
13103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      self.__fd1.close()
13113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if self.__fd2 != sys.stdout and self.__fd2 != sys.stderr:
13123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      self.__fd2.close()
13133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
13143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  def write(self, text) :
13153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    self.__fd1.write(text)
13163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    self.__fd2.write(text)
13173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
13183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  def flush(self) :
13193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    self.__fd1.flush()
13203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    self.__fd2.flush()
1321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1322c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottdef main(options, args):
1323c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  logfile = open('testserver.log', 'w')
13243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  sys.stderr = FileMultiplexer(sys.stderr, logfile)
132572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if options.log_to_console:
132672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    sys.stdout = FileMultiplexer(sys.stdout, logfile)
132772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  else:
132872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    sys.stdout = logfile
1329c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1330c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  port = options.port
1331c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1332201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  server_data = {}
1333201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
1334c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if options.server_type == SERVER_HTTP:
1335c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if options.cert:
1336c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      # let's make sure the cert file exists.
1337c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if not os.path.isfile(options.cert):
13383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        print 'specified server cert file not found: ' + options.cert + \
13393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick              ' exiting...'
1340c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        return
13413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      for ca_cert in options.ssl_client_ca:
13423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        if not os.path.isfile(ca_cert):
13433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          print 'specified trusted client CA file not found: ' + ca_cert + \
13443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                ' exiting...'
13453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          return
13463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      server = HTTPSServer(('127.0.0.1', port), TestPageHandler, options.cert,
1347513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           options.ssl_client_auth, options.ssl_client_ca,
1348513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           options.ssl_bulk_cipher)
13494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      print 'HTTPS server started on port %d...' % server.server_port
1350c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    else:
13513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      server = StoppableHTTPServer(('127.0.0.1', port), TestPageHandler)
13524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      print 'HTTP server started on port %d...' % server.server_port
1353c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1354c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    server.data_dir = MakeDataDir()
1355c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    server.file_root_url = options.file_root_url
1356201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    server_data['port'] = server.server_port
13574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    server._device_management_handler = None
1358ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    server.policy_keys = options.policy_keys
1359ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    server.policy_user = options.policy_user
13604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  elif options.server_type == SERVER_SYNC:
13614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    server = SyncHTTPServer(('127.0.0.1', port), SyncPageHandler)
13624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    print 'Sync HTTP server started on port %d...' % server.server_port
1363201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    print 'Sync XMPP server started on port %d...' % server.xmpp_port
1364201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    server_data['port'] = server.server_port
1365201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    server_data['xmpp_port'] = server.xmpp_port
1366c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  # means FTP Server
1367c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  else:
1368c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    my_data_dir = MakeDataDir()
1369c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1370c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Instantiate a dummy authorizer for managing 'virtual' users
1371c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    authorizer = pyftpdlib.ftpserver.DummyAuthorizer()
1372c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1373c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Define a new user having full r/w permissions and a read-only
1374c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # anonymous user
1375c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw')
1376c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1377c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    authorizer.add_anonymous(my_data_dir)
1378c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1379c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Instantiate FTP handler class
1380c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ftp_handler = pyftpdlib.ftpserver.FTPHandler
1381c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ftp_handler.authorizer = authorizer
1382c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1383c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Define a customized banner (string returned when client connects)
1384c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ftp_handler.banner = ("pyftpdlib %s based ftpd ready." %
1385c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                          pyftpdlib.ftpserver.__ver__)
1386c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1387c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    # Instantiate FTP server class and listen to 127.0.0.1:port
1388c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    address = ('127.0.0.1', port)
1389c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    server = pyftpdlib.ftpserver.FTPServer(address, ftp_handler)
1390201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    server_data['port'] = server.socket.getsockname()[1]
1391201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    print 'FTP server started on port %d...' % server_data['port']
1392c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
13933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  # Notify the parent that we've started. (BaseServer subclasses
13943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  # bind their sockets on construction.)
13953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if options.startup_pipe is not None:
1396201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    server_data_json = simplejson.dumps(server_data)
1397201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    server_data_len = len(server_data_json)
1398201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    print 'sending server_data: %s (%d bytes)' % (
1399201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      server_data_json, server_data_len)
14003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if sys.platform == 'win32':
14013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      fd = msvcrt.open_osfhandle(options.startup_pipe, 0)
14023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    else:
14033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      fd = options.startup_pipe
14043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    startup_pipe = os.fdopen(fd, "w")
1405201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    # First write the data length as an unsigned 4-byte value.  This
1406201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    # is _not_ using network byte ordering since the other end of the
1407201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    # pipe is on the same machine.
1408201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    startup_pipe.write(struct.pack('=L', server_data_len))
1409201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    startup_pipe.write(server_data_json)
14103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    startup_pipe.close()
14113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1412c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  try:
1413c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    server.serve_forever()
1414c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  except KeyboardInterrupt:
1415c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    print 'shutting down server'
1416c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    server.stop = True
1417c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1418c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottif __name__ == '__main__':
1419c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  option_parser = optparse.OptionParser()
1420c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  option_parser.add_option("-f", '--ftp', action='store_const',
1421c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           const=SERVER_FTP, default=SERVER_HTTP,
1422c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           dest='server_type',
14234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                           help='start up an FTP server.')
14244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  option_parser.add_option('', '--sync', action='store_const',
14254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                           const=SERVER_SYNC, default=SERVER_HTTP,
14264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                           dest='server_type',
14274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                           help='start up a sync server.')
142872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  option_parser.add_option('', '--log-to-console', action='store_const',
142972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                           const=True, default=False,
143072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                           dest='log_to_console',
143172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                           help='Enables or disables sys.stdout logging to '
143272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                           'the console.')
14334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  option_parser.add_option('', '--port', default='0', type='int',
14344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                           help='Port used by the server. If unspecified, the '
14354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                           'server will listen on an ephemeral port.')
1436c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  option_parser.add_option('', '--data-dir', dest='data_dir',
1437c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           help='Directory from which to read the files.')
1438c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  option_parser.add_option('', '--https', dest='cert',
1439c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           help='Specify that https should be used, specify '
1440c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           'the path to the cert containing the private key '
1441c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           'the server should use.')
14423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  option_parser.add_option('', '--ssl-client-auth', action='store_true',
14433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                           help='Require SSL client auth on every connection.')
14443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  option_parser.add_option('', '--ssl-client-ca', action='append', default=[],
14453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                           help='Specify that the client certificate request '
1446513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'should include the CA named in the subject of '
1447513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'the DER-encoded certificate contained in the '
1448513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'specified file. This option may appear multiple '
1449513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'times, indicating multiple CA names should be '
1450513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'sent in the request.')
1451513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  option_parser.add_option('', '--ssl-bulk-cipher', action='append',
1452513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           help='Specify the bulk encryption algorithm(s)'
1453513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'that will be accepted by the SSL server. Valid '
1454513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'values are "aes256", "aes128", "3des", "rc4". If '
1455513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'omitted, all algorithms will be used. This '
1456513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'option may appear multiple times, indicating '
1457513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                           'multiple algorithms should be enabled.');
1458c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  option_parser.add_option('', '--file-root-url', default='/files/',
1459c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           help='Specify a root URL for files served.')
14603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  option_parser.add_option('', '--startup-pipe', type='int',
14613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                           dest='startup_pipe',
14623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                           help='File handle of pipe to parent process')
1463ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  option_parser.add_option('', '--policy-key', action='append',
1464ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           dest='policy_keys',
1465ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           help='Specify a path to a PEM-encoded private key '
1466ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           'to use for policy signing. May be specified '
1467ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           'multiple times in order to load multipe keys into '
1468ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           'the server. If ther server has multiple keys, it '
1469ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           'will rotate through them in at each request a '
1470ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           'round-robin fashion. The server will generate a '
1471ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           'random key if none is specified on the command '
1472ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           'line.')
1473ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  option_parser.add_option('', '--policy-user', default='user@example.com',
1474ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           dest='policy_user',
1475ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           help='Specify the user name the server should '
1476ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           'report back to the client as the user owning the '
1477ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           'token used for making the policy request.')
1478c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  options, args = option_parser.parse_args()
1479c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1480c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  sys.exit(main(options, args))
1481