testserver.py revision f2477e01787aa58f445919b809d89e252beef54f
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)"""This is a simple HTTP/FTP/TCP/UDP/BASIC_AUTH_PROXY/WEBSOCKET server used for
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)testing Chrome.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)It supports several test URLs, as specified by the handlers in TestPageHandler.
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)By default, it listens on an ephemeral port and sends the port number back to
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)the originating process over a pipe. The originating process can specify an
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)explicit port if necessary.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)It can use https if you specify the flag --https=CERT where CERT is the path
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)to a pem file containing the certificate and private key that should be used.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import base64
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import BaseHTTPServer
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import cgi
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import hashlib
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import minica
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import json
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import random
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import select
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import socket
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import SocketServer
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import struct
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import threading
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import urllib
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import urlparse
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import zlib
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import echo_message
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import pyftpdlib.ftpserver
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import testserver_base
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import tlslite
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import tlslite.api
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BASE_DIR = os.path.dirname(os.path.abspath(__file__))
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)sys.path.insert(
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    0, os.path.join(BASE_DIR, '..', '..', '..', 'third_party/pywebsocket/src'))
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from mod_pywebsocket.standalone import WebSocketServer
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SERVER_HTTP = 0
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SERVER_FTP = 1
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SERVER_TCP_ECHO = 2
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SERVER_UDP_ECHO = 3
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SERVER_BASIC_AUTH_PROXY = 4
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SERVER_WEBSOCKET = 5
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Default request queue size for WebSocketServer.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)_DEFAULT_REQUEST_QUEUE_SIZE = 128
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class WebSocketOptions:
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Holds options for WebSocketServer."""
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, host, port, data_dir):
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.request_queue_size = _DEFAULT_REQUEST_QUEUE_SIZE
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_host = host
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.port = port
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.websock_handlers = data_dir
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.scan_dir = None
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.allow_handlers_outside_root_dir = False
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.websock_handlers_map_file = None
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.cgi_directories = []
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.is_executable_method = None
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.allow_draft75 = False
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.strict = True
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.use_tls = False
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.private_key = None
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.certificate = None
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.tls_client_auth = False
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.tls_client_ca = None
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.use_basic_auth = False
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class RecordingSSLSessionCache(object):
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """RecordingSSLSessionCache acts as a TLS session cache and maintains a log of
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  lookups and inserts in order to test session cache behaviours."""
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.log = []
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __getitem__(self, sessionID):
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.log.append(('lookup', sessionID))
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise KeyError()
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __setitem__(self, sessionID, session):
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.log.append(('insert', sessionID))
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class HTTPServer(testserver_base.ClientRestrictingServerMixIn,
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 testserver_base.BrokenPipeHandlerMixIn,
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 testserver_base.StoppableHTTPServer):
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This is a specialization of StoppableHTTPServer that adds client
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  verification."""
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pass
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class OCSPServer(testserver_base.ClientRestrictingServerMixIn,
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 testserver_base.BrokenPipeHandlerMixIn,
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 BaseHTTPServer.HTTPServer):
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This is a specialization of HTTPServer that serves an
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OCSP response"""
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def serve_forever_on_thread(self):
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.thread = threading.Thread(target = self.serve_forever,
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   name = "OCSPServerThread")
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.thread.start()
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def stop_serving(self):
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.shutdown()
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.thread.join()
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  testserver_base.ClientRestrictingServerMixIn,
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  testserver_base.BrokenPipeHandlerMixIn,
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  testserver_base.StoppableHTTPServer):
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This is a specialization of StoppableHTTPServer that add https support and
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  client verification."""
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, server_address, request_hander_class, pem_cert_and_key,
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers,
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               record_resume_info, tls_intolerant):
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.cert_chain = tlslite.api.X509CertChain().parseChain(pem_cert_and_key)
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    # Force using only python implementation - otherwise behavior is different
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    # depending on whether m2crypto Python module is present (error is thrown
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    # when it is). m2crypto uses a C (based on OpenSSL) implementation under
136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    # the hood.
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self.private_key = tlslite.api.parsePEMKey(pem_cert_and_key,
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                               private=True,
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                               implementations=['python'])
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.ssl_client_auth = ssl_client_auth
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.ssl_client_cas = []
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.tls_intolerant = tls_intolerant
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for ca_file in ssl_client_cas:
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      s = open(ca_file).read()
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      x509 = tlslite.api.X509()
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      x509.parse(s)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ssl_client_cas.append(x509.subject)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.ssl_handshake_settings = tlslite.api.HandshakeSettings()
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ssl_bulk_ciphers is not None:
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if record_resume_info:
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # If record_resume_info is true then we'll replace the session cache with
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # an object that records the lookups and inserts that it sees.
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.session_cache = RecordingSSLSessionCache()
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.session_cache = tlslite.api.SessionCache()
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    testserver_base.StoppableHTTPServer.__init__(self,
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 server_address,
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 request_hander_class)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def handshake(self, tlsConnection):
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Creates the SSL connection."""
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.tlsConnection = tlsConnection
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tlsConnection.handshakeServer(certChain=self.cert_chain,
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    privateKey=self.private_key,
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    sessionCache=self.session_cache,
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    reqCert=self.ssl_client_auth,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    settings=self.ssl_handshake_settings,
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    reqCAs=self.ssl_client_cas,
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    tlsIntolerant=self.tls_intolerant)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tlsConnection.ignoreAbruptClose = True
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except tlslite.api.TLSAbruptCloseError:
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Ignore abrupt close.
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except tlslite.api.TLSError, error:
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print "Handshake failure:", str(error)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class FTPServer(testserver_base.ClientRestrictingServerMixIn,
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                pyftpdlib.ftpserver.FTPServer):
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This is a specialization of FTPServer that adds client verification."""
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pass
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class TCPEchoServer(testserver_base.ClientRestrictingServerMixIn,
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    SocketServer.TCPServer):
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A TCP echo server that echoes back what it has received."""
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def server_bind(self):
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Override server_bind to store the server name."""
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SocketServer.TCPServer.server_bind(self)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    host, port = self.socket.getsockname()[:2]
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_name = socket.getfqdn(host)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_port = port
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def serve_forever(self):
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.stop = False
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.nonce_time = None
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while not self.stop:
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.handle_request()
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.socket.close()
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class UDPEchoServer(testserver_base.ClientRestrictingServerMixIn,
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    SocketServer.UDPServer):
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A UDP echo server that echoes back what it has received."""
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def server_bind(self):
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Override server_bind to store the server name."""
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SocketServer.UDPServer.server_bind(self)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    host, port = self.socket.getsockname()[:2]
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_name = socket.getfqdn(host)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_port = port
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def serve_forever(self):
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.stop = False
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.nonce_time = None
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while not self.stop:
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.handle_request()
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.socket.close()
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class TestPageHandler(testserver_base.BasePageHandler):
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Class variables to allow for persistence state between page handler
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # invocations
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  rst_limits = {}
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fail_precondition = {}
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, request, client_address, socket_server):
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    connect_handlers = [
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.RedirectConnectHandler,
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ServerAuthConnectHandler,
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DefaultConnectResponseHandler]
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get_handlers = [
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.NoCacheMaxAgeTimeHandler,
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.NoCacheTimeHandler,
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheTimeHandler,
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheExpiresHandler,
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheProxyRevalidateHandler,
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CachePrivateHandler,
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CachePublicHandler,
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheSMaxAgeHandler,
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheMustRevalidateHandler,
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheMustRevalidateMaxAgeHandler,
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheNoStoreHandler,
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheNoStoreMaxAgeHandler,
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheNoTransformHandler,
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DownloadHandler,
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DownloadFinishHandler,
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoHeader,
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoHeaderCache,
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoAllHandler,
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ZipFileHandler,
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.FileHandler,
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.SetCookieHandler,
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.SetManyCookiesHandler,
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ExpectAndSetCookieHandler,
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.SetHeaderHandler,
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.AuthBasicHandler,
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.AuthDigestHandler,
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.SlowServerHandler,
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ChunkedServerHandler,
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ContentTypeHandler,
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.NoContentHandler,
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ServerRedirectHandler,
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ClientRedirectHandler,
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.MultipartHandler,
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.GetSSLSessionCacheHandler,
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.SSLManySmallRecords,
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.GetChannelID,
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CloseSocketHandler,
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.RangeResetHandler,
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DefaultResponseHandler]
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    post_handlers = [
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoTitleHandler,
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoHandler,
2863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      self.PostOnlyFileHandler,
2873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      self.EchoMultipartPostHandler] + get_handlers
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    put_handlers = [
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoTitleHandler,
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoHandler] + get_handlers
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    head_handlers = [
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.FileHandler,
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DefaultResponseHandler]
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._mime_types = {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'crx' : 'application/x-chrome-extension',
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'exe' : 'application/octet-stream',
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'gif': 'image/gif',
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'jpeg' : 'image/jpeg',
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'jpg' : 'image/jpeg',
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'json': 'application/json',
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'pdf' : 'application/pdf',
303424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      'txt' : 'text/plain',
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'wav' : 'audio/wav',
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'xml' : 'text/xml'
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._default_mime_type = 'text/html'
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    testserver_base.BasePageHandler.__init__(self, request, client_address,
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             socket_server, connect_handlers,
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             get_handlers, head_handlers,
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             post_handlers, put_handlers)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetMIMETypeFromName(self, file_name):
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns the mime type for the specified file_name. So far it only looks
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    at the file extension."""
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (_shortname, extension) = os.path.splitext(file_name.split("?")[0])
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(extension) == 0:
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # no extension.
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self._default_mime_type
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # extension starts with a dot, so we need to remove it
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._mime_types.get(extension[1:], self._default_mime_type)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def NoCacheMaxAgeTimeHandler(self):
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and no caching requested."""
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/nocachetime/maxage"):
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=0')
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def NoCacheTimeHandler(self):
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and no caching requested."""
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/nocachetime"):
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'no-cache')
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheTimeHandler(self):
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and allows caching for one minute."""
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cachetime"):
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=60')
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheExpiresHandler(self):
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and set the page to expire on 1 Jan 2099."""
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/expires"):
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Expires', 'Thu, 1 Jan 2099 00:00:00 GMT')
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheProxyRevalidateHandler(self):
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and allows caching for 60 seconds"""
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/proxy-revalidate"):
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=60, proxy-revalidate')
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CachePrivateHandler(self):
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and allows caching for 5 seconds."""
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/private"):
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=3, private')
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CachePublicHandler(self):
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and allows caching for 5 seconds."""
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/public"):
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=3, public')
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheSMaxAgeHandler(self):
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow for caching."""
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/s-maxage"):
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'public, s-maxage = 60, max-age = 0')
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheMustRevalidateHandler(self):
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow caching."""
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/must-revalidate"):
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'must-revalidate')
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheMustRevalidateMaxAgeHandler(self):
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow caching event though max-age of 60
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    seconds is specified."""
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/must-revalidate/max-age"):
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=60, must-revalidate')
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheNoStoreHandler(self):
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow the page to be stored."""
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/no-store"):
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'no-store')
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheNoStoreMaxAgeHandler(self):
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow the page to be stored even though max-age
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    of 60 seconds is specified."""
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/no-store/max-age"):
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=60, no-store')
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheNoTransformHandler(self):
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow the content to transformed during
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user-agent caching"""
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/no-transform"):
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'no-transform')
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoHeader(self):
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler echoes back the value of a specific request header."""
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.EchoHeaderHelper("/echoheader")
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoHeaderCache(self):
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This function echoes back the value of a specific request header while
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    allowing caching for 16 hours."""
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.EchoHeaderHelper("/echoheadercache")
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoHeaderHelper(self, echo_header):
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This function echoes back the value of the request header passed in."""
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest(echo_header):
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char != -1:
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      header_name = self.path[query_char+1:]
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if echo_header == '/echoheadercache':
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Cache-control', 'max-age=60000')
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Cache-control', 'no-cache')
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # insert a vary header to properly indicate that the cachability of this
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # request is subject to value of the request header being echoed.
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(header_name) > 0:
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Vary', header_name)
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(header_name) > 0:
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(self.headers.getheader(header_name))
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ReadRequestBody(self):
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This function reads the body of the current HTTP request, handling
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    both plain and chunked transfer encoded requests."""
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.headers.getheader('transfer-encoding') != 'chunked':
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      length = int(self.headers.getheader('content-length'))
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self.rfile.read(length)
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Read the request body as chunks.
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    body = ""
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while True:
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      line = self.rfile.readline()
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      length = int(line, 16)
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if length == 0:
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.rfile.readline()
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      body += self.rfile.read(length)
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.rfile.read(2)
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return body
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoHandler(self):
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler just echoes back the payload of the request, for testing
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    form submission."""
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/echo"):
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(self.ReadRequestBody())
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoTitleHandler(self):
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler is like Echo, but sets the page title to the request."""
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/echotitle"):
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request = self.ReadRequestBody()
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>')
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(request)
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</title></head></html>')
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoAllHandler(self):
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler yields a (more) human-readable page listing information
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    about the request header & contents."""
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/echoall"):
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><style>'
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'pre { border: 1px solid black; margin: 5px; padding: 5px }'
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '</style></head><body>'
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '<div style="float: right">'
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '<a href="/echo">back to referring page</a></div>'
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '<h1>Request Body:</h1><pre>')
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.command == 'POST' or self.command == 'PUT':
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      qs = self.ReadRequestBody()
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      params = cgi.parse_qs(qs, keep_blank_values=1)
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for param in params:
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.wfile.write('%s=%s\n' % (param, params[param][0]))
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</pre>')
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<h1>Request Headers:</h1><pre>%s</pre>' % self.headers)
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</body></html>')
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  def EchoMultipartPostHandler(self):
6693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    """This handler echoes received multipart post data as json format."""
6703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
6713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if not (self._ShouldHandleRequest("/echomultipartpost") or
6723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)            self._ShouldHandleRequest("/searchbyimage")):
6733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      return False
6743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
6753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    content_type, parameters = cgi.parse_header(
6763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        self.headers.getheader('content-type'))
6773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if content_type == 'multipart/form-data':
6783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      post_multipart = cgi.parse_multipart(self.rfile, parameters)
6793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    elif content_type == 'application/x-www-form-urlencoded':
6803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      raise Exception('POST by application/x-www-form-urlencoded is '
6813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                      'not implemented.')
6823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    else:
6833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      post_multipart = {}
6843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
6853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    # Since the data can be binary, we encode them by base64.
6863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    post_multipart_base64_encoded = {}
6873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    for field, values in post_multipart.items():
6883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      post_multipart_base64_encoded[field] = [base64.b64encode(value)
6893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                              for value in values]
6903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
6913551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    result = {'POST_multipart' : post_multipart_base64_encoded}
6923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
6933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self.send_response(200)
6943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self.send_header("Content-type", "text/plain")
6953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self.end_headers()
6963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self.wfile.write(json.dumps(result, indent=2, sort_keys=False))
6973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return True
6983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def DownloadHandler(self):
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sends a downloadable file with or without reporting
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the size (6K)."""
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.path.startswith("/download-unknown-size"):
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      send_length = False
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.path.startswith("/download-known-size"):
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      send_length = True
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The test which uses this functionality is attempting to send
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # small chunks of data to the client.  Use a fairly large buffer
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # so that we'll fill chrome's IO buffer enough to force it to
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # actually write the data.
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # See also the comments in the client-side of this test in
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # download_uitest.cc
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_chunk1 = 35*1024
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_chunk2 = 10*1024
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'application/octet-stream')
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=0')
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if send_length:
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Length', size_chunk1 + size_chunk2)
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # First chunk of data:
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write("*" * size_chunk1)
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.flush()
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # handle requests until one of them clears this flag.
7332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.server.wait_for_download = True
7342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    while self.server.wait_for_download:
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.server.handle_request()
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Second chunk of data:
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write("*" * size_chunk2)
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def DownloadFinishHandler(self):
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler just tells the server to finish the current download."""
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/download-finish"):
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.server.wait_for_download = False
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=0')
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _ReplaceFileData(self, data, query_parameters):
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Replaces matching substrings in a file.
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    If the 'replace_text' URL query parameter is present, it is expected to be
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    of the form old_text:new_text, which indicates that any old_text strings in
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the file are replaced with new_text. Multiple 'replace_text' parameters may
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    be specified.
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    If the parameters are not present, |data| is returned.
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_dict = cgi.parse_qs(query_parameters)
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    replace_text_values = query_dict.get('replace_text', [])
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for replace_text_value in replace_text_values:
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      replace_text_args = replace_text_value.split(':')
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if len(replace_text_args) != 2:
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise ValueError(
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'replace_text must be of form old_text:new_text. Actual value: %s' %
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          replace_text_value)
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      old_text_b64, new_text_b64 = replace_text_args
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      old_text = base64.urlsafe_b64decode(old_text_b64)
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_text = base64.urlsafe_b64decode(new_text_b64)
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data = data.replace(old_text, new_text)
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return data
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ZipFileHandler(self):
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sends the contents of the requested file in compressed form.
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Can pass in a parameter that specifies that the content length be
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    C - the compressed size (OK),
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    U - the uncompressed size (Non-standard, but handled),
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    S - less than compressed (OK because we keep going),
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    M - larger than compressed but less than uncompressed (an error),
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    L - larger than uncompressed (an error)
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Example: compressedfiles/Picture_1.doc?C
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix = "/compressedfiles/"
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.path.startswith(prefix):
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Consume a request body if present.
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.command == 'POST' or self.command == 'PUT' :
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ReadRequestBody()
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not query in ('C', 'U', 'S', 'M', 'L'):
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sub_path = url_path[len(prefix):]
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entries = sub_path.split('/')
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_path = os.path.join(self.server.data_dir, *entries)
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.isdir(file_path):
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      file_path = os.path.join(file_path, 'index.html')
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not os.path.isfile(file_path):
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print "File not found " + sub_path + " full path:" + file_path
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_error(404)
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f = open(file_path, "rb")
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = f.read()
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uncompressed_len = len(data)
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f.close()
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Compress the data.
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = zlib.compress(data)
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    compressed_len = len(data)
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content_length = compressed_len
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query == 'U':
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_length = uncompressed_len
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif query == 'S':
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_length = compressed_len / 2
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif query == 'M':
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_length = (compressed_len + uncompressed_len) / 2
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif query == 'L':
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_length = compressed_len + uncompressed_len
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'application/msword')
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-encoding', 'deflate')
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Connection', 'close')
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Length', content_length)
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('ETag', '\'' + file_path + '\'')
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(data)
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def FileHandler(self):
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sends the contents of the requested file.  Wow, it's like
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    a real webserver!"""
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix = self.server.file_root_url
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.path.startswith(prefix):
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._FileHandlerHelper(prefix)
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def PostOnlyFileHandler(self):
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sends the contents of the requested file on a POST."""
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix = urlparse.urljoin(self.server.file_root_url, 'post/')
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.path.startswith(prefix):
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._FileHandlerHelper(prefix)
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _FileHandlerHelper(self, prefix):
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request_body = ''
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.command == 'POST' or self.command == 'PUT':
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Consume a request body if present.
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      request_body = self.ReadRequestBody()
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_dict = cgi.parse_qs(query)
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expected_body = query_dict.get('expected_body', [])
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if expected_body and request_body not in expected_body:
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(404)
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('')
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expected_headers = query_dict.get('expected_headers', [])
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for expected_header in expected_headers:
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      header_name, expected_value = expected_header.split(':')
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self.headers.getheader(header_name) != expected_value:
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_response(404)
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.end_headers()
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.wfile.write('')
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return True
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sub_path = url_path[len(prefix):]
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entries = sub_path.split('/')
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_path = os.path.join(self.server.data_dir, *entries)
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.isdir(file_path):
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      file_path = os.path.join(file_path, 'index.html')
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not os.path.isfile(file_path):
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print "File not found " + sub_path + " full path:" + file_path
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_error(404)
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f = open(file_path, "rb")
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = f.read()
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f.close()
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = self._ReplaceFileData(data, query)
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    old_protocol_version = self.protocol_version
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If file.mock-http-headers exists, it contains the headers we
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # should send.  Read them in and parse them.
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    headers_path = file_path + '.mock-http-headers'
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.isfile(headers_path):
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      f = open(headers_path, "r")
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # "HTTP/1.1 200 OK"
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response = f.readline()
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      http_major, http_minor, status_code = re.findall(
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'HTTP/(\d+).(\d+) (\d+)', response)[0]
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.protocol_version = "HTTP/%s.%s" % (http_major, http_minor)
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(int(status_code))
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for line in f:
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        header_values = re.findall('(\S+):\s*(.*)', line)
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if len(header_values) > 0:
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # "name: value"
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          name, value = header_values[0]
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.send_header(name, value)
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      f.close()
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Could be more generic once we support mime-type sniffing, but for
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # now we need to set it explicitly.
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      range_header = self.headers.get('Range')
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if range_header and range_header.startswith('bytes='):
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Note this doesn't handle all valid byte range_header values (i.e.
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # left open ended ones), just enough for what we needed so far.
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        range_header = range_header[6:].split('-')
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        start = int(range_header[0])
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if range_header[1]:
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          end = int(range_header[1])
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          end = len(data) - 1
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_response(206)
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        content_range = ('bytes ' + str(start) + '-' + str(end) + '/' +
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         str(len(data)))
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_header('Content-Range', content_range)
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        data = data[start: end + 1]
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_response(200)
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', self.GetMIMETypeFromName(file_path))
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Accept-Ranges', 'bytes')
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Length', len(data))
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('ETag', '\'' + file_path + '\'')
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (self.command != 'HEAD'):
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(data)
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.protocol_version = old_protocol_version
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SetCookieHandler(self):
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler just sets a cookie, for testing cookie handling."""
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/set-cookie"):
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char != -1:
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cookie_values = self.path[query_char + 1:].split('&')
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cookie_values = ("",)
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for cookie_value in cookie_values:
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Set-Cookie', '%s' % cookie_value)
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for cookie_value in cookie_values:
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('%s' % cookie_value)
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SetManyCookiesHandler(self):
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler just sets a given number of cookies, for testing handling
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       of large numbers of cookies."""
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/set-many-cookies"):
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char != -1:
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_cookies = int(self.path[query_char + 1:])
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_cookies = 0
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('', 'text/html')
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for _i in range(0, num_cookies):
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Set-Cookie', 'a=')
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('%d cookies were sent' % num_cookies)
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ExpectAndSetCookieHandler(self):
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Expects some cookies to be sent, and if they are, sets more cookies.
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The expect parameter specifies a required cookie.  May be specified multiple
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    times.
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The set parameter specifies a cookie to set if all required cookies are
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    preset.  May be specified multiple times.
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The data parameter specifies the response body data to be returned."""
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/expect-and-set-cookie"):
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _, _, _, _, query, _ = urlparse.urlparse(self.path)
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_dict = cgi.parse_qs(query)
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cookies = set()
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'Cookie' in self.headers:
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cookie_header = self.headers.getheader('Cookie')
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cookies.update([s.strip() for s in cookie_header.split(';')])
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    got_all_expected_cookies = True
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for expected_cookie in query_dict.get('expect', []):
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if expected_cookie not in cookies:
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        got_all_expected_cookies = False
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if got_all_expected_cookies:
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for cookie_value in query_dict.get('set', []):
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_header('Set-Cookie', '%s' % cookie_value)
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for data_value in query_dict.get('data', []):
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(data_value)
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SetHeaderHandler(self):
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sets a response header. Parameters are in the
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    key%3A%20value&key2%3A%20value2 format."""
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/set-header"):
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char != -1:
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      headers_values = self.path[query_char + 1:].split('&')
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      headers_values = ("",)
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for header_value in headers_values:
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      header_value = urllib.unquote(header_value)
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (key, value) = header_value.split(': ', 1)
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header(key, value)
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for header_value in headers_values:
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('%s' % header_value)
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AuthBasicHandler(self):
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler tests 'Basic' authentication.  It just sends a page with
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    title 'user/pass' if you succeed."""
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/auth-basic"):
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    username = userpass = password = b64str = ""
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expected_password = 'secret'
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    realm = 'testrealm'
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_cookie_if_challenged = False
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_params = cgi.parse_qs(query, True)
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'set-cookie-if-challenged' in query_params:
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      set_cookie_if_challenged = True
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'password' in query_params:
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected_password = query_params['password'][0]
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'realm' in query_params:
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      realm = query_params['realm'][0]
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    auth = self.headers.getheader('authorization')
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not auth:
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('no auth')
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      b64str = re.findall(r'Basic (\S+)', auth)[0]
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      userpass = base64.b64decode(b64str)
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      username, password = re.findall(r'([^:]+):(\S+)', userpass)[0]
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if password != expected_password:
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('wrong password')
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception, e:
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Authentication failed.
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(401)
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', 'text/html')
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if set_cookie_if_challenged:
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_header('Set-Cookie', 'got_challenged=true')
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<html><head>')
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<title>Denied: %s</title>' % e)
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</head><body>')
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('auth=%s<p>' % auth)
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('b64str=%s<p>' % b64str)
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('username: %s<p>' % username)
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('userpass: %s<p>' % userpass)
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('password: %s<p>' % password)
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('You sent:<br>%s<p>' % self.headers)
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</body></html>')
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Authentication successful.  (Return a cachable response to allow for
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # testing cached pages that require authentication.)
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    old_protocol_version = self.protocol_version
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.protocol_version = "HTTP/1.1"
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if_none_match = self.headers.getheader('if-none-match')
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if if_none_match == "abc":
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(304)
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif url_path.endswith(".gif"):
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Using chrome/test/data/google/logo.gif as the test image
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      test_image_path = ['google', 'logo.gif']
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gif_path = os.path.join(self.server.data_dir, *test_image_path)
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not os.path.isfile(gif_path):
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_error(404)
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.protocol_version = old_protocol_version
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return True
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      f = open(gif_path, "rb")
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data = f.read()
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      f.close()
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(200)
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', 'image/gif')
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Cache-control', 'max-age=60000')
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Etag', 'abc')
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(data)
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(200)
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', 'text/html')
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Cache-control', 'max-age=60000')
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Etag', 'abc')
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<html><head>')
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<title>%s/%s</title>' % (username, password))
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</head><body>')
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('auth=%s<p>' % auth)
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('You sent:<br>%s<p>' % self.headers)
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</body></html>')
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.protocol_version = old_protocol_version
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetNonce(self, force_reset=False):
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns a nonce that's stable per request path for the server's lifetime.
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This is a fake implementation. A real implementation would only use a given
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    nonce a single time (hence the name n-once). However, for the purposes of
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unittesting, we don't care about the security of the nonce.
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      force_reset: Iff set, the nonce will be changed. Useful for testing the
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "stale" response.
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if force_reset or not self.server.nonce_time:
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.server.nonce_time = time.time()
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return hashlib.md5('privatekey%s%d' %
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       (self.path, self.server.nonce_time)).hexdigest()
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AuthDigestHandler(self):
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler tests 'Digest' authentication.
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    It just sends a page with title 'user/pass' if you succeed.
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A stale response is sent iff "stale" is present in the request path.
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/auth-digest"):
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stale = 'stale' in self.path
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    nonce = self.GetNonce(force_reset=stale)
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    opaque = hashlib.md5('opaque').hexdigest()
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    password = 'secret'
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    realm = 'testrealm'
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    auth = self.headers.getheader('authorization')
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pairs = {}
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not auth:
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('no auth')
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not auth.startswith('Digest'):
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('not digest')
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Pull out all the name="value" pairs as a dictionary.
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pairs = dict(re.findall(r'(\b[^ ,=]+)="?([^",]+)"?', auth))
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Make sure it's all valid.
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if pairs['nonce'] != nonce:
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('wrong nonce')
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if pairs['opaque'] != opaque:
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('wrong opaque')
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Check the 'response' value and make sure it matches our magic hash.
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # See http://www.ietf.org/rfc/rfc2617.txt
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hash_a1 = hashlib.md5(
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ':'.join([pairs['username'], realm, password])).hexdigest()
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hash_a2 = hashlib.md5(':'.join([self.command, pairs['uri']])).hexdigest()
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if 'qop' in pairs and 'nc' in pairs and 'cnonce' in pairs:
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        response = hashlib.md5(':'.join([hash_a1, nonce, pairs['nc'],
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            pairs['cnonce'], pairs['qop'], hash_a2])).hexdigest()
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        response = hashlib.md5(':'.join([hash_a1, nonce, hash_a2])).hexdigest()
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if pairs['response'] != response:
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('wrong password')
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception, e:
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Authentication failed.
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(401)
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hdr = ('Digest '
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'realm="%s", '
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'domain="/", '
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'qop="auth", '
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'algorithm=MD5, '
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'nonce="%s", '
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'opaque="%s"') % (realm, nonce, opaque)
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if stale:
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        hdr += ', stale="TRUE"'
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('WWW-Authenticate', hdr)
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', 'text/html')
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<html><head>')
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<title>Denied: %s</title>' % e)
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</head><body>')
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('auth=%s<p>' % auth)
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('pairs=%s<p>' % pairs)
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('You sent:<br>%s<p>' % self.headers)
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('We are replying:<br>%s<p>' % hdr)
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</body></html>')
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Authentication successful.
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head>')
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<title>%s/%s</title>' % (pairs['username'], password))
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</head><body>')
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('auth=%s<p>' % auth)
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('pairs=%s<p>' % pairs)
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</body></html>')
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SlowServerHandler(self):
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Wait for the user suggested time before responding. The syntax is
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /slow?0.5 to wait for half a second."""
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/slow"):
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    wait_sec = 1.0
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char >= 0:
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try:
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        wait_sec = int(self.path[query_char + 1:])
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      except ValueError:
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pass
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time.sleep(wait_sec)
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write("waited %d seconds" % wait_sec)
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ChunkedServerHandler(self):
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Send chunked response. Allows to specify chunks parameters:
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     - waitBeforeHeaders - ms to wait before sending headers
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     - waitBetweenChunks - ms to wait between chunks
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     - chunkSize - size of each chunk in bytes
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     - chunksNumber - number of chunks
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Example: /chunked?waitBeforeHeaders=1000&chunkSize=5&chunksNumber=5
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    waits one second, then sends headers and five chunks five bytes each."""
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/chunked"):
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chunkedSettings = {'waitBeforeHeaders' : 0,
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'waitBetweenChunks' : 0,
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'chunkSize' : 5,
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'chunksNumber' : 5}
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char >= 0:
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      params = self.path[query_char + 1:].split('&')
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for param in params:
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        keyValue = param.split('=')
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if len(keyValue) == 2:
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          try:
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            chunkedSettings[keyValue[0]] = int(keyValue[1])
12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          except ValueError:
12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            pass
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time.sleep(0.001 * chunkedSettings['waitBeforeHeaders'])
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.protocol_version = 'HTTP/1.1' # Needed for chunked encoding
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Connection', 'close')
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Transfer-Encoding', 'chunked')
13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Chunked encoding: sending all chunks, then final zero-length chunk and
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # then final CRLF.
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for i in range(0, chunkedSettings['chunksNumber']):
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if i > 0:
13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        time.sleep(0.001 * chunkedSettings['waitBetweenChunks'])
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.sendChunkHelp('*' * chunkedSettings['chunkSize'])
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.flush() # Keep in mind that we start flushing only after 1kb.
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.sendChunkHelp('')
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ContentTypeHandler(self):
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns a string of html with the given content type.  E.g.,
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /contenttype?text/css returns an html file with the Content-Type
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    header set to text/css."""
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/contenttype"):
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content_type = self.path[query_char + 1:].strip()
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not content_type:
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_type = 'text/html'
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', content_type)
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write("<html>\n<body>\n<p>HTML text</p>\n</body>\n</html>\n")
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def NoContentHandler(self):
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns a 204 No Content response."""
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/nocontent"):
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(204)
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ServerRedirectHandler(self):
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sends a server redirect to the given URL. The syntax is
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '/server-redirect?http://foo.bar/asdf' to redirect to
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'http://foo.bar/asdf'"""
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_name = "/server-redirect"
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest(test_name):
13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char < 0 or len(self.path) <= query_char + 1:
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.sendRedirectHelp(test_name)
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
1351d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    dest = urllib.unquote(self.path[query_char + 1:])
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(301)  # moved permanently
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Location', dest)
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head>')
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ClientRedirectHandler(self):
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sends a client redirect to the given URL. The syntax is
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '/client-redirect?http://foo.bar/asdf' to redirect to
13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'http://foo.bar/asdf'"""
13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_name = "/client-redirect"
13685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest(test_name):
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char < 0 or len(self.path) <= query_char + 1:
13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.sendRedirectHelp(test_name)
13745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
1375d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    dest = urllib.unquote(self.path[query_char + 1:])
13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head>')
13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<meta http-equiv="refresh" content="0;url=%s">' % dest)
13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def MultipartHandler(self):
13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Send a multipart response (10 text/html pages)."""
13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_name = '/multipart'
13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest(test_name):
13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    num_frames = 10
13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bound = '12345'
13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type',
13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     'multipart/x-mixed-replace;boundary=' + bound)
13985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for i in xrange(num_frames):
14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('--' + bound + '\r\n')
14025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('Content-Type: text/html\r\n\r\n')
14035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<title>page ' + str(i) + '</title>')
14045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('page ' + str(i))
14055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('--' + bound + '--')
14075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
14085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetSSLSessionCacheHandler(self):
14105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Send a reply containing a log of the session cache operations."""
14115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest('/ssl-session-cache'):
14135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
14145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
14165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
14175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
14185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
14195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (action, sessionID) in self.server.session_cache.log:
14205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.wfile.write('%s\t%s\n' % (action, sessionID.encode('hex')))
14215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except AttributeError:
14225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('Pass --https-record-resume in order to use' +
14235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       ' this request')
14245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
14255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def SSLManySmallRecords(self):
14272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Sends a reply consisting of a variety of small writes. These will be
14282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    translated into a series of small SSL records when used over an HTTPS
14292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    server."""
14302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not self._ShouldHandleRequest('/ssl-many-small-records'):
14322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return False
14332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_response(200)
14352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
14362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.end_headers()
14372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Write ~26K of data, in 1350 byte chunks
14392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for i in xrange(20):
14402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.write('*' * 1350)
14412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.flush()
14422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return True
14432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def GetChannelID(self):
14452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Send a reply containing the hashed ChannelID that the client provided."""
14462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not self._ShouldHandleRequest('/channel-id'):
14482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return False
14492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_response(200)
14512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
14522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.end_headers()
14532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    channel_id = self.server.tlsConnection.channel_id.tostring()
14542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.wfile.write(hashlib.sha256(channel_id).digest().encode('base64'))
14552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return True
14562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CloseSocketHandler(self):
14585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Closes the socket without sending anything."""
14595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest('/close-socket'):
14615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
14625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.close()
14645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
14655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def RangeResetHandler(self):
14672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Send data broken up by connection resets every N (default 4K) bytes.
14682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Support range requests.  If the data requested doesn't straddle a reset
14692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    boundary, it will all be sent.  Used for testing resuming downloads."""
14702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    def DataForRange(start, end):
14722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      """Data to be provided for a particular range of bytes."""
14732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Offset and scale to avoid too obvious (and hence potentially
14742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # collidable) data.
14752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return ''.join([chr(y % 256)
14762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      for y in range(start * 2 + 15, end * 2 + 15, 2)])
14772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not self._ShouldHandleRequest('/rangereset'):
14792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return False
14802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1481f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    # HTTP/1.1 is required for ETag and range support.
1482f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self.protocol_version = 'HTTP/1.1'
14832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
14842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Defaults
14862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    size = 8000
14872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Note that the rst is sent just before sending the rst_boundary byte.
14882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    rst_boundary = 4000
14892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    respond_to_range = True
14902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    hold_for_signal = False
14912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    rst_limit = -1
14922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    token = 'DEFAULT'
14932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fail_precondition = 0
14942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    send_verifiers = True
14952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Parse the query
14972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    qdict = urlparse.parse_qs(query, True)
14982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'size' in qdict:
14992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      size = int(qdict['size'][0])
15002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'rst_boundary' in qdict:
15012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      rst_boundary = int(qdict['rst_boundary'][0])
15022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'token' in qdict:
15032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Identifying token for stateful tests.
15042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      token = qdict['token'][0]
15052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'rst_limit' in qdict:
15062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Max number of rsts for a given token.
15072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      rst_limit = int(qdict['rst_limit'][0])
15082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'bounce_range' in qdict:
15092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      respond_to_range = False
15102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'hold' in qdict:
15112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Note that hold_for_signal will not work with null range requests;
15122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # see TODO below.
15132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      hold_for_signal = True
15142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'no_verifiers' in qdict:
15152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      send_verifiers = False
15162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'fail_precondition' in qdict:
15172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fail_precondition = int(qdict['fail_precondition'][0])
15182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Record already set information, or set it.
15202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    rst_limit = TestPageHandler.rst_limits.setdefault(token, rst_limit)
15212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if rst_limit != 0:
15222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      TestPageHandler.rst_limits[token] -= 1
15232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fail_precondition = TestPageHandler.fail_precondition.setdefault(
15242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      token, fail_precondition)
15252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if fail_precondition != 0:
15262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      TestPageHandler.fail_precondition[token] -= 1
15272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    first_byte = 0
15292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_byte = size - 1
15302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Does that define what we want to return, or do we need to apply
15322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # a range?
15332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    range_response = False
15342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    range_header = self.headers.getheader('range')
15352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if range_header and respond_to_range:
15362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mo = re.match("bytes=(\d*)-(\d*)", range_header)
15372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if mo.group(1):
15382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        first_byte = int(mo.group(1))
15392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if mo.group(2):
15402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        last_byte = int(mo.group(2))
15412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if last_byte > size - 1:
15422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        last_byte = size - 1
15432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      range_response = True
15442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if last_byte < first_byte:
15452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return False
15462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (fail_precondition and
15482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (self.headers.getheader('If-Modified-Since') or
15492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         self.headers.getheader('If-Match'))):
15502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_response(412)
15512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.end_headers()
15522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return True
15532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if range_response:
15552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_response(206)
15562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_header('Content-Range',
15572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       'bytes %d-%d/%d' % (first_byte, last_byte, size))
15582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
15592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_response(200)
15602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_header('Content-Type', 'application/octet-stream')
15612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_header('Content-Length', last_byte - first_byte + 1)
15622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if send_verifiers:
15632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_header('Etag', '"XYZZY"')
15642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_header('Last-Modified', 'Tue, 19 Feb 2013 14:32 EST')
15652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.end_headers()
15662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if hold_for_signal:
15682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # TODO(rdsmith/phajdan.jr): http://crbug.com/169519: Without writing
15692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # a single byte, the self.server.handle_request() below hangs
15702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # without processing new incoming requests.
15712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.write(DataForRange(first_byte, first_byte + 1))
15722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      first_byte = first_byte + 1
15732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # handle requests until one of them clears this flag.
15742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.server.wait_for_download = True
15752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      while self.server.wait_for_download:
15762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        self.server.handle_request()
15772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    possible_rst = ((first_byte / rst_boundary) + 1) * rst_boundary
15792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if possible_rst >= last_byte or rst_limit == 0:
15802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # No RST has been requested in this range, so we don't need to
15812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # do anything fancy; just write the data and let the python
15822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # infrastructure close the connection.
15832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.write(DataForRange(first_byte, last_byte + 1))
15842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.flush()
15852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return True
15862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # We're resetting the connection part way in; go to the RST
15882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # boundary and then send an RST.
15892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Because socket semantics do not guarantee that all the data will be
15902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # sent when using the linger semantics to hard close a socket,
15912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # we send the data and then wait for our peer to release us
15922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # before sending the reset.
15932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    data = DataForRange(first_byte, possible_rst)
15942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.wfile.write(data)
15952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.wfile.flush()
15962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.server.wait_for_download = True
15972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    while self.server.wait_for_download:
15982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.server.handle_request()
15992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    l_onoff = 1  # Linger is active.
16002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    l_linger = 0  # Seconds to linger for.
16012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
16022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 struct.pack('ii', l_onoff, l_linger))
16032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Close all duplicates of the underlying socket to force the RST.
16052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.wfile.close()
16062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.rfile.close()
16072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.connection.close()
16082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return True
16102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def DefaultResponseHandler(self):
16125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This is the catch-all response handler for requests that aren't handled
16135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    by one of the special handlers above.
16145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Note that we specify the content-length as without it the https connection
16155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is not closed properly (and the browser keeps expecting data)."""
16165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    contents = "Default response given for path: " + self.path
16185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
16195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
16205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Length', len(contents))
16215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
16225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (self.command != 'HEAD'):
16235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(contents)
16245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
16255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def RedirectConnectHandler(self):
16275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sends a redirect to the CONNECT request for www.redirect.com. This
16285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    response is not specified by the RFC, so the browser should not follow
16295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the redirect."""
16305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (self.path.find("www.redirect.com") < 0):
16325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
16335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dest = "http://www.destination.com/foo.js"
16355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(302)  # moved temporarily
16375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Location', dest)
16385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Connection', 'close')
16395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
16405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
16415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ServerAuthConnectHandler(self):
16435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sends a 401 to the CONNECT request for www.server-auth.com. This
16445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    response doesn't make sense because the proxy server cannot request
16455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    server authentication."""
16465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (self.path.find("www.server-auth.com") < 0):
16485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
16495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    challenge = 'Basic realm="WallyWorld"'
16515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(401)  # unauthorized
16535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('WWW-Authenticate', challenge)
16545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Connection', 'close')
16555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
16565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
16575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def DefaultConnectResponseHandler(self):
16595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This is the catch-all response handler for CONNECT requests that aren't
16605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handled by one of the special handlers above.  Real Web servers respond
16615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    with 400 to CONNECT requests."""
16625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    contents = "Your client has issued a malformed or illegal request."
16645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(400)  # bad request
16655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
16665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Length', len(contents))
16675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
16685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(contents)
16695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
16705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # called by the redirect handling function when there is no parameter
16725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def sendRedirectHelp(self, redirect_name):
16735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
16745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
16755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
16765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><body><h1>Error: no redirect destination</h1>')
16775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('Use <pre>%s?http://dest...</pre>' % redirect_name)
16785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</body></html>')
16795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # called by chunked handling function
16815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def sendChunkHelp(self, chunk):
16825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Each chunk consists of: chunk size (hex), CRLF, chunk body, CRLF
16835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('%X\r\n' % len(chunk))
16845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(chunk)
16855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('\r\n')
16865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class OCSPHandler(testserver_base.BasePageHandler):
16895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, request, client_address, socket_server):
16905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handlers = [self.OCSPResponse]
16915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.ocsp_response = socket_server.ocsp_response
16922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    testserver_base.BasePageHandler.__init__(self, request, client_address,
16932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             socket_server, [], handlers, [],
16942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             handlers, [])
16955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def OCSPResponse(self):
16975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
16985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'application/ocsp-response')
16995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Length', str(len(self.ocsp_response)))
17005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
17015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(self.ocsp_response)
17035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class TCPEchoHandler(SocketServer.BaseRequestHandler):
17065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """The RequestHandler class for TCP echo server.
17075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  It is instantiated once per connection to the server, and overrides the
17095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handle() method to implement communication to the client.
17105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
17115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def handle(self):
17135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Handles the request from the client and constructs a response."""
17145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = self.request.recv(65536).strip()
17165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Verify the "echo request" message received from the client. Send back
17175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # "echo response" message if "echo request" message is valid.
17185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
17195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return_data = echo_message.GetEchoResponseData(data)
17205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not return_data:
17215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return
17225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except ValueError:
17235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
17245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.request.send(return_data)
17265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class UDPEchoHandler(SocketServer.BaseRequestHandler):
17295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """The RequestHandler class for UDP echo server.
17305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  It is instantiated once per connection to the server, and overrides the
17325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handle() method to implement communication to the client.
17335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
17345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def handle(self):
17365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Handles the request from the client and constructs a response."""
17375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = self.request[0].strip()
17395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request_socket = self.request[1]
17405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Verify the "echo request" message received from the client. Send back
17415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # "echo response" message if "echo request" message is valid.
17425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
17435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return_data = echo_message.GetEchoResponseData(data)
17445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not return_data:
17455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return
17465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except ValueError:
17475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
17485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request_socket.sendto(return_data, self.client_address)
17495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BasicAuthProxyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
17525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A request handler that behaves as a proxy server which requires
17535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  basic authentication. Only CONNECT, GET and HEAD is supported for now.
17545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
17555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  _AUTH_CREDENTIAL = 'Basic Zm9vOmJhcg==' # foo:bar
17575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def parse_request(self):
17595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Overrides parse_request to check credential."""
17605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not BaseHTTPServer.BaseHTTPRequestHandler.parse_request(self):
17625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
17635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    auth = self.headers.getheader('Proxy-Authorization')
17655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if auth != self._AUTH_CREDENTIAL:
17665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(407)
17675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Proxy-Authenticate', 'Basic realm="MyRealm1"')
17685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
17695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
17705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
17725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _start_read_write(self, sock):
17745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sock.setblocking(0)
17755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.request.setblocking(0)
17765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rlist = [self.request, sock]
17775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while True:
17785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ready_sockets, _unused, errors = select.select(rlist, [], [])
17795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if errors:
17805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_response(500)
17815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.end_headers()
17825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return
17835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for s in ready_sockets:
17845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        received = s.recv(1024)
17855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if len(received) == 0:
17865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return
17875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if s == self.request:
17885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          other = sock
17895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
17905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          other = self.request
17915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        other.send(received)
17925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _do_common_method(self):
17945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url = urlparse.urlparse(self.path)
17955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    port = url.port
17965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not port:
17975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if url.scheme == 'http':
17985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        port = 80
17995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif url.scheme == 'https':
18005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        port = 443
18015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not url.hostname or not port:
18025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(400)
18035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
18045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
18055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(url.path) == 0:
18075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = '/'
18085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
18095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = url.path
18105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(url.query) > 0:
18115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = '%s?%s' % (url.path, url.query)
18125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sock = None
18145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
18155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock = socket.create_connection((url.hostname, port))
18165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock.send('%s %s %s\r\n' % (
18175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.command, path, self.protocol_version))
18185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for header in self.headers.headers:
18195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        header = header.strip()
18205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (header.lower().startswith('connection') or
18215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            header.lower().startswith('proxy')):
18225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue
18235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sock.send('%s\r\n' % header)
18245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock.send('\r\n')
18255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._start_read_write(sock)
18265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception:
18275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(500)
18285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
18295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finally:
18305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if sock is not None:
18315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sock.close()
18325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def do_CONNECT(self):
18345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
18355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pos = self.path.rfind(':')
18365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      host = self.path[:pos]
18375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      port = int(self.path[pos+1:])
18385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception:
18395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(400)
18405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
18415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
18435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock = socket.create_connection((host, port))
18445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(200, 'Connection established')
18455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
18465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._start_read_write(sock)
18475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception:
18485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(500)
18495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
18505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finally:
18515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock.close()
18525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def do_GET(self):
18545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._do_common_method()
18555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def do_HEAD(self):
18575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._do_common_method()
18585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ServerRunner(testserver_base.TestServerRunner):
18615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """TestServerRunner for the net test servers."""
18625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
18645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    super(ServerRunner, self).__init__()
18655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.__ocsp_server = None
18665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __make_data_dir(self):
18685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.data_dir:
18695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not os.path.isdir(self.options.data_dir):
18705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise testserver_base.OptionError('specified data dir not found: ' +
18715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self.options.data_dir + ' exiting...')
18725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my_data_dir = self.options.data_dir
18735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
18745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Create the default path to our data dir, relative to the exe dir.
18755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my_data_dir = os.path.join(BASE_DIR, "..", "..", "..", "..",
18765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 "test", "data")
18775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #TODO(ibrar): Must use Find* funtion defined in google\tools
18795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #i.e my_data_dir = FindUpward(my_data_dir, "test", "data")
18805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return my_data_dir
18825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def create_server(self, server_data):
18845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    port = self.options.port
18855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    host = self.options.host
18865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.server_type == SERVER_HTTP:
18885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self.options.https:
18895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pem_cert_and_key = None
18905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if self.options.cert_and_key_file:
18915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if not os.path.isfile(self.options.cert_and_key_file):
18925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            raise testserver_base.OptionError(
18935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'specified server cert file not found: ' +
18945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                self.options.cert_and_key_file + ' exiting...')
18955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          pem_cert_and_key = file(self.options.cert_and_key_file, 'r').read()
18965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
18975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # generate a new certificate and run an OCSP server for it.
18985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.__ocsp_server = OCSPServer((host, 0), OCSPHandler)
18995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          print ('OCSP server started on %s:%d...' %
19005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              (host, self.__ocsp_server.server_port))
19015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ocsp_der = None
19035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ocsp_state = None
19045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if self.options.ocsp == 'ok':
19065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_GOOD
19075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif self.options.ocsp == 'revoked':
19085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_REVOKED
19095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif self.options.ocsp == 'invalid':
19105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_INVALID
19115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif self.options.ocsp == 'unauthorized':
19125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_UNAUTHORIZED
19135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif self.options.ocsp == 'unknown':
19145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_UNKNOWN
19155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          else:
19165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            raise testserver_base.OptionError('unknown OCSP status: ' +
19175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                self.options.ocsp_status)
19185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (pem_cert_and_key, ocsp_der) = minica.GenerateCertKeyAndOCSP(
19205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              subject = "127.0.0.1",
19215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              ocsp_url = ("http://%s:%d/ocsp" %
19225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  (host, self.__ocsp_server.server_port)),
1923a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)              ocsp_state = ocsp_state,
1924a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)              serial = self.options.cert_serial)
19255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.__ocsp_server.ocsp_response = ocsp_der
19275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for ca_cert in self.options.ssl_client_ca:
19295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if not os.path.isfile(ca_cert):
19305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            raise testserver_base.OptionError(
19315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'specified trusted client CA file not found: ' + ca_cert +
19325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                ' exiting...')
19335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key,
19345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             self.options.ssl_client_auth,
19355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             self.options.ssl_client_ca,
19365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             self.options.ssl_bulk_cipher,
19375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             self.options.record_resume,
19385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             self.options.tls_intolerant)
19395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        print 'HTTPS server started on %s:%d...' % (host, server.server_port)
19405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
19415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        server = HTTPServer((host, port), TestPageHandler)
19425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        print 'HTTP server started on %s:%d...' % (host, server.server_port)
19435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server.data_dir = self.__make_data_dir()
19455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server.file_root_url = self.options.file_root_url
19465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
19475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_WEBSOCKET:
19485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Launch pywebsocket via WebSocketServer.
19495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logger = logging.getLogger()
19505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logger.addHandler(logging.StreamHandler())
19515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # TODO(toyoshim): Remove following os.chdir. Currently this operation
19525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # is required to work correctly. It should be fixed from pywebsocket side.
19535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.chdir(self.__make_data_dir())
19545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      websocket_options = WebSocketOptions(host, port, '.')
19555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self.options.cert_and_key_file:
19565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.use_tls = True
19575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.private_key = self.options.cert_and_key_file
19585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.certificate = self.options.cert_and_key_file
19595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self.options.ssl_client_auth:
19605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.tls_client_auth = True
19615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if len(self.options.ssl_client_ca) != 1:
19625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          raise testserver_base.OptionError(
19635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'one trusted client CA file should be specified')
19645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if not os.path.isfile(self.options.ssl_client_ca[0]):
19655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          raise testserver_base.OptionError(
19665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'specified trusted client CA file not found: ' +
19675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              self.options.ssl_client_ca[0] + ' exiting...')
19685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.tls_client_ca = self.options.ssl_client_ca[0]
19695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = WebSocketServer(websocket_options)
19705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'WebSocket server started on %s:%d...' % (host, server.server_port)
19715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
19725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_TCP_ECHO:
19735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Used for generating the key (randomly) that encodes the "echo request"
19745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # message.
19755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      random.seed()
19765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = TCPEchoServer((host, port), TCPEchoHandler)
19775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'Echo TCP server started on port %d...' % server.server_port
19785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
19795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_UDP_ECHO:
19805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Used for generating the key (randomly) that encodes the "echo request"
19815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # message.
19825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      random.seed()
19835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = UDPEchoServer((host, port), UDPEchoHandler)
19845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'Echo UDP server started on port %d...' % server.server_port
19855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
19865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_BASIC_AUTH_PROXY:
19875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = HTTPServer((host, port), BasicAuthProxyRequestHandler)
19885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'BasicAuthProxy server started on port %d...' % server.server_port
19895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
19905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_FTP:
19915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my_data_dir = self.__make_data_dir()
19925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Instantiate a dummy authorizer for managing 'virtual' users
19945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      authorizer = pyftpdlib.ftpserver.DummyAuthorizer()
19955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Define a new user having full r/w permissions and a read-only
19975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # anonymous user
19985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw')
19995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      authorizer.add_anonymous(my_data_dir)
20015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Instantiate FTP handler class
20035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ftp_handler = pyftpdlib.ftpserver.FTPHandler
20045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ftp_handler.authorizer = authorizer
20055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Define a customized banner (string returned when client connects)
20075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ftp_handler.banner = ("pyftpdlib %s based ftpd ready." %
20085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            pyftpdlib.ftpserver.__ver__)
20095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Instantiate FTP server class and listen to address:port
20115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = pyftpdlib.ftpserver.FTPServer((host, port), ftp_handler)
20125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.socket.getsockname()[1]
20135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'FTP server started on port %d...' % server_data['port']
20145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
20155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise testserver_base.OptionError('unknown server type' +
20165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.options.server_type)
20175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return server
20195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def run_server(self):
20215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.__ocsp_server:
20225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.__ocsp_server.serve_forever_on_thread()
20235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    testserver_base.TestServerRunner.run_server(self)
20255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.__ocsp_server:
20275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.__ocsp_server.stop_serving()
20285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def add_options(self):
20305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    testserver_base.TestServerRunner.add_options(self)
20315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('-f', '--ftp', action='store_const',
20325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_FTP, default=SERVER_HTTP,
20335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='server_type',
20345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up an FTP server.')
20355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--tcp-echo', action='store_const',
20365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_TCP_ECHO, default=SERVER_HTTP,
20375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='server_type',
20385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up a tcp echo server.')
20395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--udp-echo', action='store_const',
20405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_UDP_ECHO, default=SERVER_HTTP,
20415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='server_type',
20425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up a udp echo server.')
20435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--basic-auth-proxy', action='store_const',
20445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_BASIC_AUTH_PROXY,
20455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  default=SERVER_HTTP, dest='server_type',
20465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up a proxy server which requires '
20475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'basic authentication.')
20485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--websocket', action='store_const',
20495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_WEBSOCKET, default=SERVER_HTTP,
20505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='server_type',
20515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up a WebSocket server.')
20525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--https', action='store_true',
20535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='https', help='Specify that https '
20545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'should be used.')
20555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--cert-and-key-file',
20565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='cert_and_key_file', help='specify the '
20575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'path to the file containing the certificate '
20585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'and private key for the server in PEM '
20595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'format')
20605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--ocsp', dest='ocsp', default='ok',
20615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='The type of OCSP response generated '
20625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'for the automatically generated '
20635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'certificate. One of [ok,revoked,invalid]')
2064a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    self.option_parser.add_option('--cert-serial', dest='cert_serial',
2065a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                                  default=0, type=int,
2066a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                                  help='If non-zero then the generated '
2067a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                                  'certificate will have this serial number')
20685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--tls-intolerant', dest='tls_intolerant',
20695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  default='0', type='int',
20705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='If nonzero, certain TLS connections '
20715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'will be aborted in order to test version '
20725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'fallback. 1 means all TLS versions will be '
20735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'aborted. 2 means TLS 1.1 or higher will be '
20745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'aborted. 3 means TLS 1.2 or higher will be '
20755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'aborted.')
20765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--https-record-resume',
20775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='record_resume', const=True,
20785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  default=False, action='store_const',
20795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Record resumption cache events rather '
20805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'than resuming as normal. Allows the use of '
20815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'the /ssl-session-cache request')
20825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--ssl-client-auth', action='store_true',
20835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Require SSL client auth on every '
20845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'connection.')
20855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--ssl-client-ca', action='append',
20865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  default=[], help='Specify that the client '
20875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'certificate request should include the CA '
20885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'named in the subject of the DER-encoded '
20895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'certificate contained in the specified '
20905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'file. This option may appear multiple '
20915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'times, indicating multiple CA names should '
20925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'be sent in the request.')
20935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--ssl-bulk-cipher', action='append',
20945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Specify the bulk encryption '
20955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'algorithm(s) that will be accepted by the '
20965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'SSL server. Valid values are "aes256", '
20975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  '"aes128", "3des", "rc4". If omitted, all '
20985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'algorithms will be used. This option may '
20995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'appear multiple times, indicating '
21005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'multiple algorithms should be enabled.');
21015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--file-root-url', default='/files/',
21025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Specify a root URL for files served.')
21035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
21065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sys.exit(ServerRunner().main())
2107