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