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
30effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochimport ssl
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import struct
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import threading
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import urllib
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import urlparse
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import zlib
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)BASE_DIR = os.path.dirname(os.path.abspath(__file__))
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(BASE_DIR)))
41a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
42a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch# Temporary hack to deal with tlslite 0.3.8 -> 0.4.6 upgrade.
43a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch#
44a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch# TODO(davidben): Remove this when it has cycled through all the bots and
45a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch# developer checkouts or when http://crbug.com/356276 is resolved.
46a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochtry:
47a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  os.remove(os.path.join(ROOT_DIR, 'third_party', 'tlslite',
48a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                         'tlslite', 'utils', 'hmac.pyc'))
49a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochexcept Exception:
50a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  pass
51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# Append at the end of sys.path, it's fine to use the system library.
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)sys.path.append(os.path.join(ROOT_DIR, 'third_party', 'pyftpdlib', 'src'))
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch# Insert at the beginning of the path, we want to use our copies of the library
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# unconditionally.
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party', 'pywebsocket', 'src'))
58a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochsys.path.insert(0, os.path.join(ROOT_DIR, 'third_party', 'tlslite'))
59a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
60effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochimport mod_pywebsocket.standalone
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from mod_pywebsocket.standalone import WebSocketServer
62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# import manually
63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochmod_pywebsocket.standalone.ssl = ssl
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochimport pyftpdlib.ftpserver
66a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
67a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochimport tlslite
68a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochimport tlslite.api
69a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
70a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochimport echo_message
71a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochimport testserver_base
72a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SERVER_HTTP = 0
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SERVER_FTP = 1
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SERVER_TCP_ECHO = 2
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SERVER_UDP_ECHO = 3
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SERVER_BASIC_AUTH_PROXY = 4
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SERVER_WEBSOCKET = 5
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Default request queue size for WebSocketServer.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)_DEFAULT_REQUEST_QUEUE_SIZE = 128
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class WebSocketOptions:
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Holds options for WebSocketServer."""
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, host, port, data_dir):
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.request_queue_size = _DEFAULT_REQUEST_QUEUE_SIZE
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_host = host
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.port = port
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.websock_handlers = data_dir
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.scan_dir = None
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.allow_handlers_outside_root_dir = False
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.websock_handlers_map_file = None
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.cgi_directories = []
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.is_executable_method = None
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.allow_draft75 = False
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.strict = True
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.use_tls = False
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.private_key = None
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.certificate = None
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.tls_client_auth = False
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.tls_client_ca = None
104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    self.tls_module = 'ssl'
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.use_basic_auth = False
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.basic_auth_credential = 'Basic ' + base64.b64encode('test:test')
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class RecordingSSLSessionCache(object):
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """RecordingSSLSessionCache acts as a TLS session cache and maintains a log of
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  lookups and inserts in order to test session cache behaviours."""
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.log = []
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __getitem__(self, sessionID):
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.log.append(('lookup', sessionID))
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise KeyError()
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __setitem__(self, sessionID, session):
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.log.append(('insert', sessionID))
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class HTTPServer(testserver_base.ClientRestrictingServerMixIn,
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 testserver_base.BrokenPipeHandlerMixIn,
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 testserver_base.StoppableHTTPServer):
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This is a specialization of StoppableHTTPServer that adds client
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  verification."""
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pass
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class OCSPServer(testserver_base.ClientRestrictingServerMixIn,
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 testserver_base.BrokenPipeHandlerMixIn,
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 BaseHTTPServer.HTTPServer):
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This is a specialization of HTTPServer that serves an
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OCSP response"""
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def serve_forever_on_thread(self):
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.thread = threading.Thread(target = self.serve_forever,
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   name = "OCSPServerThread")
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.thread.start()
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def stop_serving(self):
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.shutdown()
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.thread.join()
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class HTTPSServer(tlslite.api.TLSSocketServerMixIn,
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  testserver_base.ClientRestrictingServerMixIn,
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  testserver_base.BrokenPipeHandlerMixIn,
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  testserver_base.StoppableHTTPServer):
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This is a specialization of StoppableHTTPServer that add https support and
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  client verification."""
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, server_address, request_hander_class, pem_cert_and_key,
156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)               ssl_client_auth, ssl_client_cas, ssl_client_cert_types,
1570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch               ssl_bulk_ciphers, ssl_key_exchanges, enable_npn,
158116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch               record_resume_info, tls_intolerant,
159116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch               tls_intolerance_type, signed_cert_timestamps,
1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               fallback_scsv_enabled, ocsp_response, disable_session_cache):
161a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    self.cert_chain = tlslite.api.X509CertChain()
162a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    self.cert_chain.parsePemList(pem_cert_and_key)
163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    # Force using only python implementation - otherwise behavior is different
164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    # depending on whether m2crypto Python module is present (error is thrown
165eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    # when it is). m2crypto uses a C (based on OpenSSL) implementation under
166eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    # the hood.
167eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self.private_key = tlslite.api.parsePEMKey(pem_cert_and_key,
168eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                               private=True,
169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                               implementations=['python'])
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.ssl_client_auth = ssl_client_auth
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.ssl_client_cas = []
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    self.ssl_client_cert_types = []
1730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if enable_npn:
1740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      self.next_protos = ['http/1.1']
1750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    else:
1760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      self.next_protos = None
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    self.signed_cert_timestamps = signed_cert_timestamps
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.fallback_scsv_enabled = fallback_scsv_enabled
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.ocsp_response = ocsp_response
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if ssl_client_auth:
182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      for ca_file in ssl_client_cas:
183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        s = open(ca_file).read()
184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        x509 = tlslite.api.X509()
185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        x509.parse(s)
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        self.ssl_client_cas.append(x509.subject)
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      for cert_type in ssl_client_cert_types:
189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        self.ssl_client_cert_types.append({
190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            "rsa_sign": tlslite.api.ClientCertificateType.rsa_sign,
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            "dss_sign": tlslite.api.ClientCertificateType.dss_sign,
192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            "ecdsa_sign": tlslite.api.ClientCertificateType.ecdsa_sign,
193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            }[cert_type])
194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.ssl_handshake_settings = tlslite.api.HandshakeSettings()
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ssl_bulk_ciphers is not None:
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers
198a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    if ssl_key_exchanges is not None:
199a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      self.ssl_handshake_settings.keyExchangeNames = ssl_key_exchanges
200116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if tls_intolerant != 0:
201116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      self.ssl_handshake_settings.tlsIntolerant = (3, tls_intolerant)
202116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      self.ssl_handshake_settings.tlsIntoleranceType = tls_intolerance_type
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if disable_session_cache:
2065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self.session_cache = None
2075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    elif record_resume_info:
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # If record_resume_info is true then we'll replace the session cache with
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # an object that records the lookups and inserts that it sees.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.session_cache = RecordingSSLSessionCache()
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.session_cache = tlslite.api.SessionCache()
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    testserver_base.StoppableHTTPServer.__init__(self,
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 server_address,
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 request_hander_class)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def handshake(self, tlsConnection):
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Creates the SSL connection."""
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.tlsConnection = tlsConnection
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tlsConnection.handshakeServer(certChain=self.cert_chain,
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    privateKey=self.private_key,
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    sessionCache=self.session_cache,
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    reqCert=self.ssl_client_auth,
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    settings=self.ssl_handshake_settings,
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    reqCAs=self.ssl_client_cas,
228cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                    reqCertTypes=self.ssl_client_cert_types,
2290529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                    nextProtos=self.next_protos,
230a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    signedCertTimestamps=
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    self.signed_cert_timestamps,
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    fallbackSCSV=self.fallback_scsv_enabled,
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    ocspResponse = self.ocsp_response)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tlsConnection.ignoreAbruptClose = True
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except tlslite.api.TLSAbruptCloseError:
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Ignore abrupt close.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except tlslite.api.TLSError, error:
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print "Handshake failure:", str(error)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class FTPServer(testserver_base.ClientRestrictingServerMixIn,
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                pyftpdlib.ftpserver.FTPServer):
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This is a specialization of FTPServer that adds client verification."""
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pass
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class TCPEchoServer(testserver_base.ClientRestrictingServerMixIn,
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    SocketServer.TCPServer):
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A TCP echo server that echoes back what it has received."""
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def server_bind(self):
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Override server_bind to store the server name."""
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SocketServer.TCPServer.server_bind(self)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    host, port = self.socket.getsockname()[:2]
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_name = socket.getfqdn(host)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_port = port
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def serve_forever(self):
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.stop = False
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.nonce_time = None
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while not self.stop:
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.handle_request()
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.socket.close()
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class UDPEchoServer(testserver_base.ClientRestrictingServerMixIn,
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    SocketServer.UDPServer):
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A UDP echo server that echoes back what it has received."""
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def server_bind(self):
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Override server_bind to store the server name."""
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SocketServer.UDPServer.server_bind(self)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    host, port = self.socket.getsockname()[:2]
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_name = socket.getfqdn(host)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.server_port = port
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def serve_forever(self):
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.stop = False
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.nonce_time = None
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while not self.stop:
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.handle_request()
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.socket.close()
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class TestPageHandler(testserver_base.BasePageHandler):
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Class variables to allow for persistence state between page handler
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # invocations
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  rst_limits = {}
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fail_precondition = {}
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, request, client_address, socket_server):
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    connect_handlers = [
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.RedirectConnectHandler,
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ServerAuthConnectHandler,
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DefaultConnectResponseHandler]
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    get_handlers = [
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.NoCacheMaxAgeTimeHandler,
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.NoCacheTimeHandler,
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheTimeHandler,
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheExpiresHandler,
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheProxyRevalidateHandler,
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CachePrivateHandler,
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CachePublicHandler,
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheSMaxAgeHandler,
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheMustRevalidateHandler,
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheMustRevalidateMaxAgeHandler,
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheNoStoreHandler,
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheNoStoreMaxAgeHandler,
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CacheNoTransformHandler,
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DownloadHandler,
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DownloadFinishHandler,
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoHeader,
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoHeaderCache,
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoAllHandler,
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ZipFileHandler,
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.FileHandler,
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.SetCookieHandler,
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.SetManyCookiesHandler,
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ExpectAndSetCookieHandler,
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.SetHeaderHandler,
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.AuthBasicHandler,
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.AuthDigestHandler,
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.SlowServerHandler,
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ChunkedServerHandler,
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ContentTypeHandler,
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.NoContentHandler,
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ServerRedirectHandler,
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ClientRedirectHandler,
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.GetSSLSessionCacheHandler,
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.SSLManySmallRecords,
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.GetChannelID,
3381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      self.ClientCipherListHandler,
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.CloseSocketHandler,
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.RangeResetHandler,
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DefaultResponseHandler]
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    post_handlers = [
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoTitleHandler,
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoHandler,
3453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      self.PostOnlyFileHandler,
3463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      self.EchoMultipartPostHandler] + get_handlers
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    put_handlers = [
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoTitleHandler,
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.EchoHandler] + get_handlers
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    head_handlers = [
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.FileHandler,
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.DefaultResponseHandler]
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._mime_types = {
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'crx' : 'application/x-chrome-extension',
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'exe' : 'application/octet-stream',
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'gif': 'image/gif',
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'jpeg' : 'image/jpeg',
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'jpg' : 'image/jpeg',
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'json': 'application/json',
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'pdf' : 'application/pdf',
362424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      'txt' : 'text/plain',
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'wav' : 'audio/wav',
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'xml' : 'text/xml'
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._default_mime_type = 'text/html'
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    testserver_base.BasePageHandler.__init__(self, request, client_address,
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             socket_server, connect_handlers,
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             get_handlers, head_handlers,
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             post_handlers, put_handlers)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetMIMETypeFromName(self, file_name):
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns the mime type for the specified file_name. So far it only looks
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    at the file extension."""
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (_shortname, extension) = os.path.splitext(file_name.split("?")[0])
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(extension) == 0:
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # no extension.
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self._default_mime_type
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # extension starts with a dot, so we need to remove it
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._mime_types.get(extension[1:], self._default_mime_type)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def NoCacheMaxAgeTimeHandler(self):
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and no caching requested."""
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/nocachetime/maxage"):
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=0')
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def NoCacheTimeHandler(self):
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and no caching requested."""
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/nocachetime"):
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'no-cache')
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheTimeHandler(self):
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and allows caching for one minute."""
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cachetime"):
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=60')
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheExpiresHandler(self):
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and set the page to expire on 1 Jan 2099."""
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/expires"):
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Expires', 'Thu, 1 Jan 2099 00:00:00 GMT')
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheProxyRevalidateHandler(self):
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and allows caching for 60 seconds"""
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/proxy-revalidate"):
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=60, proxy-revalidate')
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CachePrivateHandler(self):
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and allows caching for 5 seconds."""
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/private"):
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=3, private')
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CachePublicHandler(self):
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and allows caching for 5 seconds."""
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/public"):
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=3, public')
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheSMaxAgeHandler(self):
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow for caching."""
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/s-maxage"):
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'public, s-maxage = 60, max-age = 0')
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheMustRevalidateHandler(self):
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow caching."""
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/must-revalidate"):
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'must-revalidate')
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheMustRevalidateMaxAgeHandler(self):
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow caching event though max-age of 60
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    seconds is specified."""
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/must-revalidate/max-age"):
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=60, must-revalidate')
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheNoStoreHandler(self):
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow the page to be stored."""
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/no-store"):
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'no-store')
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheNoStoreMaxAgeHandler(self):
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow the page to be stored even though max-age
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    of 60 seconds is specified."""
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/no-store/max-age"):
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=60, no-store')
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CacheNoTransformHandler(self):
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This request handler yields a page with the title set to the current
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system time, and does not allow the content to transformed during
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user-agent caching"""
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/cache/no-transform"):
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'no-transform')
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>%s</title></head></html>' %
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     time.time())
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoHeader(self):
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler echoes back the value of a specific request header."""
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.EchoHeaderHelper("/echoheader")
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoHeaderCache(self):
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This function echoes back the value of a specific request header while
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    allowing caching for 16 hours."""
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.EchoHeaderHelper("/echoheadercache")
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoHeaderHelper(self, echo_header):
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This function echoes back the value of the request header passed in."""
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest(echo_header):
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char != -1:
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      header_name = self.path[query_char+1:]
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if echo_header == '/echoheadercache':
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Cache-control', 'max-age=60000')
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Cache-control', 'no-cache')
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # insert a vary header to properly indicate that the cachability of this
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # request is subject to value of the request header being echoed.
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(header_name) > 0:
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Vary', header_name)
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(header_name) > 0:
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(self.headers.getheader(header_name))
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ReadRequestBody(self):
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This function reads the body of the current HTTP request, handling
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    both plain and chunked transfer encoded requests."""
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.headers.getheader('transfer-encoding') != 'chunked':
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      length = int(self.headers.getheader('content-length'))
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self.rfile.read(length)
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Read the request body as chunks.
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    body = ""
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while True:
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      line = self.rfile.readline()
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      length = int(line, 16)
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if length == 0:
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.rfile.readline()
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      body += self.rfile.read(length)
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.rfile.read(2)
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return body
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoHandler(self):
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler just echoes back the payload of the request, for testing
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    form submission."""
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/echo"):
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(self.ReadRequestBody())
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoTitleHandler(self):
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler is like Echo, but sets the page title to the request."""
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/echotitle"):
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request = self.ReadRequestBody()
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><title>')
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(request)
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</title></head></html>')
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def EchoAllHandler(self):
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler yields a (more) human-readable page listing information
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    about the request header & contents."""
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/echoall"):
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head><style>'
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'pre { border: 1px solid black; margin: 5px; padding: 5px }'
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '</style></head><body>'
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '<div style="float: right">'
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '<a href="/echo">back to referring page</a></div>'
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '<h1>Request Body:</h1><pre>')
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.command == 'POST' or self.command == 'PUT':
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      qs = self.ReadRequestBody()
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      params = cgi.parse_qs(qs, keep_blank_values=1)
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for param in params:
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.wfile.write('%s=%s\n' % (param, params[param][0]))
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</pre>')
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<h1>Request Headers:</h1><pre>%s</pre>' % self.headers)
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</body></html>')
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  def EchoMultipartPostHandler(self):
7283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    """This handler echoes received multipart post data as json format."""
7293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if not (self._ShouldHandleRequest("/echomultipartpost") or
7313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)            self._ShouldHandleRequest("/searchbyimage")):
7323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      return False
7333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    content_type, parameters = cgi.parse_header(
7353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        self.headers.getheader('content-type'))
7363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if content_type == 'multipart/form-data':
7373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      post_multipart = cgi.parse_multipart(self.rfile, parameters)
7383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    elif content_type == 'application/x-www-form-urlencoded':
7393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      raise Exception('POST by application/x-www-form-urlencoded is '
7403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                      'not implemented.')
7413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    else:
7423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      post_multipart = {}
7433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    # Since the data can be binary, we encode them by base64.
7453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    post_multipart_base64_encoded = {}
7463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    for field, values in post_multipart.items():
7473551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      post_multipart_base64_encoded[field] = [base64.b64encode(value)
7483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                              for value in values]
7493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    result = {'POST_multipart' : post_multipart_base64_encoded}
7513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self.send_response(200)
7533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self.send_header("Content-type", "text/plain")
7543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self.end_headers()
7553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self.wfile.write(json.dumps(result, indent=2, sort_keys=False))
7563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return True
7573551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def DownloadHandler(self):
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sends a downloadable file with or without reporting
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the size (6K)."""
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.path.startswith("/download-unknown-size"):
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      send_length = False
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.path.startswith("/download-known-size"):
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      send_length = True
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The test which uses this functionality is attempting to send
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # small chunks of data to the client.  Use a fairly large buffer
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # so that we'll fill chrome's IO buffer enough to force it to
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # actually write the data.
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # See also the comments in the client-side of this test in
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # download_uitest.cc
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_chunk1 = 35*1024
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_chunk2 = 10*1024
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'application/octet-stream')
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=0')
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if send_length:
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Length', size_chunk1 + size_chunk2)
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # First chunk of data:
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write("*" * size_chunk1)
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.flush()
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # handle requests until one of them clears this flag.
7922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.server.wait_for_download = True
7932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    while self.server.wait_for_download:
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.server.handle_request()
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Second chunk of data:
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write("*" * size_chunk2)
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def DownloadFinishHandler(self):
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler just tells the server to finish the current download."""
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/download-finish"):
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.server.wait_for_download = False
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Cache-Control', 'max-age=0')
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _ReplaceFileData(self, data, query_parameters):
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Replaces matching substrings in a file.
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    If the 'replace_text' URL query parameter is present, it is expected to be
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    of the form old_text:new_text, which indicates that any old_text strings in
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the file are replaced with new_text. Multiple 'replace_text' parameters may
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    be specified.
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    If the parameters are not present, |data| is returned.
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_dict = cgi.parse_qs(query_parameters)
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    replace_text_values = query_dict.get('replace_text', [])
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for replace_text_value in replace_text_values:
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      replace_text_args = replace_text_value.split(':')
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if len(replace_text_args) != 2:
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise ValueError(
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'replace_text must be of form old_text:new_text. Actual value: %s' %
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          replace_text_value)
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      old_text_b64, new_text_b64 = replace_text_args
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      old_text = base64.urlsafe_b64decode(old_text_b64)
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_text = base64.urlsafe_b64decode(new_text_b64)
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data = data.replace(old_text, new_text)
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return data
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ZipFileHandler(self):
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sends the contents of the requested file in compressed form.
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Can pass in a parameter that specifies that the content length be
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    C - the compressed size (OK),
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    U - the uncompressed size (Non-standard, but handled),
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    S - less than compressed (OK because we keep going),
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    M - larger than compressed but less than uncompressed (an error),
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    L - larger than uncompressed (an error)
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Example: compressedfiles/Picture_1.doc?C
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix = "/compressedfiles/"
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.path.startswith(prefix):
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Consume a request body if present.
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.command == 'POST' or self.command == 'PUT' :
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ReadRequestBody()
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not query in ('C', 'U', 'S', 'M', 'L'):
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sub_path = url_path[len(prefix):]
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entries = sub_path.split('/')
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_path = os.path.join(self.server.data_dir, *entries)
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.isdir(file_path):
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      file_path = os.path.join(file_path, 'index.html')
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not os.path.isfile(file_path):
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print "File not found " + sub_path + " full path:" + file_path
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_error(404)
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f = open(file_path, "rb")
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = f.read()
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uncompressed_len = len(data)
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f.close()
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Compress the data.
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = zlib.compress(data)
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    compressed_len = len(data)
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content_length = compressed_len
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query == 'U':
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_length = uncompressed_len
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif query == 'S':
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_length = compressed_len / 2
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif query == 'M':
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_length = (compressed_len + uncompressed_len) / 2
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif query == 'L':
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_length = compressed_len + uncompressed_len
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'application/msword')
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-encoding', 'deflate')
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Connection', 'close')
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Length', content_length)
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('ETag', '\'' + file_path + '\'')
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(data)
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def FileHandler(self):
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sends the contents of the requested file.  Wow, it's like
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    a real webserver!"""
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix = self.server.file_root_url
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.path.startswith(prefix):
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._FileHandlerHelper(prefix)
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def PostOnlyFileHandler(self):
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sends the contents of the requested file on a POST."""
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix = urlparse.urljoin(self.server.file_root_url, 'post/')
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.path.startswith(prefix):
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._FileHandlerHelper(prefix)
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _FileHandlerHelper(self, prefix):
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request_body = ''
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.command == 'POST' or self.command == 'PUT':
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Consume a request body if present.
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      request_body = self.ReadRequestBody()
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_dict = cgi.parse_qs(query)
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expected_body = query_dict.get('expected_body', [])
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if expected_body and request_body not in expected_body:
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(404)
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('')
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expected_headers = query_dict.get('expected_headers', [])
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for expected_header in expected_headers:
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      header_name, expected_value = expected_header.split(':')
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self.headers.getheader(header_name) != expected_value:
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_response(404)
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.end_headers()
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.wfile.write('')
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return True
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sub_path = url_path[len(prefix):]
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entries = sub_path.split('/')
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_path = os.path.join(self.server.data_dir, *entries)
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.isdir(file_path):
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      file_path = os.path.join(file_path, 'index.html')
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not os.path.isfile(file_path):
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print "File not found " + sub_path + " full path:" + file_path
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_error(404)
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f = open(file_path, "rb")
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = f.read()
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f.close()
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = self._ReplaceFileData(data, query)
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    old_protocol_version = self.protocol_version
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If file.mock-http-headers exists, it contains the headers we
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # should send.  Read them in and parse them.
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    headers_path = file_path + '.mock-http-headers'
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.isfile(headers_path):
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      f = open(headers_path, "r")
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # "HTTP/1.1 200 OK"
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response = f.readline()
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      http_major, http_minor, status_code = re.findall(
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'HTTP/(\d+).(\d+) (\d+)', response)[0]
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.protocol_version = "HTTP/%s.%s" % (http_major, http_minor)
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(int(status_code))
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for line in f:
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        header_values = re.findall('(\S+):\s*(.*)', line)
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if len(header_values) > 0:
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # "name: value"
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          name, value = header_values[0]
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.send_header(name, value)
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      f.close()
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Could be more generic once we support mime-type sniffing, but for
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # now we need to set it explicitly.
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      range_header = self.headers.get('Range')
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if range_header and range_header.startswith('bytes='):
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Note this doesn't handle all valid byte range_header values (i.e.
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # left open ended ones), just enough for what we needed so far.
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        range_header = range_header[6:].split('-')
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        start = int(range_header[0])
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if range_header[1]:
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          end = int(range_header[1])
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          end = len(data) - 1
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_response(206)
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        content_range = ('bytes ' + str(start) + '-' + str(end) + '/' +
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         str(len(data)))
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_header('Content-Range', content_range)
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        data = data[start: end + 1]
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_response(200)
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', self.GetMIMETypeFromName(file_path))
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Accept-Ranges', 'bytes')
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Length', len(data))
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('ETag', '\'' + file_path + '\'')
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (self.command != 'HEAD'):
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(data)
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.protocol_version = old_protocol_version
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SetCookieHandler(self):
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler just sets a cookie, for testing cookie handling."""
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/set-cookie"):
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char != -1:
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cookie_values = self.path[query_char + 1:].split('&')
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cookie_values = ("",)
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for cookie_value in cookie_values:
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Set-Cookie', '%s' % cookie_value)
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for cookie_value in cookie_values:
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('%s' % cookie_value)
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SetManyCookiesHandler(self):
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler just sets a given number of cookies, for testing handling
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       of large numbers of cookies."""
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/set-many-cookies"):
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char != -1:
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_cookies = int(self.path[query_char + 1:])
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_cookies = 0
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('', 'text/html')
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for _i in range(0, num_cookies):
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Set-Cookie', 'a=')
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('%d cookies were sent' % num_cookies)
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ExpectAndSetCookieHandler(self):
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Expects some cookies to be sent, and if they are, sets more cookies.
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The expect parameter specifies a required cookie.  May be specified multiple
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    times.
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The set parameter specifies a cookie to set if all required cookies are
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    preset.  May be specified multiple times.
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The data parameter specifies the response body data to be returned."""
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/expect-and-set-cookie"):
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _, _, _, _, query, _ = urlparse.urlparse(self.path)
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_dict = cgi.parse_qs(query)
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cookies = set()
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'Cookie' in self.headers:
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cookie_header = self.headers.getheader('Cookie')
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cookies.update([s.strip() for s in cookie_header.split(';')])
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    got_all_expected_cookies = True
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for expected_cookie in query_dict.get('expect', []):
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if expected_cookie not in cookies:
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        got_all_expected_cookies = False
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if got_all_expected_cookies:
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for cookie_value in query_dict.get('set', []):
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_header('Set-Cookie', '%s' % cookie_value)
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for data_value in query_dict.get('data', []):
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(data_value)
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SetHeaderHandler(self):
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler sets a response header. Parameters are in the
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    key%3A%20value&key2%3A%20value2 format."""
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/set-header"):
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char != -1:
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      headers_values = self.path[query_char + 1:].split('&')
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      headers_values = ("",)
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for header_value in headers_values:
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      header_value = urllib.unquote(header_value)
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (key, value) = header_value.split(': ', 1)
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header(key, value)
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for header_value in headers_values:
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('%s' % header_value)
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AuthBasicHandler(self):
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler tests 'Basic' authentication.  It just sends a page with
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    title 'user/pass' if you succeed."""
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/auth-basic"):
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    username = userpass = password = b64str = ""
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expected_password = 'secret'
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    realm = 'testrealm'
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_cookie_if_challenged = False
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_params = cgi.parse_qs(query, True)
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'set-cookie-if-challenged' in query_params:
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      set_cookie_if_challenged = True
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'password' in query_params:
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expected_password = query_params['password'][0]
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'realm' in query_params:
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      realm = query_params['realm'][0]
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    auth = self.headers.getheader('authorization')
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not auth:
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('no auth')
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      b64str = re.findall(r'Basic (\S+)', auth)[0]
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      userpass = base64.b64decode(b64str)
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      username, password = re.findall(r'([^:]+):(\S+)', userpass)[0]
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if password != expected_password:
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('wrong password')
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception, e:
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Authentication failed.
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(401)
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', 'text/html')
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if set_cookie_if_challenged:
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_header('Set-Cookie', 'got_challenged=true')
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<html><head>')
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<title>Denied: %s</title>' % e)
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</head><body>')
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('auth=%s<p>' % auth)
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('b64str=%s<p>' % b64str)
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('username: %s<p>' % username)
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('userpass: %s<p>' % userpass)
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('password: %s<p>' % password)
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('You sent:<br>%s<p>' % self.headers)
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</body></html>')
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Authentication successful.  (Return a cachable response to allow for
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # testing cached pages that require authentication.)
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    old_protocol_version = self.protocol_version
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.protocol_version = "HTTP/1.1"
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if_none_match = self.headers.getheader('if-none-match')
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if if_none_match == "abc":
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(304)
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif url_path.endswith(".gif"):
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Using chrome/test/data/google/logo.gif as the test image
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      test_image_path = ['google', 'logo.gif']
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gif_path = os.path.join(self.server.data_dir, *test_image_path)
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not os.path.isfile(gif_path):
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_error(404)
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.protocol_version = old_protocol_version
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return True
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      f = open(gif_path, "rb")
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data = f.read()
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      f.close()
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(200)
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', 'image/gif')
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Cache-control', 'max-age=60000')
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Etag', 'abc')
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(data)
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(200)
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', 'text/html')
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Cache-control', 'max-age=60000')
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Etag', 'abc')
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<html><head>')
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<title>%s/%s</title>' % (username, password))
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</head><body>')
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('auth=%s<p>' % auth)
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('You sent:<br>%s<p>' % self.headers)
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</body></html>')
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.protocol_version = old_protocol_version
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetNonce(self, force_reset=False):
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns a nonce that's stable per request path for the server's lifetime.
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This is a fake implementation. A real implementation would only use a given
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    nonce a single time (hence the name n-once). However, for the purposes of
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unittesting, we don't care about the security of the nonce.
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      force_reset: Iff set, the nonce will be changed. Useful for testing the
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "stale" response.
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if force_reset or not self.server.nonce_time:
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.server.nonce_time = time.time()
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return hashlib.md5('privatekey%s%d' %
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       (self.path, self.server.nonce_time)).hexdigest()
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AuthDigestHandler(self):
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This handler tests 'Digest' authentication.
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    It just sends a page with title 'user/pass' if you succeed.
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A stale response is sent iff "stale" is present in the request path.
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/auth-digest"):
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stale = 'stale' in self.path
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    nonce = self.GetNonce(force_reset=stale)
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    opaque = hashlib.md5('opaque').hexdigest()
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    password = 'secret'
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    realm = 'testrealm'
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    auth = self.headers.getheader('authorization')
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pairs = {}
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not auth:
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('no auth')
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not auth.startswith('Digest'):
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('not digest')
12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Pull out all the name="value" pairs as a dictionary.
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pairs = dict(re.findall(r'(\b[^ ,=]+)="?([^",]+)"?', auth))
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Make sure it's all valid.
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if pairs['nonce'] != nonce:
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('wrong nonce')
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if pairs['opaque'] != opaque:
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('wrong opaque')
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Check the 'response' value and make sure it matches our magic hash.
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # See http://www.ietf.org/rfc/rfc2617.txt
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hash_a1 = hashlib.md5(
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ':'.join([pairs['username'], realm, password])).hexdigest()
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hash_a2 = hashlib.md5(':'.join([self.command, pairs['uri']])).hexdigest()
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if 'qop' in pairs and 'nc' in pairs and 'cnonce' in pairs:
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        response = hashlib.md5(':'.join([hash_a1, nonce, pairs['nc'],
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            pairs['cnonce'], pairs['qop'], hash_a2])).hexdigest()
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        response = hashlib.md5(':'.join([hash_a1, nonce, hash_a2])).hexdigest()
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if pairs['response'] != response:
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('wrong password')
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception, e:
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Authentication failed.
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(401)
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hdr = ('Digest '
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'realm="%s", '
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'domain="/", '
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'qop="auth", '
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'algorithm=MD5, '
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'nonce="%s", '
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'opaque="%s"') % (realm, nonce, opaque)
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if stale:
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        hdr += ', stale="TRUE"'
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('WWW-Authenticate', hdr)
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Content-Type', 'text/html')
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<html><head>')
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('<title>Denied: %s</title>' % e)
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</head><body>')
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('auth=%s<p>' % auth)
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('pairs=%s<p>' % pairs)
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('You sent:<br>%s<p>' % self.headers)
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('We are replying:<br>%s<p>' % hdr)
12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('</body></html>')
12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Authentication successful.
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head>')
13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<title>%s/%s</title>' % (pairs['username'], password))
13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</head><body>')
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('auth=%s<p>' % auth)
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('pairs=%s<p>' % pairs)
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</body></html>')
13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SlowServerHandler(self):
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Wait for the user suggested time before responding. The syntax is
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /slow?0.5 to wait for half a second."""
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/slow"):
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    wait_sec = 1.0
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char >= 0:
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try:
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        wait_sec = int(self.path[query_char + 1:])
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      except ValueError:
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pass
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time.sleep(wait_sec)
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write("waited %d seconds" % wait_sec)
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ChunkedServerHandler(self):
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Send chunked response. Allows to specify chunks parameters:
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     - waitBeforeHeaders - ms to wait before sending headers
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     - waitBetweenChunks - ms to wait between chunks
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     - chunkSize - size of each chunk in bytes
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     - chunksNumber - number of chunks
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Example: /chunked?waitBeforeHeaders=1000&chunkSize=5&chunksNumber=5
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    waits one second, then sends headers and five chunks five bytes each."""
13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/chunked"):
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chunkedSettings = {'waitBeforeHeaders' : 0,
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'waitBetweenChunks' : 0,
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'chunkSize' : 5,
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'chunksNumber' : 5}
13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char >= 0:
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      params = self.path[query_char + 1:].split('&')
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for param in params:
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        keyValue = param.split('=')
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if len(keyValue) == 2:
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          try:
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            chunkedSettings[keyValue[0]] = int(keyValue[1])
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          except ValueError:
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            pass
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time.sleep(0.001 * chunkedSettings['waitBeforeHeaders'])
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.protocol_version = 'HTTP/1.1' # Needed for chunked encoding
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Connection', 'close')
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Transfer-Encoding', 'chunked')
13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Chunked encoding: sending all chunks, then final zero-length chunk and
13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # then final CRLF.
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for i in range(0, chunkedSettings['chunksNumber']):
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if i > 0:
13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        time.sleep(0.001 * chunkedSettings['waitBetweenChunks'])
13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.sendChunkHelp('*' * chunkedSettings['chunkSize'])
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.flush() # Keep in mind that we start flushing only after 1kb.
13685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.sendChunkHelp('')
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ContentTypeHandler(self):
13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns a string of html with the given content type.  E.g.,
13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /contenttype?text/css returns an html file with the Content-Type
13745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    header set to text/css."""
13755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/contenttype"):
13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content_type = self.path[query_char + 1:].strip()
13805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not content_type:
13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_type = 'text/html'
13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', content_type)
13845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write("<html>\n<body>\n<p>HTML text</p>\n</body>\n</html>\n")
13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def NoContentHandler(self):
13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns a 204 No Content response."""
13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest("/nocontent"):
13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(204)
13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ServerRedirectHandler(self):
13985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sends a server redirect to the given URL. The syntax is
13995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '/server-redirect?http://foo.bar/asdf' to redirect to
14005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'http://foo.bar/asdf'"""
14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_name = "/server-redirect"
14035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest(test_name):
14045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
14055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
14075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char < 0 or len(self.path) <= query_char + 1:
14085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.sendRedirectHelp(test_name)
14095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
1410d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    dest = urllib.unquote(self.path[query_char + 1:])
14115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(301)  # moved permanently
14135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Location', dest)
14145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
14155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
14165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head>')
14175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
14185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
14205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ClientRedirectHandler(self):
14225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sends a client redirect to the given URL. The syntax is
14235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '/client-redirect?http://foo.bar/asdf' to redirect to
14245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'http://foo.bar/asdf'"""
14255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_name = "/client-redirect"
14275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest(test_name):
14285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
14295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    query_char = self.path.find('?')
14315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if query_char < 0 or len(self.path) <= query_char + 1:
14325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.sendRedirectHelp(test_name)
14335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
1434d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    dest = urllib.unquote(self.path[query_char + 1:])
14355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
14375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
14385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
14395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><head>')
14405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<meta http-equiv="refresh" content="0;url=%s">' % dest)
14415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
14425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
14445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetSSLSessionCacheHandler(self):
14465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Send a reply containing a log of the session cache operations."""
14475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest('/ssl-session-cache'):
14495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
14505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
14525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
14535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
14545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
1455a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      log = self.server.session_cache.log
14565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except AttributeError:
14575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write('Pass --https-record-resume in order to use' +
14585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       ' this request')
1459a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      return True
1460a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
1461a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    for (action, sessionID) in log:
1462a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      self.wfile.write('%s\t%s\n' % (action, bytes(sessionID).encode('hex')))
14635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
14645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def SSLManySmallRecords(self):
14662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Sends a reply consisting of a variety of small writes. These will be
14672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    translated into a series of small SSL records when used over an HTTPS
14682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    server."""
14692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not self._ShouldHandleRequest('/ssl-many-small-records'):
14712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return False
14722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_response(200)
14742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
14752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.end_headers()
14762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Write ~26K of data, in 1350 byte chunks
14782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for i in xrange(20):
14792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.write('*' * 1350)
14802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.flush()
14812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return True
14822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def GetChannelID(self):
14842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Send a reply containing the hashed ChannelID that the client provided."""
14852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not self._ShouldHandleRequest('/channel-id'):
14872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return False
14882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_response(200)
14902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_header('Content-Type', 'text/plain')
14912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.end_headers()
1492a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    channel_id = bytes(self.server.tlsConnection.channel_id)
14932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.wfile.write(hashlib.sha256(channel_id).digest().encode('base64'))
14942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return True
14952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def ClientCipherListHandler(self):
14971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    """Send a reply containing the cipher suite list that the client
14981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    provided. Each cipher suite value is serialized in decimal, followed by a
14991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    newline."""
15001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
15011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if not self._ShouldHandleRequest('/client-cipher-list'):
15021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return False
15031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
15041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    self.send_response(200)
15051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    self.send_header('Content-Type', 'text/plain')
15061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    self.end_headers()
15071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
15081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    for cipher_suite in self.server.tlsConnection.clientHello.cipher_suites:
15091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      self.wfile.write(str(cipher_suite))
15101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      self.wfile.write('\n')
15111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return True
15121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
15135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CloseSocketHandler(self):
15145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Closes the socket without sending anything."""
15155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._ShouldHandleRequest('/close-socket'):
15175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
15185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.close()
15205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
15215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def RangeResetHandler(self):
15232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Send data broken up by connection resets every N (default 4K) bytes.
15242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Support range requests.  If the data requested doesn't straddle a reset
15252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    boundary, it will all be sent.  Used for testing resuming downloads."""
15262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    def DataForRange(start, end):
15282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      """Data to be provided for a particular range of bytes."""
15292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Offset and scale to avoid too obvious (and hence potentially
15302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # collidable) data.
15312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return ''.join([chr(y % 256)
15322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      for y in range(start * 2 + 15, end * 2 + 15, 2)])
15332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not self._ShouldHandleRequest('/rangereset'):
15352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return False
15362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1537f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    # HTTP/1.1 is required for ETag and range support.
1538f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self.protocol_version = 'HTTP/1.1'
15392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
15402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Defaults
15422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    size = 8000
15432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Note that the rst is sent just before sending the rst_boundary byte.
15442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    rst_boundary = 4000
15452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    respond_to_range = True
15462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    hold_for_signal = False
15472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    rst_limit = -1
15482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    token = 'DEFAULT'
15492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fail_precondition = 0
15502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    send_verifiers = True
15512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Parse the query
15532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    qdict = urlparse.parse_qs(query, True)
15542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'size' in qdict:
15552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      size = int(qdict['size'][0])
15562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'rst_boundary' in qdict:
15572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      rst_boundary = int(qdict['rst_boundary'][0])
15582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'token' in qdict:
15592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Identifying token for stateful tests.
15602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      token = qdict['token'][0]
15612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'rst_limit' in qdict:
15622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Max number of rsts for a given token.
15632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      rst_limit = int(qdict['rst_limit'][0])
15642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'bounce_range' in qdict:
15652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      respond_to_range = False
15662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'hold' in qdict:
15672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Note that hold_for_signal will not work with null range requests;
15682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # see TODO below.
15692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      hold_for_signal = True
15702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'no_verifiers' in qdict:
15712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      send_verifiers = False
15722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if 'fail_precondition' in qdict:
15732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fail_precondition = int(qdict['fail_precondition'][0])
15742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Record already set information, or set it.
15762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    rst_limit = TestPageHandler.rst_limits.setdefault(token, rst_limit)
15772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if rst_limit != 0:
15782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      TestPageHandler.rst_limits[token] -= 1
15792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fail_precondition = TestPageHandler.fail_precondition.setdefault(
15802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      token, fail_precondition)
15812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if fail_precondition != 0:
15822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      TestPageHandler.fail_precondition[token] -= 1
15832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    first_byte = 0
15852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_byte = size - 1
15862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Does that define what we want to return, or do we need to apply
15882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # a range?
15892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    range_response = False
15902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    range_header = self.headers.getheader('range')
15912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if range_header and respond_to_range:
15922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mo = re.match("bytes=(\d*)-(\d*)", range_header)
15932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if mo.group(1):
15942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        first_byte = int(mo.group(1))
15952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if mo.group(2):
15962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        last_byte = int(mo.group(2))
15972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if last_byte > size - 1:
15982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        last_byte = size - 1
15992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      range_response = True
16002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if last_byte < first_byte:
16012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return False
16022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (fail_precondition and
16042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (self.headers.getheader('If-Modified-Since') or
16052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         self.headers.getheader('If-Match'))):
16062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_response(412)
16072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.end_headers()
16082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return True
16092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if range_response:
16112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_response(206)
16122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_header('Content-Range',
16132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       'bytes %d-%d/%d' % (first_byte, last_byte, size))
16142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
16152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_response(200)
16162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_header('Content-Type', 'application/octet-stream')
16172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.send_header('Content-Length', last_byte - first_byte + 1)
16182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if send_verifiers:
16195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      # If fail_precondition is non-zero, then the ETag for each request will be
16205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      # different.
16215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      etag = "%s%d" % (token, fail_precondition)
16225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      self.send_header('ETag', etag)
16232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.send_header('Last-Modified', 'Tue, 19 Feb 2013 14:32 EST')
16242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.end_headers()
16252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if hold_for_signal:
16272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # TODO(rdsmith/phajdan.jr): http://crbug.com/169519: Without writing
16282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # a single byte, the self.server.handle_request() below hangs
16292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # without processing new incoming requests.
16302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.write(DataForRange(first_byte, first_byte + 1))
16312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      first_byte = first_byte + 1
16322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # handle requests until one of them clears this flag.
16332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.server.wait_for_download = True
16342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      while self.server.wait_for_download:
16352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        self.server.handle_request()
16362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    possible_rst = ((first_byte / rst_boundary) + 1) * rst_boundary
16382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if possible_rst >= last_byte or rst_limit == 0:
16392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # No RST has been requested in this range, so we don't need to
16402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # do anything fancy; just write the data and let the python
16412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # infrastructure close the connection.
16422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.write(DataForRange(first_byte, last_byte + 1))
16432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.wfile.flush()
16442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return True
16452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # We're resetting the connection part way in; go to the RST
16472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # boundary and then send an RST.
16482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Because socket semantics do not guarantee that all the data will be
16492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # sent when using the linger semantics to hard close a socket,
16502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # we send the data and then wait for our peer to release us
16512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # before sending the reset.
16522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    data = DataForRange(first_byte, possible_rst)
16532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.wfile.write(data)
16542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.wfile.flush()
16552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.server.wait_for_download = True
16562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    while self.server.wait_for_download:
16572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.server.handle_request()
16582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    l_onoff = 1  # Linger is active.
16592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    l_linger = 0  # Seconds to linger for.
16602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
16612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 struct.pack('ii', l_onoff, l_linger))
16622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Close all duplicates of the underlying socket to force the RST.
16642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.wfile.close()
16652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.rfile.close()
16662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.connection.close()
16672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return True
16692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def DefaultResponseHandler(self):
16715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This is the catch-all response handler for requests that aren't handled
16725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    by one of the special handlers above.
16735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Note that we specify the content-length as without it the https connection
16745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is not closed properly (and the browser keeps expecting data)."""
16755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    contents = "Default response given for path: " + self.path
16775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
16785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
16795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Length', len(contents))
16805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
16815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (self.command != 'HEAD'):
16825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.wfile.write(contents)
16835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
16845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def RedirectConnectHandler(self):
16865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sends a redirect to the CONNECT request for www.redirect.com. This
16875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    response is not specified by the RFC, so the browser should not follow
16885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the redirect."""
16895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (self.path.find("www.redirect.com") < 0):
16915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
16925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dest = "http://www.destination.com/foo.js"
16945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(302)  # moved temporarily
16965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Location', dest)
16975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Connection', 'close')
16985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
16995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
17005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ServerAuthConnectHandler(self):
17025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sends a 401 to the CONNECT request for www.server-auth.com. This
17035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    response doesn't make sense because the proxy server cannot request
17045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    server authentication."""
17055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (self.path.find("www.server-auth.com") < 0):
17075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
17085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    challenge = 'Basic realm="WallyWorld"'
17105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(401)  # unauthorized
17125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('WWW-Authenticate', challenge)
17135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Connection', 'close')
17145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
17155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
17165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def DefaultConnectResponseHandler(self):
17185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """This is the catch-all response handler for CONNECT requests that aren't
17195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handled by one of the special handlers above.  Real Web servers respond
17205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    with 400 to CONNECT requests."""
17215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    contents = "Your client has issued a malformed or illegal request."
17235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(400)  # bad request
17245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
17255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Length', len(contents))
17265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
17275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(contents)
17285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
17295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # called by the redirect handling function when there is no parameter
17315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def sendRedirectHelp(self, redirect_name):
17325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
17335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'text/html')
17345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
17355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('<html><body><h1>Error: no redirect destination</h1>')
17365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('Use <pre>%s?http://dest...</pre>' % redirect_name)
17375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('</body></html>')
17385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # called by chunked handling function
17405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def sendChunkHelp(self, chunk):
17415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Each chunk consists of: chunk size (hex), CRLF, chunk body, CRLF
17425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('%X\r\n' % len(chunk))
17435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(chunk)
17445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write('\r\n')
17455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class OCSPHandler(testserver_base.BasePageHandler):
17485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, request, client_address, socket_server):
17495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handlers = [self.OCSPResponse]
17505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.ocsp_response = socket_server.ocsp_response
17512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    testserver_base.BasePageHandler.__init__(self, request, client_address,
17522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             socket_server, [], handlers, [],
17532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             handlers, [])
17545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def OCSPResponse(self):
17565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_response(200)
17575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Type', 'application/ocsp-response')
17585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.send_header('Content-Length', str(len(self.ocsp_response)))
17595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.end_headers()
17605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.wfile.write(self.ocsp_response)
17625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class TCPEchoHandler(SocketServer.BaseRequestHandler):
17655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """The RequestHandler class for TCP echo server.
17665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  It is instantiated once per connection to the server, and overrides the
17685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handle() method to implement communication to the client.
17695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
17705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def handle(self):
17725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Handles the request from the client and constructs a response."""
17735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = self.request.recv(65536).strip()
17755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Verify the "echo request" message received from the client. Send back
17765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # "echo response" message if "echo request" message is valid.
17775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
17785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return_data = echo_message.GetEchoResponseData(data)
17795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not return_data:
17805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return
17815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except ValueError:
17825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
17835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.request.send(return_data)
17855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class UDPEchoHandler(SocketServer.BaseRequestHandler):
17885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """The RequestHandler class for UDP echo server.
17895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  It is instantiated once per connection to the server, and overrides the
17915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handle() method to implement communication to the client.
17925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
17935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def handle(self):
17955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Handles the request from the client and constructs a response."""
17965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data = self.request[0].strip()
17985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request_socket = self.request[1]
17995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Verify the "echo request" message received from the client. Send back
18005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # "echo response" message if "echo request" message is valid.
18015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
18025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return_data = echo_message.GetEchoResponseData(data)
18035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not return_data:
18045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return
18055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except ValueError:
18065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
18075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request_socket.sendto(return_data, self.client_address)
18085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BasicAuthProxyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
18115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A request handler that behaves as a proxy server which requires
18125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  basic authentication. Only CONNECT, GET and HEAD is supported for now.
18135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
18145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  _AUTH_CREDENTIAL = 'Basic Zm9vOmJhcg==' # foo:bar
18165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def parse_request(self):
18185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Overrides parse_request to check credential."""
18195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not BaseHTTPServer.BaseHTTPRequestHandler.parse_request(self):
18215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
18225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    auth = self.headers.getheader('Proxy-Authorization')
18245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if auth != self._AUTH_CREDENTIAL:
18255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(407)
18265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_header('Proxy-Authenticate', 'Basic realm="MyRealm1"')
18275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
18285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
18295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return True
18315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _start_read_write(self, sock):
18335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sock.setblocking(0)
18345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.request.setblocking(0)
18355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rlist = [self.request, sock]
18365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while True:
18375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ready_sockets, _unused, errors = select.select(rlist, [], [])
18385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if errors:
18395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.send_response(500)
18405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.end_headers()
18415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return
18425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for s in ready_sockets:
18435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        received = s.recv(1024)
18445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if len(received) == 0:
18455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return
18465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if s == self.request:
18475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          other = sock
18485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
18495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          other = self.request
18505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        other.send(received)
18515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _do_common_method(self):
18535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url = urlparse.urlparse(self.path)
18545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    port = url.port
18555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not port:
18565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if url.scheme == 'http':
18575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        port = 80
18585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif url.scheme == 'https':
18595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        port = 443
18605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not url.hostname or not port:
18615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(400)
18625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
18635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
18645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(url.path) == 0:
18665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = '/'
18675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
18685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = url.path
18695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(url.query) > 0:
18705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = '%s?%s' % (url.path, url.query)
18715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sock = None
18735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
18745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock = socket.create_connection((url.hostname, port))
18755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock.send('%s %s %s\r\n' % (
18765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.command, path, self.protocol_version))
18775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for header in self.headers.headers:
18785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        header = header.strip()
18795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (header.lower().startswith('connection') or
18805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            header.lower().startswith('proxy')):
18815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue
18825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sock.send('%s\r\n' % header)
18835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock.send('\r\n')
18845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._start_read_write(sock)
18855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception:
18865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(500)
18875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
18885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finally:
18895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if sock is not None:
18905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sock.close()
18915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def do_CONNECT(self):
18935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
18945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pos = self.path.rfind(':')
18955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      host = self.path[:pos]
18965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      port = int(self.path[pos+1:])
18975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception:
18985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(400)
18995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
19005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
19025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock = socket.create_connection((host, port))
19035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(200, 'Connection established')
19045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
19055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._start_read_write(sock)
19065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception:
19075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.send_response(500)
19085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.end_headers()
19095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finally:
19105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sock.close()
19115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def do_GET(self):
19135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._do_common_method()
19145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def do_HEAD(self):
19165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._do_common_method()
19175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ServerRunner(testserver_base.TestServerRunner):
19205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """TestServerRunner for the net test servers."""
19215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
19235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    super(ServerRunner, self).__init__()
19245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.__ocsp_server = None
19255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __make_data_dir(self):
19275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.data_dir:
19285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not os.path.isdir(self.options.data_dir):
19295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise testserver_base.OptionError('specified data dir not found: ' +
19305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self.options.data_dir + ' exiting...')
19315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my_data_dir = self.options.data_dir
19325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
19335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Create the default path to our data dir, relative to the exe dir.
19345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my_data_dir = os.path.join(BASE_DIR, "..", "..", "..", "..",
19355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 "test", "data")
19365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #TODO(ibrar): Must use Find* funtion defined in google\tools
19385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #i.e my_data_dir = FindUpward(my_data_dir, "test", "data")
19395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return my_data_dir
19415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def create_server(self, server_data):
19435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    port = self.options.port
19445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    host = self.options.host
19455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.server_type == SERVER_HTTP:
19475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self.options.https:
19485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pem_cert_and_key = None
19495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if self.options.cert_and_key_file:
19505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if not os.path.isfile(self.options.cert_and_key_file):
19515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            raise testserver_base.OptionError(
19525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'specified server cert file not found: ' +
19535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                self.options.cert_and_key_file + ' exiting...')
19545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          pem_cert_and_key = file(self.options.cert_and_key_file, 'r').read()
19555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
19565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # generate a new certificate and run an OCSP server for it.
19575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.__ocsp_server = OCSPServer((host, 0), OCSPHandler)
19585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          print ('OCSP server started on %s:%d...' %
19595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              (host, self.__ocsp_server.server_port))
19605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ocsp_der = None
19625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ocsp_state = None
19635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if self.options.ocsp == 'ok':
19655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_GOOD
19665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif self.options.ocsp == 'revoked':
19675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_REVOKED
19685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif self.options.ocsp == 'invalid':
19695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_INVALID
19705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif self.options.ocsp == 'unauthorized':
19715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_UNAUTHORIZED
19725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif self.options.ocsp == 'unknown':
19735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ocsp_state = minica.OCSP_STATE_UNKNOWN
19745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          else:
19755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            raise testserver_base.OptionError('unknown OCSP status: ' +
19765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                self.options.ocsp_status)
19775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (pem_cert_and_key, ocsp_der) = minica.GenerateCertKeyAndOCSP(
19795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              subject = "127.0.0.1",
19805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              ocsp_url = ("http://%s:%d/ocsp" %
19815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  (host, self.__ocsp_server.server_port)),
1982a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)              ocsp_state = ocsp_state,
1983a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)              serial = self.options.cert_serial)
19845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.__ocsp_server.ocsp_response = ocsp_der
19865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for ca_cert in self.options.ssl_client_ca:
19885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if not os.path.isfile(ca_cert):
19895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            raise testserver_base.OptionError(
19905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'specified trusted client CA file not found: ' + ca_cert +
19915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                ' exiting...')
19925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
19935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        stapled_ocsp_response = None
19945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if self.__ocsp_server and self.options.staple_ocsp_response:
19955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          stapled_ocsp_response = self.__ocsp_server.ocsp_response
19965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
19975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key,
19985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             self.options.ssl_client_auth,
19995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             self.options.ssl_client_ca,
2000cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                             self.options.ssl_client_cert_type,
20015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             self.options.ssl_bulk_cipher,
2002a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                             self.options.ssl_key_exchange,
20030529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                             self.options.enable_npn,
20045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             self.options.record_resume,
2005a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                             self.options.tls_intolerant,
2006116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                             self.options.tls_intolerance_type,
20075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             self.options.signed_cert_timestamps_tls_ext.decode(
20085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 "base64"),
20095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             self.options.fallback_scsv,
20105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                             stapled_ocsp_response,
20115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                             self.options.disable_session_cache)
201246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        print 'HTTPS server started on https://%s:%d...' % \
201346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)            (host, server.server_port)
20145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
20155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        server = HTTPServer((host, port), TestPageHandler)
201646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        print 'HTTP server started on http://%s:%d...' % \
201746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)            (host, server.server_port)
20185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server.data_dir = self.__make_data_dir()
20205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server.file_root_url = self.options.file_root_url
20215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
20225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_WEBSOCKET:
20235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Launch pywebsocket via WebSocketServer.
20245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logger = logging.getLogger()
20255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logger.addHandler(logging.StreamHandler())
20265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # TODO(toyoshim): Remove following os.chdir. Currently this operation
20275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # is required to work correctly. It should be fixed from pywebsocket side.
20285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.chdir(self.__make_data_dir())
20295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      websocket_options = WebSocketOptions(host, port, '.')
203046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      scheme = "ws"
20315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self.options.cert_and_key_file:
203246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        scheme = "wss"
20335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.use_tls = True
20345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.private_key = self.options.cert_and_key_file
20355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.certificate = self.options.cert_and_key_file
20365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self.options.ssl_client_auth:
20375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        websocket_options.tls_client_cert_optional = False
20385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.tls_client_auth = True
20395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if len(self.options.ssl_client_ca) != 1:
20405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          raise testserver_base.OptionError(
20415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'one trusted client CA file should be specified')
20425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if not os.path.isfile(self.options.ssl_client_ca[0]):
20435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          raise testserver_base.OptionError(
20445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'specified trusted client CA file not found: ' +
20455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              self.options.ssl_client_ca[0] + ' exiting...')
20465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        websocket_options.tls_client_ca = self.options.ssl_client_ca[0]
20475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = WebSocketServer(websocket_options)
204846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      print 'WebSocket server started on %s://%s:%d...' % \
204946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)          (scheme, host, server.server_port)
20505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
20515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      websocket_options.use_basic_auth = self.options.ws_basic_auth
20525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_TCP_ECHO:
20535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Used for generating the key (randomly) that encodes the "echo request"
20545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # message.
20555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      random.seed()
20565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = TCPEchoServer((host, port), TCPEchoHandler)
20575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'Echo TCP server started on port %d...' % server.server_port
20585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
20595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_UDP_ECHO:
20605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Used for generating the key (randomly) that encodes the "echo request"
20615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # message.
20625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      random.seed()
20635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = UDPEchoServer((host, port), UDPEchoHandler)
20645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'Echo UDP server started on port %d...' % server.server_port
20655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
20665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_BASIC_AUTH_PROXY:
20675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = HTTPServer((host, port), BasicAuthProxyRequestHandler)
20685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'BasicAuthProxy server started on port %d...' % server.server_port
20695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.server_port
20705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.server_type == SERVER_FTP:
20715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my_data_dir = self.__make_data_dir()
20725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Instantiate a dummy authorizer for managing 'virtual' users
20745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      authorizer = pyftpdlib.ftpserver.DummyAuthorizer()
20755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Define a new user having full r/w permissions and a read-only
20775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # anonymous user
20785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw')
20795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      authorizer.add_anonymous(my_data_dir)
20815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Instantiate FTP handler class
20835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ftp_handler = pyftpdlib.ftpserver.FTPHandler
20845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ftp_handler.authorizer = authorizer
20855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Define a customized banner (string returned when client connects)
20875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ftp_handler.banner = ("pyftpdlib %s based ftpd ready." %
20885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            pyftpdlib.ftpserver.__ver__)
20895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Instantiate FTP server class and listen to address:port
20915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server = pyftpdlib.ftpserver.FTPServer((host, port), ftp_handler)
20925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      server_data['port'] = server.socket.getsockname()[1]
20935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'FTP server started on port %d...' % server_data['port']
20945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
20955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise testserver_base.OptionError('unknown server type' +
20965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.options.server_type)
20975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return server
20995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def run_server(self):
21015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.__ocsp_server:
21025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.__ocsp_server.serve_forever_on_thread()
21035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    testserver_base.TestServerRunner.run_server(self)
21055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.__ocsp_server:
21075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.__ocsp_server.stop_serving()
21085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def add_options(self):
21105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    testserver_base.TestServerRunner.add_options(self)
21115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.option_parser.add_option('--disable-session-cache',
21125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  action='store_true',
21135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  dest='disable_session_cache',
21145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  help='tells the server to disable the'
21155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  'TLS session cache.')
21165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('-f', '--ftp', action='store_const',
21175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_FTP, default=SERVER_HTTP,
21185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='server_type',
21195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up an FTP server.')
21205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--tcp-echo', action='store_const',
21215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_TCP_ECHO, default=SERVER_HTTP,
21225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='server_type',
21235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up a tcp echo server.')
21245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--udp-echo', action='store_const',
21255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_UDP_ECHO, default=SERVER_HTTP,
21265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='server_type',
21275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up a udp echo server.')
21285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--basic-auth-proxy', action='store_const',
21295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_BASIC_AUTH_PROXY,
21305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  default=SERVER_HTTP, dest='server_type',
21315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up a proxy server which requires '
21325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'basic authentication.')
21335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--websocket', action='store_const',
21345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const=SERVER_WEBSOCKET, default=SERVER_HTTP,
21355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='server_type',
21365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='start up a WebSocket server.')
21375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--https', action='store_true',
21385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='https', help='Specify that https '
21395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'should be used.')
21405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--cert-and-key-file',
21415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='cert_and_key_file', help='specify the '
21425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'path to the file containing the certificate '
21435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'and private key for the server in PEM '
21445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'format')
21455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--ocsp', dest='ocsp', default='ok',
21465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='The type of OCSP response generated '
21475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'for the automatically generated '
21485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'certificate. One of [ok,revoked,invalid]')
2149a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    self.option_parser.add_option('--cert-serial', dest='cert_serial',
2150a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                                  default=0, type=int,
2151a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                                  help='If non-zero then the generated '
2152a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                                  'certificate will have this serial number')
21535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--tls-intolerant', dest='tls_intolerant',
21545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  default='0', type='int',
21555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='If nonzero, certain TLS connections '
21565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'will be aborted in order to test version '
21575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'fallback. 1 means all TLS versions will be '
21585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'aborted. 2 means TLS 1.1 or higher will be '
21595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'aborted. 3 means TLS 1.2 or higher will be '
21605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'aborted.')
2161116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    self.option_parser.add_option('--tls-intolerance-type',
2162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  dest='tls_intolerance_type',
2163116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  default="alert",
2164116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  help='Controls how the server reacts to a '
2165116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  'TLS version it is intolerant to. Valid '
2166116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  'values are "alert", "close", and "reset".')
21675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.option_parser.add_option('--signed-cert-timestamps-tls-ext',
21685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  dest='signed_cert_timestamps_tls_ext',
2169a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  default='',
2170a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  help='Base64 encoded SCT list. If set, '
2171a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  'server will respond with a '
2172a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  'signed_certificate_timestamp TLS extension '
2173a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  'whenever the client supports it.')
21745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.option_parser.add_option('--fallback-scsv', dest='fallback_scsv',
21755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  default=False, const=True,
21765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  action='store_const',
21775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  help='If given, TLS_FALLBACK_SCSV support '
21785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  'will be enabled. This causes the server to '
21795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  'reject fallback connections from compatible '
21805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  'clients (e.g. Chrome).')
21815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.option_parser.add_option('--staple-ocsp-response',
21825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  dest='staple_ocsp_response',
21835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  default=False, action='store_true',
21845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  help='If set, server will staple the OCSP '
21855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  'response whenever OCSP is on and the client '
21865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  'supports OCSP stapling.')
21875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--https-record-resume',
21885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dest='record_resume', const=True,
21895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  default=False, action='store_const',
21905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Record resumption cache events rather '
21915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'than resuming as normal. Allows the use of '
21925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'the /ssl-session-cache request')
21935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--ssl-client-auth', action='store_true',
21945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Require SSL client auth on every '
21955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'connection.')
21965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--ssl-client-ca', action='append',
21975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  default=[], help='Specify that the client '
21985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'certificate request should include the CA '
21995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'named in the subject of the DER-encoded '
22005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'certificate contained in the specified '
22015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'file. This option may appear multiple '
22025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'times, indicating multiple CA names should '
22035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'be sent in the request.')
2204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    self.option_parser.add_option('--ssl-client-cert-type', action='append',
2205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                  default=[], help='Specify that the client '
2206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                  'certificate request should include the '
2207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                  'specified certificate_type value. This '
2208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                  'option may appear multiple times, '
2209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                  'indicating multiple values should be send '
2210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                  'in the request. Valid values are '
2211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                  '"rsa_sign", "dss_sign", and "ecdsa_sign". '
2212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                  'If omitted, "rsa_sign" will be used.')
22135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--ssl-bulk-cipher', action='append',
22145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Specify the bulk encryption '
22155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'algorithm(s) that will be accepted by the '
22165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'SSL server. Valid values are "aes256", '
22175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  '"aes128", "3des", "rc4". If omitted, all '
22185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'algorithms will be used. This option may '
22195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'appear multiple times, indicating '
22205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  'multiple algorithms should be enabled.');
2221a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    self.option_parser.add_option('--ssl-key-exchange', action='append',
2222a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                  help='Specify the key exchange algorithm(s)'
2223a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                  'that will be accepted by the SSL server. '
2224a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                  'Valid values are "rsa", "dhe_rsa". If '
2225a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                  'omitted, all algorithms will be used. This '
2226a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                  'option may appear multiple times, '
2227a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                  'indicating multiple algorithms should be '
2228a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                  'enabled.');
22290529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    # TODO(davidben): Add ALPN support to tlslite.
22300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    self.option_parser.add_option('--enable-npn', dest='enable_npn',
22310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                  default=False, const=True,
22320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                  action='store_const',
22330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                  help='Enable server support for the NPN '
22340529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                  'extension. The server will advertise '
22350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                                  'support for exactly one protocol, http/1.1')
22365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.option_parser.add_option('--file-root-url', default='/files/',
22375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  help='Specify a root URL for files served.')
22385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # TODO(ricea): Generalize this to support basic auth for HTTP too.
22395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.option_parser.add_option('--ws-basic-auth', action='store_true',
22405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  dest='ws_basic_auth',
22415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  help='Enable basic-auth for WebSocket')
22425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
22455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sys.exit(ServerRunner().main())
2246